分布式选举算法详解:Bully算法

引言

在分布式系统中,节点故障是不可避免的。当主节点(Leader)发生故障时,系统需要快速选举出新的主节点来保证服务的连续性。Bully算法是一种经典的分布式选举算法,以其简单高效的特点被广泛应用于各种分布式系统中。

什么是Bully算法?

Bully算法是一种基于优先级的分布式选举算法。每个节点都有一个唯一的ID,ID值越大的节点优先级越高。当主节点故障时,优先级最高的节点将成为新的主节点。

核心思想

  • “强者为王”:ID最大的节点自动成为主节点
  • 主动选举:节点发现主节点故障时,主动发起选举
  • 快速收敛:选举过程简单,收敛速度快

算法流程

1. 选举触发条件

选举在以下情况下触发:

  • 节点发现主节点无响应
  • 新节点加入系统
  • 节点从故障中恢复

2. 选举过程

节点A (ID=1)    节点B (ID=2)    节点C (ID=3)    节点D (ID=4)|              |              |              ||-- Election -->|              |              ||              |-- Election -->|              ||              |              |-- Election -->||              |              |              ||              |              |<-- Victory ---||              |<-- Victory ---|              ||<-- Victory ---|              |              |

详细步骤:

  1. 发起选举:节点A发现主节点故障,向所有ID大于自己的节点发送Election消息
  2. 响应检查:如果收到响应,说明有更高优先级的节点存在
  3. 等待胜利:如果没有收到响应,等待Victory消息
  4. 宣布胜利:如果自己是最高优先级,向所有节点发送Victory消息
  5. 成为主节点:收到Victory消息的节点更新主节点信息

3. 消息类型

  • Election:发起选举请求
  • Victory:宣布选举胜利
  • Ping:心跳检测
  • Pong:心跳响应

算法特点

优点

  1. 简单高效:算法逻辑简单,易于实现和理解

    • 只需要比较节点ID大小
    • 不需要复杂的状态机
    • 代码实现直观,调试容易
  2. 快速收敛:选举过程快速,通常只需要几轮消息交换

    • 最多需要O(n)轮消息交换
    • 不需要多轮投票过程
    • 适合对响应时间要求高的场景
  3. 确定性:总是选举出ID最大的活跃节点

    • 结果可预测,便于系统设计
    • 避免了随机性带来的不确定性
    • 便于负载均衡策略制定
  4. 容错性:能够处理节点故障和网络分区

    • 自动检测节点故障
    • 支持部分网络分区场景
    • 故障恢复后能重新选举

缺点

  1. 消息开销大:选举过程中需要发送大量消息

    • 每个节点都要向所有更高优先级节点发送消息
    • 消息数量为O(n²)级别
    • 在大规模集群中开销显著
  2. 不公平:总是选择ID最大的节点,可能导致负载不均

    • 高优先级节点承担更多责任
    • 低优先级节点资源利用率低
    • 不利于负载分散
  3. 网络敏感:对网络延迟和丢包比较敏感

    • 消息丢失会导致选举失败
    • 网络延迟影响选举速度
    • 需要额外的可靠性机制
  4. 活锁风险:在某些情况下可能出现选举冲突

    • 多个节点同时发起选举
    • 消息丢失导致超时重试
    • 可能形成无限循环

常见问题与解决方案

1. 脑裂问题(Split Brain)

问题描述:
网络分区导致系统出现多个主节点,每个分区都认为自己是主节点。

场景示例:

网络分区前:
节点A(1) -- 节点B(2) -- 节点C(3) -- 节点D(4)Leader: 节点D网络分区后:
分区1: 节点A(1) -- 节点B(2)    分区2: 节点C(3) -- 节点D(4)Leader: 节点B                Leader: 节点D

解决方案:

