Spring Cloud Stream - 构建高可靠消息驱动与事件溯源架构

news2025/3/18 20:49:08

一、引言

在分布式系统中,传统的 REST 调用模式往往导致耦合,难以满足高并发和异步解耦的需求。消息驱动架构(EDA, Event-Driven Architecture)通过异步通信、事件溯源等模式,提高了系统的扩展性与可观测性。

作为 Spring Cloud 生态的一部分,Spring Cloud Stream 抽象了不同消息中间件(如 Kafka、RabbitMQ)的底层差异,提供统一的编程模型,从而简化了微服务间的事件交互。本文将结合理论与实例,探讨 Spring Cloud Stream 的核心价值,具体包括:

• 高效解耦:通过声明式通道和 Binder 抽象,屏蔽底层中间件的复杂性。

• 状态可溯:通过事件日志驱动业务状态,确保数据一致性。

• 生产就绪:通过容错机制与治理策略,支持高可靠系统的落地。

二、消息驱动微服务模型

2.1 Spring Cloud Stream 架构与核心组件

Spring Cloud Stream 是 Spring Cloud 生态中消息中间件的抽象层,通过统一的编程模型屏蔽 Kafka、RabbitMQ 等中间件的实现差异,实现跨平台消息交互。

核心组件:

• Binder

作用:对接具体消息中间件(如 Kafka、RabbitMQ),提供统一的 API。

价值:开发者无需关注底层协议(如 AMQP、Kafka Protocol),通过配置切换中间件。

• Binding

作用:定义消息通道与中间件物理目标(如 Topic、Queue)的绑定规则。

配置示例:

spring.cloud.stream.bindings.outputChannel.destination=orders

• Message Channel

编程接口:通过@Input、@Output注解声明输入/输出通道。

示例:

public interface OrderChannels {  
    @Output("order-events") MessageChannel orderOutput();  
}

设计原则:

• 开箱即用:自动配置连接工厂、序列化器等基础设施。

• 扩展性:支持自定义 Binder 实现(如阿里云 RocketMQ)。

2.2 完整的消息驱动示例

生产者发送流程
在这里插入图片描述

消费者监听流程
在这里插入图片描述

完整代码结构参考

生产者项目
├── src/main/java
│   ├── com/example/producer
│   │   ├── MessageProducer.java
│   │   └── MyMessageChannels.java
│   └── resources/application.yml

消费者项目
├── src/main/java
│   ├── com/example/consumer
│   │   ├── MessageConsumer.java
│   │   └── MyMessageChannels.java
│   └── resources/application.yml

完整示例步骤如下:

第1步:创建 Spring Boot 项目

使用 Spring Initializr 创建项目,选择依赖:

• 生产者项目:Spring Web,Spring Cloud Stream,Lombok

• 消费者项目:Spring Cloud Stream,Lombok

• 中间件支持:根据实际选择配置RabbitMQ或Kafka,本示例以RabbitMQ为例。

生成项目,下载并解压项目,相关依赖都在pom.xml中。

消费者项目中核心依赖示例:

<dependencies>
    <!-- Web支持(用于创建REST接口) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 消息驱动核心 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream</artifactId>
    </dependency>

    <!-- 选择其中一个中间件依赖 -->
    <!-- RabbitMQ -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
    </dependency>
    <!-- 或 Kafka -->
    <!--
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream-binder-kafka</artifactId>
    </dependency>
    -->

    <!-- 代码简化工具 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

第2步:定义消息通道接口(生产者 & 消费者共用)

在两个项目的src/main/java下创建消息通道接口文件:

定义输出通道方法和输入通道方法。

public interface MyMessageChannels {
   //定义消息发送通道
    @Output("outputChannel")  // 指定通道名称为 "outputChannel"
    MessageChannel outputChannel(); // 方法名也是 outputChannel(推荐但不强制)

    //定义消息接收通道
    @Input("inputChannel")     // 指定通道名称为 "inputChannel"
    SubscribableChannel inputChannel(); // 方法名也是 inputChannel
}

注解解析:

• @Output(“outputChannel”):定义输出通道,用于消息生产者向outputChannel 通道发送消息。

• @Input(“inputChannel”):定于输入通道,用于消息消费者从inputChannel通道读取消息。

注解名称解析:

注解名称”outputChannel “ 与方法名(outputChannel)一致,是最佳实践,代码清晰易读。

如果修改方法名(但保持注解名称不变),代码依然有效。

情况1:通道名称以注解中的值为主,方法名可随意。

@Output("myCustomOutput") // 通道名称是 "myCustomOutput"
MessageChannel anyMethodName(); // 方法名随意

情况2:注解未指定名称,则通道名称默认取方法名(此时方法名必须有意义)。

@Output // 未指定名称,通道名称自动取方法名 "outputChannel"
MessageChannel outputChannel(); 

配置绑定的关键点

在配置文件(如 application.yml)中,绑定的是 注解中定义的通道名称,而不是方法名。具体可见下文 第5步示例。

第3步:实现消息生产者

在生产者项目中,创建控制器:

// MessageProducer.java
@RestController
@RequiredArgsConstructor
public class MessageProducer {
    // 自动注入通道
    private final MyMessageChannels channels;

    // 处理POST请求:/send?message=内容
    @PostMapping("/send")
    public String sendMessage(@RequestParam String message) {
        // 发送消息到outputChannel:
        // 1. channels.outputChannel() 获取输出通道对象
        // 2. MessageBuilder构建消息对象,withPayload设置消息内容
        // 3. 调用send()将消息发送到消息中间件
        channels.outputChannel().send(
            MessageBuilder.withPayload(message).build()
        );
        //返回响应结果
        return "Message sent: " + message;
    }
}

注解解析:

@RestController:相当于@Controller + @ResponseBody。

• 表明该类是一个处理 Web 请求的控制器,其方法的返回数据会直接作为响应内容(非视图页面)。

@RequiredArgsConstructor:Lombok 注解。

• 自动生成构造器,用于注入final修饰的字段(例如:channels)。

代码解析:

• channels.outputChannel().send():将消息发送到outputChannel,RabbitMQ会将其存入主题(Topic)。

• MessageBuilder.withPayload(message).build():创建消息对象,将字符串message作为负载。

第4步:实现消息消费者

在消费者项目中,创建消息监听器:

@Service
@EnableBinding(MyMessageChannels.class) // 绑定消息通道
public class MessageConsumer {
    @StreamListener("inputChannel")
    public void handle(String message) {
        System.out.println("Received: " + message); // 消费并打印消息
    }
}

解析:
• @EnableBinding(MyMessageChannels.class):声明并绑定应用的消息通道,使 Spring Cloud Stream 自动配置与消息代理(如 Kafka/RabbitMQ)的连接。该注解仅负责通道的注册。
• 消息的接收与处理由@StreamListener或@RabbitListener等注解实现。
• @StreamListener(“inputChannel”):监听inputChannel,当有消息到达时触发handle方法。

第5步:配置绑定(关键步骤)
生产者配置文件

在生产者项目的src/main/resources/application.yml中添加如下配置:

spring:
  cloud:
    stream:
      bindings:
        outputChannel-out-0:  # 对应注解中的名称,@Output("outputChannel")
          destination: demo-queue  # 消息队列名称(RabbitMQ 自动创建)
      # 如果使用 RabbitMQ,需配置连接信息(默认连本地)
      rabbit:
        bindings:
          outputChannel-out-0:
            producer:
              exchangeType: direct  # 交换机类型

消费者配置文件

spring:
  cloud:
    stream:
      bindings:
        inputChannel-in-0:    # 对应注解中的名称@Input("inputChannel")
          destination: demo-queue  # 必须与生产者的destination一致
          group: my-group     # 消费者组(RabbitMQ中可选,Kafka必填)
      # RabbitMQ 连接配置(与生产者一致)
      rabbit:
        bindings:
          inputChannel-in-0:
            consumer:
              exchangeType: direct
              durableSubscription: true  # 持久化订阅

第6步:运行与测试

1、 启动 RabbitMQ

本地安装 RabbitMQ或使用 Docker:

docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:management

2、启动生产者应用

• 访问http://localhost:8080/send?message=Hello

• 预期响应:Message sent: Hello

3、启动消费者应用

• 控制台输出:[消费者] 收到消息:Hello

4、验证队列