方案1:多数派机制
class BullyNode:def __init__(self, node_id, all_nodes):self.node_id = node_idself.all_nodes = all_nodesself.quorum_size = len(all_nodes) // 2 + 1  # 多数派阈值def declare_victory(self):"""只有获得多数派支持才能成为主节点"""responses = self.collect_victory_responses()if len(responses) >= self.quorum_size:self.become_leader()else:self.wait_for_quorum()
方案2:租约机制(Lease)
class LeaseBasedBullyNode:def __init__(self, node_id, all_nodes):self.node_id = node_idself.lease_duration = 30  # 租约30秒self.lease_expiry = 0def renew_lease(self):"""定期续约,确保主节点有效性"""if time.time() > self.lease_expiry:self.start_election()else:self.broadcast_lease_renewal()
方案3:时间戳机制
class TimestampBasedBullyNode:def __init__(self, node_id, all_nodes):self.node_id = node_idself.term_number = 0  # 任期号def start_election(self):"""使用任期号避免脑裂"""self.term_number += 1self.broadcast_election_with_term(self.term_number)def receive_victory(self, leader_id, term):"""只接受更高任期的主节点"""if term >= self.term_number:self.leader_id = leader_idself.term_number = term

2. 活锁问题(Live Lock)

问题描述:
多个节点同时发起选举,导致选举过程无限循环。

深入分析:

活锁问题的核心在于并发选举触发消息传递的时序问题。即使只向ID更大的节点发送消息,仍然可能出现以下情况:

场景1:并发选举触发
时间线分析:
T1: 节点A(1) 发现主节点故障,发起选举
T2: 节点B(2) 同时发现主节点故障,发起选举  
T3: 节点C(3) 同时发现主节点故障,发起选举
场景2:消息传递时序问题
详细时序:
T1: A向B发送Election消息
T2: B向C发送Election消息  
T3: A等待B的响应(但B正在处理自己的选举)
T4: B等待C的响应
T5: C没有更高优先级节点,C成为主节点
T6: C向B发送Victory消息
T7: B向A发送Victory消息问题:如果T6或T7的消息丢失了怎么办?
场景3:网络延迟和消息丢失
更复杂的场景:
节点A(1) -- 网络延迟 -- 节点B(2) -- 网络延迟 -- 节点C(3)T1: A发起选举,向B发送消息
T2: B发起选举,向C发送消息(A的消息还没到)
T3: C成为主节点,向B发送Victory
T4: B收到C的Victory,但A还在等待B的响应
T5: A超时,重新发起选举
T6: 循环开始...

活锁的根本原因:

  1. 并发检测:多个节点同时检测到主节点故障
  2. 网络不确定性:消息延迟、丢失、乱序
  3. 超时重试:超时机制触发重新选举
  4. 缺乏协调:没有全局的选举协调机制

解决方案:

方案1:随机退避
import random
import timeclass BullyNode:def start_election(self):"""随机退避避免冲突"""if self.election_in_progress:return# 随机延迟,减少冲突delay = random.uniform(0, 2.0)time.sleep(delay)self.election_in_progress = Trueself.broadcast_election()
方案2:优先级队列
class PriorityBasedBullyNode:def __init__(self, node_id, all_nodes):self.node_id = node_idself.election_queue = []def start_election(self):"""按优先级顺序发起选举"""if not self.election_queue:self.election_queue = sorted(self.all_nodes, reverse=True)if self.election_queue[0] == self.node_id:self.declare_victory()else:self.wait_for_higher_priority()
方案3:状态机机制
from enum import Enumclass NodeState(Enum):FOLLOWER = "follower"CANDIDATE = "candidate"LEADER = "leader"class StateMachineBullyNode:def __init__(self, node_id, all_nodes):self.state = NodeState.FOLLOWERself.election_timeout = 5def start_election(self):"""状态机控制选举流程"""if self.state == NodeState.FOLLOWER:self.state = NodeState.CANDIDATEself.broadcast_election()self.start_election_timer()def handle_election_timeout(self):"""选举超时处理"""if self.state == NodeState.CANDIDATE:self.state = NodeState.FOLLOWERself.start_election()  # 重新发起选举

3. 消息丢失问题

问题描述:
网络不稳定导致选举消息丢失,影响选举结果。

具体影响:

  • Election消息丢失:导致选举无法正常进行
  • Victory消息丢失:导致节点无法确认主节点
  • 心跳消息丢失:导致误判节点故障