• 访问RabbitMQ 管理界面:http://localhost:15672

• 查看Queues标签页,确认demo-queue.my-group队列已创建。

• 检查消息是否被消费(队列中的消息数应为 0)。

2.3 常见问题自查表
在这里插入图片描述

三、事件溯源与消息驱动的架构融合

3.1 事件溯源(Event Sourcing)模型

事件溯源是一种以不可变事件流为核心的数据持久化模式。所有系统状态变更均以事件(Event)形式按顺序记录在事件日志(Event Log)中,而非直接修改当前状态。每个事件代表一次原子性操作(如订单创建、账户扣款),通过事件回放可重建任意时间点的系统状态。

核心特性:

• 不可变性:事件一旦存储,不可修改或删除。

• 顺序性:事件按时间顺序持久化,形成完整的操作历史。

• 唯一事实源:系统的当前状态完全由事件日志推导得出。

类比:

• 传统数据库:直接覆盖银行账户余额(如余额从 1000 → 800,无法追溯原因)。

• 事件溯源:记录每笔交易事件(如“存款 +200”“转账 -400”),通过事件回放计算当前余额(1000 + 200 - 400 = 800)。

3.2 核心优势与应用场景
在这里插入图片描述

3.3 事件溯源与CQRS的协同设计

CQRS(命令查询职责分离)

核心思想:将系统的写操作(Command)与读操作(Query)分离,独立优化。

与事件溯源的协同

• 写模型(Command Side)

职责:生成事件并持久化到事件日志(如 Kafka)。

示例:创建订单时发布OrderCreatedEvent,而非直接更新数据库。

• 读模型(Query Side)

职责:从优化的读存储(如 Redis、Elasticsearch)获取数据。

示例:查询订单状态时直接从缓存读取,避免复杂的 JOIN 查询。

技术价值

• 性能优化:读写分离避免数据库锁竞争,提升吞吐量。

• 架构灵活性:读模型可针对业务需求独立扩展(如全文检索、聚合统计)。

3.4 Spring Cloud Stream 在事件驱动架构中的实践

核心作用:

作为事件驱动架构的传输层,Spring Cloud Stream 实现以下关键能力:

能力1:事件传输管道(事件分发与路由)

生产者:通过@Output通道发布事件,推送事件至消息代理(如 Kafka)。

消费者:通过@Input通道订阅事件,支持条件路由(如基于消息头过滤)。

示例场景:订单服务发布OrderCreatedEvent,库存服务、支付服务分别订阅并处理。

能力2:读写分离(CQRS)实现

写模型:生成事件并持久化至事件日志(如 Kafka)。

@RestController  
public class OrderController {  
    @PostMapping("/orders")  
    public void createOrder() {  
        channels.orderOutput().send(MessageBuilder.withPayload(event).build());  
    }  
}

读模型:监听事件更新物化视图(如 Redis 缓存)。

@StreamListener("order-events")  
public void updateOrderView(OrderCreatedEvent event) {  
    redisTemplate.opsForValue().set(event.getOrderId(), event);  
}

能力3:可靠性保障

• 顺序性:通过分区键(如orderId)保证同一实体事件顺序处理。

• 幂等性:结合 Redis 防重机制(见 4.2 节)。

• 容错:集成死信队列(DLQ)隔离异常消息(见 4.1 节)。

3.5 事件存储选型与全链路协作

事件存储模式选择
在这里插入图片描述

从事件生成到存储到消费的完整协作过程

sequenceDiagram
    participant CommandService as 命令服务(写模型)
    participant Kafka as 消息代理(Kafka)
    participant EventStore as 事件存储(数据库)
    participant QueryService as 查询服务(读模型)
    participant ReadDB as 读数据库(Redis)

    CommandService->>Kafka: 发送OrderCreatedEvent
    Kafka->>EventStore: 持久化事件日志
    Kafka->>QueryService: 推送事件
    QueryService->>ReadDB: 更新读模型(物化视图)
    QueryService-->>Client: 响应查询请求

核心角色:

• 命令服务(生产者):生成事件并发送到消息代理。

• 消息代理(如 Kafka):作为事件传输通道,负责分发事件。

• 事件存储(如 MongoDB):持久化事件日志,支持回放与查询。

• 查询服务(消费者):监听事件并更新读模型(如 Redis 缓存)。

3.6 事件溯源完整示例 (订单系统为例)

1)定义事件对象(核心数据结构)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderCreatedEvent {
    private String orderId;    // 订单唯一标识
    private String product;    // 商品名称
    private int quantity;      // 购买数量
}

解析:

• OrderCreatedEvent:订单创建事件,包含orderId、product、quantity。

• @Data:Lombok 注解,自动生成类的所有 getter、setter等 方法。

• @AllArgsConstructor:Lombok 注解,自动生成一个包含所有字段的构造器。

• @NoArgsConstructor:Lombok 注解,自动生成一个无参构造器。

2)事件生产者(写模型:生成事件)

相关 MyMessageChannels 定义参见第二章2.2示例代码。

@RestController
@RequiredArgsConstructor
public class OrderController {
    private final MyMessageChannels channels; // 消息通道接口

    // 创建订单并发送事件
    @PostMapping("/createOrder")
    public String createOrder(@RequestParam String product, @RequestParam int quantity) {
        OrderCreatedEvent event = new OrderCreatedEvent(
            UUID.randomUUID().toString(), // 生成唯一订单ID
            product, 
            quantity
        );
        // 发送事件到消息通道
        channels.outputChannel().send(MessageBuilder.withPayload(event).build());
        return "Order Created: " + event;
    }
}

解析:

• 生产者通过 HTTP 接口接收请求,构造OrderCreatedEvent事件,并发送到 Kafka 事件流(outputChannel)进行异步处理。

3)事件存储(持久化事件日志)
事件消费者(Event Store Service)

@EnableBinding(MyMessageChannels.class) // 绑定消息通道
public class EventStore {
    @StreamListener("inputChannel")
    public void storeEvent(OrderCreatedEvent event) {
        System.out.println("Storing Event: " + event);
        saveEventToDatabase(event); // 模拟事件存储
    }

    private void saveEventToDatabase(OrderCreatedEvent event) {
        // 实际场景:事件应存入数据库(如MySQL、MongoDB)
    }
}

解析:

• 消费者监听消息通道inputChannel的OrderCreatedEvent并存储,实现事件溯源。

• 事件存入数据库(如 MySQL、MongoDB),以支持历史回放和查询。

4)事件查询(读模型)

CQRS 模式下,读模型典型实现:

@RestController
@RequiredArgsConstructor
public class OrderQueryController {
    private final OrderRepository orderRepository; // 读数据库(如Redis)

    // 查询订单信息
    @GetMapping("/orders/{orderId}")
    public OrderView getOrder(@PathVariable String orderId) {
        return orderRepository.findById(orderId)
                .orElseThrow(() -> new OrderNotFoundException("Order Not Found"));
    }
}

解析:

• OrderView存储在读数据库(如 Redis/Elasticsearch),保证高效查询。

• 采用事件驱动更新,每次OrderCreatedEvent发生时,通过监听事件更新OrderView读模型(如订单创建后更新 Redis 缓存)。

3.7 常见问题解答

Q1:事件存储和传统数据库有什么区别?

• 事件存储:仅追加(append-only)不可修改的事件日志,记录“发生了什么”。

• 传统数据库:直接修改当前状态,记录“现在是什么”。

Q2:CQRS 会增加系统复杂度吗?

• 初期:需要维护读写两套逻辑,有一定学习成本。

• 长期:提升扩展性和性能,适合高并发场景。

Q3:如何保证事件顺序?

• Kafka:通过分区键(如订单 ID)确保同一实体的事件顺序处理。

• 数据库:使用递增版本号或时间戳排序。

总结
事件溯源与消息驱动架构,通过不可变事件流与读写分离重塑了系统设计。

1.事件溯源:以事件日志为唯一事实源,支持历史回溯与状态重建,保障数据可靠性与审计能力。

2.CQRS协同:解耦命令与查询,写模型生成事件流,读模型通过缓存或搜索引擎优化响应效率。

3.消息驱动:基于Spring Cloud Stream实现异步事件传输,服务间解耦,适配高并发与分布式场景。

核心价值

• 技术侧:提升吞吐量、扩展性与容错能力。