解决方案:

方案1:确认机制
class ReliableBullyNode:def send_election_message(self, target_node):"""发送选举消息并等待确认"""message_id = self.generate_message_id()self.send_message(target_node, "Election", message_id)# 等待确认if not self.wait_for_ack(message_id, timeout=3):self.retry_send(target_node, message_id)def send_ack(self, message_id):"""发送确认消息"""self.send_message(self.sender, "ACK", message_id)
方案2:重传机制
class RetryBullyNode:def __init__(self, node_id, all_nodes):self.pending_messages = {}  # 待确认的消息self.max_retries = 3def send_with_retry(self, target, message, max_retries=3):"""带重试的消息发送"""for attempt in range(max_retries):if self.send_message(target, message):return Truetime.sleep(2 ** attempt)  # 指数退避return False

4. 性能问题

问题描述:
选举过程中消息开销大,影响系统性能。

性能瓶颈分析:

  • 消息数量:O(n²)的消息复杂度
  • 网络带宽:大量并发消息占用带宽
  • CPU开销:消息处理消耗CPU资源
  • 延迟影响:选举期间服务可能暂停

解决方案:

方案1:批量消息
class BatchBullyNode:def broadcast_election(self):"""批量发送选举消息"""message = self.create_election_message()batch_size = 10for i in range(0, len(self.all_nodes), batch_size):batch = self.all_nodes[i:i+batch_size]self.send_batch_message(batch, message)
方案2:异步处理
import asyncioclass AsyncBullyNode:async def start_election_async(self):"""异步选举处理"""tasks = []for node_id in self.higher_priority_nodes:task = asyncio.create_task(self.send_election_async(node_id))tasks.append(task)responses = await asyncio.gather(*tasks, return_exceptions=True)return [r for r in responses if not isinstance(r, Exception)]
方案3:缓存机制
class CachedBullyNode:def __init__(self, node_id, all_nodes):self.node_cache = {}  # 节点状态缓存self.cache_ttl = 30   # 缓存30秒def get_node_status(self, node_id):"""获取节点状态(带缓存)"""if node_id in self.node_cache:cache_time, status = self.node_cache[node_id]if time.time() - cache_time < self.cache_ttl:return statusstatus = self.ping_node(node_id)self.node_cache[node_id] = (time.time(), status)return status

最佳实践

1. 监控与告警

class MonitoredBullyNode:def __init__(self, node_id, all_nodes):self.metrics = {'election_count': 0,'election_duration': [],'message_loss_rate': 0.0}def record_election_metrics(self, duration):"""记录选举指标"""self.metrics['election_count'] += 1self.metrics['election_duration'].append(duration)# 告警:选举过于频繁if self.metrics['election_count'] > 10:self.alert("Election frequency too high")

2. 配置管理