• 业务侧:满足高频交易(如电商、金融)的合规需求,支持复杂业务链路追踪。

适用场景:适用于需高可靠性、实时响应及跨服务协作的系统,如:订单管理、实时计费等。

四、生产级消息治理

4.1 死信队列(DLQ)容错机制

死信队列(Dead Letter Queue, DLQ)

死信队列是消息系统中用于存储无法正常消费的消息的特殊队列。当消息因异常(如处理失败、超时、格式错误)无法被消费者正确处理时,系统自动将其转移到 DLQ,避免消息丢失或无限重试阻塞系统。

1.核心作用

• 容错处理:隔离异常消息,防止主业务队列被“毒丸消息”(Poison Pill)阻塞。

• 问题排查:集中存储失败消息,便于后续人工或自动分析原因。

• 重试机制:支持手动或自动从 DLQ 重新投递消息到主队列进行重试。

2.配置示例

在消费者应用程序的application.yml 中配置,定义消费失败的信息处理方法。

spring:  
  cloud:  
    stream:  
      bindings:  
        inputChannel:  
          consumer:  
            enable-dlq: true    # 启用死信队列  
            dlq-name: my-dlq    # 指定 DLQ 名称  

解析:

• enable-dlq: true:开启 DLQ 功能,默认将失败消息发送到名为.dlq的队列。

• dlq-name: my-dlq:指定 DLQ 名称,覆盖默认命名规则。

3.应用场景

在这里插入图片描述

4.注意事项

• 监控 DLQ 堆积:需集成监控工具(如 Prometheus)告警 DLQ 消息量,避免积压。

• 死信处理策略:

• 人工介入:分析日志,修复代码后重投递。

• 自动重试:配置规则(如延迟重试、错误类型过滤)。

• 结合重试机制:设置合理的重试次数(如 3 次)后再进入 DLQ,减少无效处理。

总结

死信队列是消息系统的“安全网”,通过隔离异常消息保障系统健壮性,是生产环境中不可或缺的容错机制。

4.2 幂等性设计(基于 Redis)

通过 Redis 原子操作实现消息消费的幂等性,确保消息仅被处理一次,避免重复消费导致的数据不一致问题。

1.代码示例

@Component  
@RequiredArgsConstructor  
public class IdempotentConsumer {  
    private final StringRedisTemplate redisTemplate;  

    @StreamListener("inputChannel")  
    public void processEvent(OrderCreatedEvent event) {  
        // 生成唯一事件标识(基于业务唯一键,如订单ID)  
        String eventId = "event:" + event.getOrderId();  

        // 原子性操作:尝试将事件ID存入Redis(仅当Key不存在时成功)  
        Boolean isNew = redisTemplate.opsForValue()  
            .setIfAbsent(eventId, "processed", Duration.ofMinutes(10));  

        if (Boolean.TRUE.equals(isNew)) {  
            // 首次处理事件(执行业务逻辑)  
            System.out.println("Processing event: " + event);  
        } else {  
            // 重复事件,跳过处理(记录日志或告警)  
            System.out.println("Duplicate event ignored: " + event);  
        }  
    }  
}  

核心设计解析:

在这里插入图片描述

2.生产级优化建议

异常处理:

• Redis 操作失败:捕获RedisConnectionFailureException,结合重试机制或死信队列(DLQ)处理。

• 业务逻辑异常:删除 Redis Key 并重试,或标记为需人工干预。

性能优化:

• 集群模式:使用 Redis Cluster 提升可用性与扩展性。

• 本地缓存:结合本地缓存(如 Caffeine)减少 Redis 访问频率。

监控与告警:

• Redis Key 堆积:监控 Key 数量与内存占用,设置阈值告警。

• 重复事件频率:统计重复事件日志,分析系统瓶颈或攻击行为。

3.适用场景

• 支付回调:防止重复扣款或到账。

• 订单状态更新:避免多次触发发货、库存扣减。

• 事件溯源:确保事件回放时数据一致性。

总结

通过 Redis 的原子操作与唯一键设计,实现轻量级分布式幂等性控制。此方案兼顾简洁性与可靠性,适用于多数高并发场景,是消息驱动架构中保障数据一致性的核心手段之一。

4.3 监控与告警

指标采集:集成Prometheus监控消息吞吐量、延迟与错误率。

可视化看板:通过Grafana展示实时数据,设置阈值触发告警(如 DLQ 堆积超限)。

五、总结

5.1 核心重点

• 消息驱动架构:@Input/@Output 定义通道,Binder 抽象层简化消息传递与异步解耦。

• 事件溯源与 CQRS:事件日志驱动状态回溯,读写分离优化性能,确保一致性。

• 生产级治理:死信队列容错、幂等性防重、监控告警保障稳定性。

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

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

相关文章

突破连接边界!O9201PM Wi-Fi 6 + 蓝牙 5.4 模块重新定义笔记本无线体验

在当今数字化时代&#xff0c;笔记本电脑已成为人们工作、学习和娱乐的必备工具。而无线连接技术&#xff0c;作为笔记本电脑与外界交互的关键桥梁&#xff0c;其性能的优劣直接关乎用户体验的好坏。当下&#xff0c;笔记本电脑无线连接领域存在诸多痛点&#xff0c;严重影响着…

Python----计算机视觉处理(Opencv:图像颜色替换)

一、开运算 开运算就是对图像先进行腐蚀操作&#xff0c; 然后进行膨胀操作。开运算可以去除二值化图中的小的噪点&#xff0c;并分离相连的物体。 其主要目的就是消除那些小白点 在开运算组件中&#xff0c;有一个叫做kernel的参数&#xff0c;指的是核的大小&#xff0c;通常…

一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块&#xff0c;后面可以和学生模块&#xff0c;实现一对多的数据库操作。 blueprint下新建g…

Matlab 风力发电机磁悬浮轴承模型pid控制

1、内容简介 略 Matlab 174-风力发电机磁悬浮轴承模型pid控制 可以交流、咨询、答疑 2、内容说明 磁悬浮轴承具有无接触、无摩擦、高速度、高精度、能耗低、不需要需润滑无油污染、可靠性高、寿命长和密封等一系列显著的优点。将磁悬浮技术应用于风力发电机中可以降低风机切入…

FPGA中级项目1——IP核(ROM 与 RAM)

FPGA中级项目1——IP核&#xff08;ROM 与 RAM&#xff09; IP核简介 在 FPGA&#xff08;现场可编程门阵列&#xff09;设计中&#xff0c;IP 核&#xff08;Intellectual Property Core&#xff0c;知识产权核&#xff09;是预先设计好的、可重用的电路模块&#xff0c;用于实…

Matlab 基于专家pid控制的时滞系统

1、内容简介 Matlab 185-基于专家pid控制的时滞系统 可以交流、咨询、答疑 2、内容说明 略 在处理时滞系统&#xff08;Time Delay Systems&#xff09;时&#xff0c;使用传统的PID控制可能会面临挑战&#xff0c;因为时滞会导致系统的不稳定或性能下降。专家PID控制通过结…

Unity 笔记:在EditorWindow中绘制 Sorting Layer

在Unity开发过程中&#xff0c;可能会对旧资源进行批量修改&#xff0c;一个个手动修改费人费事&#xff0c;所以催生出了一堆批量工具。 分享一下在此过程中绘制 Sorting Layer 面板的代码脚本。 示意图&#xff1a; 在 EditorGUI 和 EditorGUILayer 中内置了 SortingLayerF…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …

蓝桥杯嵌入式赛道复习笔记2(按键控制LED灯,双击按键,单击按键,长按按键)

硬件原理解释 这张图展示了一个简单的按键电路原理图&#xff0c;其中包含四个按键&#xff08;PB0、PB1、PB2、PB3、PA0&#xff09;&#xff0c;每个按键通过一个10kΩ的上拉电阻连接到VDD&#xff08;电源电压&#xff09;&#xff0c;并接地&#xff08;GND&#xff09;。 …

每天五分钟深度学习PyTorch:循环神经网络RNN的计算以及维度信息

本文重点 前面我们学习了RNN从何而来,以及它的一些优点,我们也知道了它的模型的大概情况,本文我们将学习它的计算,我们来看一下RNN模型的每一个时间步在计算什么? RNN的计算 ht-1是上一时刻的输出,xt是本时刻的输入,然后二者共同计算得到了ht,然后yt通过ht计算得到,…