class ConfigurableBullyNode:def __init__(self, node_id, all_nodes, config):self.election_timeout = config.get('election_timeout', 5)self.heartbeat_interval = config.get('heartbeat_interval', 1)self.max_retries = config.get('max_retries', 3)self.quorum_size = config.get('quorum_size', len(all_nodes) // 2 + 1)

3. 日志记录

import loggingclass LoggedBullyNode:def __init__(self, node_id, all_nodes):self.logger = logging.getLogger(f"bully_node_{node_id}")def log_election_event(self, event_type, details):"""记录选举事件"""self.logger.info(f"Election event: {event_type} - {details}")def log_error(self, error_type, details):"""记录错误"""self.logger.error(f"Error: {error_type} - {details}")

实际应用场景

1. 数据库集群

  • MongoDB:使用类似Bully的算法进行主节点选举
  • Redis Cluster:节点故障时的主从切换

2. 分布式锁服务

  • Zookeeper:Leader选举机制
  • etcd:Raft算法(更复杂的选举算法)

3. 微服务架构

  • 服务注册中心:主节点负责服务发现
  • 配置中心:主节点负责配置同步

与其他选举算法对比

算法复杂度消息开销收敛速度容错性脑裂防护
Bully简单中等中等需要额外机制
Ring中等天然防护
Raft复杂内置防护
Paxos复杂内置防护

总结

Bully算法是分布式系统中最重要的选举算法之一。虽然存在脑裂、活锁等问题,但通过合理的解决方案和最佳实践,可以在大多数场景中提供可靠的选举服务。

关键要点:

  1. 脑裂问题:通过多数派、租约、时间戳等机制解决
  2. 活锁问题:使用随机退避、优先级队列、状态机等避免
  3. 消息丢失:采用确认、重传、批量等机制提高可靠性
  4. 性能优化:通过异步、缓存、批量等技术提升效率

在实际应用中,需要根据具体场景选择合适的解决方案,并做好监控和告警。


Java实现示例

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;public class BullyNode {private final int nodeId;private final List<Integer> allNodes;private final AtomicInteger leaderId;private final AtomicBoolean isLeader;private final AtomicBoolean electionInProgress;private final ExecutorService executorService;private final Map<Integer, NodeStatus> nodeStatusCache;private final int quorumSize;private final int electionTimeout;public BullyNode(int nodeId, List<Integer> allNodes) {this.nodeId = nodeId;this.allNodes = new ArrayList<>(allNodes);this.leaderId = new AtomicInteger(-1);this.isLeader = new AtomicBoolean(false);this.electionInProgress = new AtomicBoolean(false);this.executorService = Executors.newCachedThreadPool();this.nodeStatusCache = new ConcurrentHashMap<>();this.quorumSize = allNodes.size() / 2 + 1;this.electionTimeout = 5000; // 5秒}public void startElection() {if (!electionInProgress.compareAndSet(false, true)) {return; // 选举已在进行中}System.out.println("节点 " + nodeId + " 发起选举");// 获取更高优先级的节点List<Integer> higherNodes = allNodes.stream().filter(id -> id > nodeId).collect(Collectors.toList());if (higherNodes.isEmpty()) {// 没有更高优先级的节点,直接成为主节点declareVictory();} else {// 向更高优先级的节点发送选举消息CompletableFuture.runAsync(() -> {List<Integer> responses = sendElectionMessages(higherNodes);if (responses.isEmpty()) {declareVictory();} else {waitForVictory();}}, executorService);}}private List<Integer> sendElectionMessages(List<Integer> targetNodes) {List<Integer> responses = new ArrayList<>();for (Integer nodeId : targetNodes) {if (pingNode(nodeId)) {responses.add(nodeId);}}return responses;}private boolean pingNode(int targetNodeId) {// 模拟网络延迟和节点故障try {Thread.sleep(new Random().nextInt(300) + 100);return new Random().nextDouble() > 0.2; // 80%概率节点存活} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}private void declareVictory() {isLeader.set(true);leaderId.set(nodeId);electionInProgress.set(false);System.out.println("节点 " + nodeId + " 成为主节点");// 向所有节点发送Victory消息allNodes.stream().filter(id -> id != nodeId).forEach(this::sendVictoryMessage);}private void sendVictoryMessage(int targetNodeId) {System.out.println("节点 " + nodeId + " 向节点 " + targetNodeId + " 发送Victory消息");// 实际实现中这里会发送网络消息}public void receiveVictory(int newLeaderId) {leaderId.set(newLeaderId);isLeader.set(newLeaderId == nodeId);electionInProgress.set(false);System.out.println("节点 " + nodeId + " 确认主节点为 " + newLeaderId);}private void waitForVictory() {System.out.println("节点 " + nodeId + " 等待Victory消息");// 设置超时机制CompletableFuture.delayedExecutor(electionTimeout, TimeUnit.MILLISECONDS).execute(() -> {if (electionInProgress.get()) {electionInProgress.set(false);startElection(); // 超时后重新发起选举}});}// 脑裂防护:多数派机制public boolean declareVictoryWithQuorum() {List<Integer> responses = collectVictoryResponses();if (responses.size() >= quorumSize) {declareVictory();return true;}return false;}private List<Integer> collectVictoryResponses() {// 收集Victory响应return new ArrayList<>(); // 简化实现}// 活锁防护:随机退避public void startElectionWithBackoff() {if (electionInProgress.compareAndSet(false, true)) {// 随机延迟long delay = new Random().nextInt(2000);CompletableFuture.delayedExecutor(delay, TimeUnit.MILLISECONDS).execute(this::startElection);}}// 消息可靠性:重试机制public boolean sendWithRetry(int targetNode, String message, int maxRetries) {for (int attempt = 0; attempt < maxRetries; attempt++) {if (sendMessage(targetNode, message)) {return true;}try {Thread.sleep((long) Math.pow(2, attempt) * 1000); // 指数退避} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}return false;}private boolean sendMessage(int targetNode, String message) {// 模拟消息发送return new Random().nextDouble() > 0.1; // 90%成功率}// 监控指标private final AtomicInteger electionCount = new AtomicInteger(0);private final List<Long> electionDurations = new CopyOnWriteArrayList<>();public void recordElectionMetrics(long duration) {electionCount.incrementAndGet();electionDurations.add(duration);// 告警:选举过于频繁if (electionCount.get() > 10) {System.err.println("警告:选举频率过高");}}public void shutdown() {executorService.shutdown();try {if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {executorService.shutdownNow();}} catch (InterruptedException e) {executorService.shutdownNow();Thread.currentThread().interrupt();}}// 使用示例public static void main(String[] args) {List<Integer> allNodes = Arrays.asList(1, 2, 3, 4, 5);Map<Integer, BullyNode> nodes = new HashMap<>();// 创建所有节点for (Integer nodeId : allNodes) {nodes.put(nodeId, new BullyNode(nodeId, allNodes));}// 模拟选举过程System.out.println("=== Bully算法演示 ===");nodes.get(2).startElection();try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}// 模拟节点4响应选举System.out.println("\n=== 节点4响应选举 ===");nodes.get(4).startElection();try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}// 节点4成为主节点System.out.println("\n=== 节点4成为主节点 ===");nodes.get(4).declareVictory();// 其他节点接收Victory消息for (Integer nodeId : Arrays.asList(1, 2, 3, 5)) {nodes.get(nodeId).receiveVictory(4);}// 关闭所有节点nodes.values().forEach(BullyNode::shutdown);}
}

参考资料:

  • 分布式系统概念与设计
  • Zookeeper官方文档
  • Redis Cluster文档

相关文章:

  • 分布式系统中的一致性算法
  • Raft算法详解
  • Paxos算法原理

ES的选举算法

ES的选主算法是基于Bully算法的改进,主要思路是对节点ID排序,取ID值最大的节点作为Master,每个节点都运行这个流程。是不是非常简单?选主的目的是确定唯一的主节点,初学者可能认为选举出的主节点应该持有最新的元数据信息,实际上这个问题在实现上被分解为两步:先确定唯一的、大家公认的主节点,再想办法把最新的机器元数据复制到选举出的主节点上。

基于节点ID排序的简单选举算法有三个附加约定条件:
(1)参选人数需要过半,达到 quorum(多数)后就选出了临时的主。为什么是临时的?每个节点运行排序取最大值的算法,结果不一定相同。举个例子,集群有5台主机,节点ID分别是1、2、3、4、5。当产生网络分区或节点启动速度差异较大时,节点1看到的节点列表是1、2、3、4,选出4;节点2看到的节点列表是2、3、4、5,选出5。结果就不一致了,由此产生下面的第二条限制。

(2)得票数需过半。某节点被选为主节点,必须判断加入它的节点数过半,才确认Master身份。解决第一个问题。

(3)当探测到节点离开事件时,必须判断当前节点数是否过半。如果达不到 quorum,则放弃Master身份,重新加入集群。如果不这么做,则设想以下情况:假设5台机器组成的集群产生网络分区,2台一组,3台一组,产生分区前,Master位于2台中的一个,此时3台一组的节点会重新并成功选取Master,产生双主,俗称脑裂。

集群并不知道自己共有多少个节点,quorum值从配置中读取,我们需要设置配置项:
discovery.zen.minimum_master_nodes

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

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

相关文章

高效调试 AI 大模型 API:用 Apipost 实现 SSE 流式解析与可视化

借助 AI 大模型的实时接口&#xff08;如 OpenAI GPT 或其他第三方模型 API&#xff09;&#xff0c;开发者可以通过 SSE&#xff08;Server-Sent Events&#xff09;流式处理数据&#xff0c;实时获取模型的逐步输出。这一技术已广泛应用于实时问答、代码生成等领域。本文将基…

【网络产品经营】园区网络

园区网络的产品经营逻辑发生显著变化&#xff0c;从传统的“连接功能”导向转向“业务体验驱动”&#xff0c;并结合行业场景化需求、技术架构革新及智能化能力提升&#xff0c;形成多维度的产品策略升级。 一、技术架构变革&#xff1a;从多层复杂到极简全光 传统架构的瓶颈与…

EasyExcel 4.X 读写数据

文章目录 EasyExcel与SpringBoot集成读数据读取数据的流程定义实体类简单读取自定义监听器 读取指定sheet和所有sheet多行头读取数据格式转换列表数据实体类自定义转换器自定义监听器数据读取 写数据简单数据写出存储到磁盘返回前端下载 写出指定列宽&#xff0c;和数值精度丢失…

JVM内存管理<一>:Java内存异常问题排查

一、 内存溢出问题的排查 1. 使用工具 - jdk自带 jmapvisualvm 2. 流程 堆转储&#xff1a; (1) 方法一&#xff1a;程序运行时&#xff0c;采用&#xff1a;jmap -dump:formatb,filed:\\data\\xxlJob.hprof 23300 进行堆文件的转储 (2) 方法二&#xff1a;在内存溢出的时候…

Android中Glide.with().load().into() 应付面试源码解析

1. with(this)&#xff1a;生命周期绑定 Glide.with(Activity/Fragment/Context) 核心机制&#xff1a;创建与 UI 生命周期绑定的 RequestManager 底层实现&#xff1a; 通过 RequestManagerRetriever 获取单例 非 Application 上下文&#xff1a; 向 Activity/Fragment 添加…

#### es相关内容的索引 ####

倒排索引 结构 #### es倒排索引的结构 ####-CSDN博客 向量索引 结构应用 #### es向量检索 的 结构及应用_es 向量 文本检索-CSDN博客 ann算法 ann算法的种类有哪些&#xff0c;之间的区别&#xff0c;各自的适用场景-CSDN博客 地理信息索引 es地理信息索引的类型以及geo_po…

小飞电视:智能电视与移动设备的娱乐新选择

在数字娱乐时代&#xff0c;人们对于影视内容的需求日益增长&#xff0c;不仅追求丰富多样的节目选择&#xff0c;还希望获得便捷、个性化的观看体验。小飞电视正是这样一款专为智能电视和移动设备设计的视频娱乐应用&#xff0c;它凭借海量的影视资源、高清流畅的播放效果以及…

删除node并且重装然后重装vue

参考第一篇文章 node.js卸载与安装超详细教程_node卸载重装-CSDN博客 第二篇文章安装vue Vue安装与配置教程&#xff08;非常详细&#xff09;_安装vue-CSDN博客

基于YOLOv10算法的交通信号灯检测与识别

目录 一.&#x1f981; 写在前面1.1 实现模块划分1.2 优化与实时性支持 二.&#x1f981; 相关技术与理论基础2.1 各版本yolo对比2.2 YOLOv10网络结构 三.&#x1f981; 结果分析3.1 训练损失与验证损失分析3.2 精确率&#xff08;Precision&#xff09;、召回率&#xff08;Re…

洪水风险图制作全流程:HEC-RAS 与 ArcGIS 的耦合应用

技术点目录 一、HER-RAS理论二、一维数学模型基本地形导入三、恒定流、非恒定流一维数学模型水流计算四、一维数学模型计算结果分析五、一维数学模型增设构筑物六、二维河道水动力模拟七、HEC-RAS在溃坝模型中的应用八、HEC-RAS在洪水风险图中的应用了解更多 —————————…

视觉大语言模型未能充分利用视觉表征

视觉大语言模型未能充分利用视觉表征 FesianXu 20250612 at Wechat Search Team 前言 这两天看到一篇新挂在arxiv上的文章 [1]&#xff0c;讨论了下视觉大语言模型的视觉表征退化问题。先前的研究将VLM缺陷归咎于视觉编码器薄弱&#xff0c;并提出集成编码器方案以弥补不足&am…

SSRF3 任意文件读取

一.任意文件读取 http://192.168.112.12/pikachu-master/vul/ssrf/ssrf_curl.php?urlfile:///etc/passwd 读取文件使用 file://文件路径即可&#xff0c;这里我们换协议为file&#xff0c;然后从根目录开始读取。 /etc/passwd 我们这样修改完url路径后查看结果可以看到文件内…

洛谷P3953 [NOIP 2017 提高组] 逛公园

洛谷P3953 [NOIP 2017 提高组] 逛公园 洛谷题目传送门 题目背景 NOIP2017 D1T3 题目描述 策策同学特别喜欢逛公园。公园可以看成一张 N N N 个点 M M M 条边构成的有向图&#xff0c;且没有 自环和重边。其中 1 1 1 号点是公园的入口&#xff0c; N N N 号点是公园的出…

Vue3+TypeScript+Element Plus 表格展开行优化方案

在 Vue3 TypeScript Element Plus 项目中优化表格展开行的内存使用&#xff0c;主要从 渲染优化、数据管理 和 内存回收 三方面入手。以下是最佳实践和完整解决方案&#xff1a; 1. 懒加载展开内容&#xff08;核心优化&#xff09; 只当行展开时才渲染内容&#xff0c;避免…

OpenCV——直方图与匹配

直方图与匹配 一、直方图简介二、直方图统计三、直方图比较四、直方图均衡化五、自适应的直方图均衡化六、直方图反向投影七、模板匹配 一、直方图简介 图像直方图&#xff08;Histogram&#xff09;是一种频率分布图&#xff0c;它描述了不同强度值在图像中出现的频率。图像直…

通义大模型在文档自动化处理中的高效部署指南(OCR集成与批量处理优化)

1. 传统OCR解决方案常面临识别精度低、版面分析能力弱、处理效率瓶颈等问题。通义大模型凭借其多模态理解和生成能力&#xff0c;为文档处理领域带来革命性突破。本文将深入探讨如何高效部署通义大模型实现端到端的文档自动化处理&#xff0c;特别聚焦OCR集成与批量处理优化两…

Ubuntu20.04通过ssh协议配置远程终端

一、在目标计算机&#xff08;即被连接的计算机&#xff09;上操作&#xff1a; 1、安装 OpenSSH 服务器&#xff1a; sudo apt update sudo apt install openssh-server3、启动并设置 SSH 服务开机自启&#xff1a; sudo systemctl enable --now ssh二、在源计算机&#xf…

《HTTP权威指南》 第7章 缓存

带着问题学习&#xff1a; 缓存如何提高性能如何衡量缓存的有效性缓存置于何处作用最大HTTP如何保持缓存副本的新鲜度缓存如何与其他缓存及服务器通信 web缓存是可以自动保存常见文档副本的HTTP设备。 缓存优点 减少冗余的数据传输&#xff0c;节省网络费用缓解网络瓶颈问题&…

第十三章 模板

函数模板 函数模板使用 函数模板注意事项 自动类型推导&#xff0c;必须推导出一致的数据类型T,才可以使用 模板必须要确定出T的数据类型&#xff0c;才可以使用 普通函数和函数模板的类型转化 普通函数隐式类型转化&#xff08;char转int&#xff09; 函数模板正常使用不会发生…

云计算-专有网络VPC

&#x1f310; 什么是 VPC&#xff1f;&#xff08;Virtual Private Cloud&#xff09; VPC&#xff08;Virtual Private Cloud&#xff0c;虚拟私有云&#xff09; 是公有云服务商提供的一种网络隔离服务&#xff0c;允许用户在云中创建一个逻辑隔离的私有网络环境。你可以在这…