Ubuntu docker安装milvusdb

一、安装docker 1.更新软件包 sudo apt update sudo apt upgrade sudo apt-get install docker-ce docker-ce-cli containerd.io查看是否安装成功 docker -v二、使用国内的镜像下载 milvusdb Docker中国区官方镜像: https://registry.docker-cn.com milvusdb/milvus - Doc…

【优选算法篇】--深度解析之滑动窗口篇

滑动窗口 一、长度最小的子数组二、无重复字符的最长子串三、最大连续1的个数III四、水果成篮 一、长度最小的子数组 长度最小的子数组 解析&#xff1a; 首先看到这题 我们首先想到的是暴力枚举&#xff0c;就是暴力枚举所有子数组和。时间复杂度是O(n^3)。 我们这里用解法…

[STM32]新建工程||一个工程文件应该有哪些基本内容?

目录 一 、开发方法 1.直接使用程序来配置寄存器 2.基于库函数的方式 3.基于HAL库的方式 二 、常规的工程文件分类 STM32芯片型号分类以及缩写 ​三 步骤总结 四 工程架构 五 调用外设基本通用步骤 一 、开发方法 1.直接使用程序来配置寄存器 底层&#xff0c;直接&…

python编写的一个打砖块小游戏

游戏介绍 打砖块是一款经典的街机游戏&#xff0c;玩家控制底部的挡板&#xff0c;使球反弹以击碎上方的砖块。当球击中砖块时&#xff0c;砖块消失&#xff0c;球反弹&#xff1b;若球碰到挡板&#xff0c;则改变方向继续运动&#xff1b;若球掉出屏幕底部&#xff0c;玩家失…

【菜鸟飞】通过vsCode用python访问公网deepseek-r1等模型(Tocken模式)

目标 通过vsCode用python访问deepseek。 环境准备 没有环境的&#xff0c;vscode环境准备请参考之前的文章&#xff0c;另外需安装ollama&#xff1a; 【菜鸟飞】用vsCode搭建python运行环境-CSDN博客 AI入门1&#xff1a;AI模型管家婆ollama的安装和使用-CSDN博客 选读文章…

Figma介绍(基于云的协作式界面设计工具,主要用于UI/UX设计、原型制作和团队协作)

文章目录 注册和登录简单操作说明Figma介绍**核心特点**1. **云端协作与实时同步**2. **跨平台兼容**3. **高效设计工具**4. **原型交互与动效**5. **开发对接友好**6. **插件生态**7. **版本控制与历史记录** **适用场景**- **团队协作**&#xff1a;远程团队共同设计、评审、…

Text-to-SQL将自然语言转换为数据库查询语句

有关Text-To-SQL方法&#xff0c;可以查阅我的另一篇文章&#xff0c;Text-to-SQL方法研究 直接与数据库对话-text2sql Text2sql就是把文本转换为sql语言&#xff0c;这段时间公司有这方面的需求&#xff0c;调研了一下市面上text2sql的方法&#xff0c;比如阿里的Chat2DB,麻…

XSS漏洞靶场---(复现)

XSS漏洞靶场—&#xff08;复现&#xff09; 反射型 XSS 的特点是攻击者诱导用户点击包含恶意脚本的 URL&#xff0c;服务器接收到请求后将恶意脚本反射回响应页面&#xff0c;浏览器执行该脚本从而造成攻击&#xff0c;恶意脚本不会在服务器端存储。 Level 1(反射型XSS) 此漏…

基于ssm的电子病历系统(全套)

一、系统架构 前端&#xff1a;jsp | bootstrap | jquery 后端&#xff1a;spring | springmvc | mybatis 环境&#xff1a;jdk1.8 | mysql | maven | tomcat | idea 二、代码及数据库 三、功能介绍 01. 登录 02. 主页 03. 管理员-个人中心-修改密码…

Linux-数据结构-线性表-单链表

一.链表的概念 【1】线性表的链式存储 解决顺序存储的缺点&#xff0c;插入和删除&#xff0c;动态存储问题。 【2】特点&#xff1a; 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素&#xff0c;存储单元可以是连续的&#xff0c;也可以不连续。可以被存…