Spring Boot 3 集成 RabbitMQ 实践指南

Spring Boot 3 集成 RabbitMQ 实践指南

1. RabbitMQ 核心原理

1.1 什么是RabbitMQ

RabbitMQ是一个开源的消息代理和队列服务器,使用Erlang语言开发,基于AMQP(Advanced Message Queuing Protocol)协议实现。它支持多种消息传递模式,具有高可用性、可扩展性和可靠性等特点。

1.2 核心概念

1.2.1 基础组件
  1. Producer(生产者)

    • 消息的发送者
    • 负责创建消息并发布到RabbitMQ中
  2. Consumer(消费者)

    • 消息的接收者
    • 连接到RabbitMQ服务器并订阅队列
  3. Exchange(交换机)

    • 接收生产者发送的消息并根据路由规则转发到队列
    • 类型:
      • Direct Exchange:根据routing key精确匹配
      • Topic Exchange:根据routing key模式匹配
      • Fanout Exchange:广播到所有绑定队列
      • Headers Exchange:根据消息属性匹配
  4. Queue(队列)

    • 消息存储的地方
    • 支持持久化、临时、自动删除等特性
  5. Binding(绑定)

    • 交换机和队列之间的虚拟连接
    • 定义消息路由规则
1.2.2 高级特性
  1. 消息持久化

    • 交换机持久化:创建时设置durable=true
    • 队列持久化:创建时设置durable=true
    • 消息持久化:设置delivery-mode=2
  2. 消息确认机制

    • 生产者确认:Publisher Confirm和Return机制
    • 消费者确认:自动确认、手动确认、批量确认
  3. 死信队列(DLX)

    • 消息被拒绝且不重新入队
    • 消息过期(TTL)
    • 队列达到最大长度

1.3 应用场景

  1. 异步处理

    • 发送邮件、短信通知
    • 日志处理、报表生成
    • 文件处理、图片处理
  2. 应用解耦

    • 系统间通信
    • 服务解耦
    • 流程分离
  3. 流量控制

    • 削峰填谷
    • 请求缓冲
    • 流量整形
  4. 定时任务

    • 延迟队列
    • 定时处理
    • 任务调度

2. 环境搭建

2.1 基础环境

  • Spring Boot: 3.x
  • Java: 17+
  • RabbitMQ: 3.12+
  • Maven/Gradle

2.2 依赖配置

<dependencies>
    <!-- Spring Boot Starter AMQP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
</dependencies>

2.3 基础配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    # 消息确认配置
    publisher-confirm-type: correlated  # 开启发布确认
    publisher-returns: true             # 开启发布返回
    template:
      mandatory: true                   # 消息路由失败返回
    # 消费者配置
    listener:
      simple:
        acknowledge-mode: manual        # 手动确认
        prefetch: 1                     # 每次获取消息数量
        retry:
          enabled: true                 # 开启重试
          initial-interval: 1000        # 重试间隔时间
          max-attempts: 3               # 最大重试次数
          multiplier: 1.0              # 重试时间乘数
    # SSL配置(可选)
    ssl:
      enabled: false
      key-store: classpath:keystore.p12
      key-store-password: password
      trust-store: classpath:truststore.p12
      trust-store-password: password

3. 核心配置类

3.1 RabbitMQ配置类

@Configuration
@EnableRabbit
public class RabbitMQConfig {
    
    // 交换机名称
    public static final String BUSINESS_EXCHANGE = "business.exchange";
    public static final String DEAD_LETTER_EXCHANGE = "dead.letter.exchange";
    
    // 队列名称
    public static final String BUSINESS_QUEUE = "business.queue";
    public static final String DEAD_LETTER_QUEUE = "dead.letter.queue";
    
    // 路由键
    public static final String BUSINESS_KEY = "business.key";
    public static final String DEAD_LETTER_KEY = "dead.letter.key";
    
    // 业务交换机
    @Bean
    public DirectExchange businessExchange() {
        return ExchangeBuilder.directExchange(BUSINESS_EXCHANGE)
                .durable(true)
                .build();
    }
    
    // 死信交换机
    @Bean
    public DirectExchange deadLetterExchange() {
        return ExchangeBuilder.directExchange(DEAD_LETTER_EXCHANGE)
                .durable(true)
                .build();
    }
    
    // 业务队列
    @Bean
    public Queue businessQueue() {
        Map<String, Object> args = new HashMap<>(3);
        // 消息过期时间
        args.put("x-message-ttl", 60000);
        // 队列最大长度
        args.put("x-max-length", 1000);
        // 死信交换机
        args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        args.put("x-dead-letter-routing-key", DEAD_LETTER_KEY);
        
        return QueueBuilder.durable(BUSINESS_QUEUE)
                .withArguments(args)
                .build();
    }
    
    // 死信队列
    @Bean
    public Queue deadLetterQueue() {
        return QueueBuilder.durable(DEAD_LETTER_QUEUE).build();
    }
    
    // 业务绑定
    @Bean
    public Binding businessBinding() {
        return BindingBuilder.bind(businessQueue())
                .to(businessExchange())
                .with(BUSINESS_KEY);
    }
    
    // 死信绑定
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue())
                .to(deadLetterExchange())
                .with(DEAD_LETTER_KEY);
    }
    
    // 消息转换器
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
    
    // RabbitTemplate配置
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter());
        return rabbitTemplate;
    }
}

3.2 消息确认配置

@Configuration
@Slf4j
public class RabbitConfirmConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }
    
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            log.info("消息发送到交换机成功: correlationData={}", correlationData);
        } else {
            log.error("消息发送到交换机失败: correlationData={}, cause={}", correlationData, cause);
            // 处理失败逻辑,如重试、告警等
        }
    }
    
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        log.error("消息路由到队列失败: exchange={}, routingKey={}, replyCode={}, replyText={}, message={}",
                returned.getExchange(),
                returned.getRoutingKey(),
                returned.getReplyCode(),
                returned.getReplyText(),
                new String(returned.getMessage().getBody()));
        // 处理失败逻辑,如重试、告警等
    }
}

4. 消息生产者

4.1 消息发送服务

@Service
@Slf4j
public class MessageProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendMessage(Object message, String exchange, String routingKey) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        
        try {
            rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
            log.info("消息发送成功: message={}, exchange={}, routingKey={}, correlationData={}",
                    message, exchange, routingKey, correlationData);
        } catch (Exception e) {
            log.error("消息发送异常: message={}, exchange={}, routingKey={}, correlationData={}, error={}",
                    message, exchange, routingKey, correlationData, e.getMessage());
            throw new RuntimeException("消息发送失败", e);
        }
    }
    
    public void sendDelayMessage(Object message, String exchange, String routingKey, long delayMillis) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        
        MessagePostProcessor messagePostProcessor = msg -> {
            msg.getMessageProperties().setDelay((int) delayMillis);
            return msg;
        };
        
        try {
            rabbitTemplate.convertAndSend(exchange, routingKey, message, messagePostProcessor, correlationData);
            log.info("延迟消息发送成功: message={}, exchange={}, routingKey={}, delay={}, correlationData={}",
                    message, exchange, routingKey, delayMillis, correlationData);
        } catch (Exception e) {
            log.error("延迟消息发送异常: message={}, exchange={}, routingKey={}, delay={}, correlationData={}, error={}",
                    message, exchange, routingKey, delayMillis, correlationData, e.getMessage());
            throw new RuntimeException("延迟消息发送失败", e);
        }
    }
}

5. 消息消费者

5.1 消息处理服务

@Service
@Slf4j
public class MessageConsumer {
    
    @RabbitListener(queues = RabbitMQConfig.BUSINESS_QUEUE)
    public void handleMessage(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        
        try {
            // 获取消息内容
            String messageBody = new String(message.getBody());
            log.info("收到消息: message={}, deliveryTag={}", messageBody, deliveryTag);
            
            // 业务处理
            processMessage(messageBody);
            
            // 手动确认消息
            channel.basicAck(deliveryTag, false);
            log.info("消息处理成功: deliveryTag={}", deliveryTag);
            
        } catch (Exception e) {
            log.error("消息处理异常: deliveryTag={}, error={}", deliveryTag, e.getMessage());
            
            // 判断是否重新投递
            if (message.getMessageProperties().getRedelivered()) {
                log.error("消息已重试,拒绝消息: deliveryTag={}", deliveryTag);
                channel.basicReject(deliveryTag, false);
            } else {
                log.info("消息首次处理失败,重新投递: deliveryTag={}", deliveryTag);
                channel.basicNack(deliveryTag, false, true);
            }
        }
    }
    
    private void processMessage(String message) {
        // 实现具体的业务逻辑
        log.info("处理消息: {}", message);
    }
}

5.2 死信消息处理

@Service
@Slf4j
public class DeadLetterConsumer {
    
    @RabbitListener(queues = RabbitMQConfig.DEAD_LETTER_QUEUE)
    public void handleDeadLetter(Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        
        try {
            String messageBody = new String(message.getBody());
            log.info("收到死信消息: message={}, deliveryTag={}", messageBody, deliveryTag);
            
            // 死信消息处理逻辑
            processDeadLetter(messageBody);
            
            channel.basicAck(deliveryTag, false);
            log.info("死信消息处理成功: deliveryTag={}", deliveryTag);
            
        } catch (Exception e) {
            log.error("死信消息处理异常: deliveryTag={}, error={}", deliveryTag, e.getMessage());
            channel.basicReject(deliveryTag, false);
        }
    }
    
    private void processDeadLetter(String message) {
        // 实现死信消息处理逻辑
        log.info("处理死信消息: {}", message);
    }
}

6. 接口控制器

@RestController
@RequestMapping("/api/mq")
@Slf4j
public class MessageController {
    
    @Autowired
    private MessageProducer messageProducer;
    
    @PostMapping("/send")
    public ResponseEntity<String> sendMessage(@RequestBody MessageDTO message) {
        try {
            messageProducer.sendMessage(message.getContent(),
                    RabbitMQConfig.BUSINESS_EXCHANGE,
                    RabbitMQConfig.BUSINESS_KEY);
            return ResponseEntity.ok("消息发送成功");
        } catch (Exception e) {
            log.error("消息发送失败: {}", e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("消息发送失败: " + e.getMessage());
        }
    }
    
    @PostMapping("/send/delay")
    public ResponseEntity<String> sendDelayMessage(
            @RequestBody MessageDTO message,
            @RequestParam long delayMillis) {
        try {
            messageProducer.sendDelayMessage(message.getContent(),
                    RabbitMQConfig.BUSINESS_EXCHANGE,
                    RabbitMQConfig.BUSINESS_KEY,
                    delayMillis);
            return ResponseEntity.ok("延迟消息发送成功");
        } catch (Exception e) {
            log.error("延迟消息发送失败: {}", e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("延迟消息发送失败: " + e.getMessage());
        }
    }
}

7. 监控与运维

7.1 RabbitMQ管理界面

  • 访问地址:http://localhost:15672
  • 默认账号:guest/guest
  • 主要功能:
    • 队列监控
    • 交换机管理
    • 连接状态
    • 消息追踪

7.2 Prometheus + Grafana监控

# prometheus.yml
scrape_configs:
  - job_name: 'rabbitmq'
    static_configs:
      - targets: ['localhost:15692']

7.3 日志配置

logging:
  level:
    org.springframework.amqp: INFO
    com.your.package: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

7.4 告警配置

@Configuration
public class RabbitMQAlertConfig {
    
    @Value("${alert.dingtalk.webhook}")
    private String webhookUrl;
    
    @Bean
    public AlertService alertService() {
        return new DingTalkAlertService(webhookUrl);
    }
}

8. 最佳实践

8.1 消息幂等性处理

@Service
public class MessageIdempotentHandler {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public boolean isProcessed(String messageId) {
        String key = "mq:processed:" + messageId;
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, "1", 24, TimeUnit.HOURS));
    }
}

8.2 消息重试策略

@Configuration
public class RetryConfig {
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        retryTemplate.setRetryPolicy(retryPolicy);
        
        return retryTemplate;
    }
}

8.3 消息序列化

@Configuration
public class MessageConverterConfig {
    
    @Bean
    public MessageConverter jsonMessageConverter() {
        Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
        converter.setCreateMessageIds(true);
        return converter;
    }
}

8.4 消息追踪

@Aspect
@Component
@Slf4j
public class MessageTraceAspect {
    
    @Around("@annotation(org.springframework.amqp.rabbit.annotation.RabbitListener)")
    public Object traceMessage(ProceedingJoinPoint joinPoint) throws Throwable {
        String messageId = MDC.get("messageId");
        log.info("开始处理消息: messageId={}", messageId);
        
        try {
            Object result = joinPoint.proceed();
            log.info("消息处理完成: messageId={}", messageId);
            return result;
        } catch (Exception e) {
            log.error("消息处理异常: messageId={}, error={}", messageId, e.getMessage());
            throw e;
        }
    }
}

9. 常见问题与解决方案

9.1 消息丢失问题

  1. 生产者确认机制
  2. 消息持久化
  3. 手动确认模式
  4. 集群高可用

9.2 消息重复消费

  1. 幂等性处理
  2. 消息去重
  3. 业务检查

9.3 消息堆积问题

  1. 增加消费者数量
  2. 提高处理效率
  3. 队列分片
  4. 死信队列处理

9.4 性能优化

  1. 合理设置预取数量
  2. 批量确认消息
  3. 消息压缩
  4. 连接池优化

10. 高可用部署

10.1 集群配置

spring:
  rabbitmq:
    addresses: rabbit1:5672,rabbit2:5672,rabbit3:5672
    username: admin
    password: password
    virtual-host: /

10.2 镜像队列

# 设置镜像策略
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

10.3 负载均衡

# nginx.conf
upstream rabbitmq_cluster {
    server rabbit1:15672;
    server rabbit2:15672;
    server rabbit3:15672;
}

11. 参考资源

  1. Spring AMQP官方文档
  2. RabbitMQ官方文档
  3. Spring Boot官方文档

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

斩波放大器

目录 简介 自稳零斩波放大器 噪声 简介 双极性放大器的失调电压为25 μV&#xff0c;漂移为0.1 μV/C。斩波放大器尽管存在一些不利影 响&#xff0c;但可提供低于5 μV的失调电压&#xff0c;而且不会出现明显的失调漂移&#xff0c; 以下图1给出了基本的斩波放大器电路图。…

windows设置暂停更新时长

windows设置暂停更新时长 win11与win10修改注册表操作一致 &#xff0c;系统界面不同 1.打开注册表 2.在以下路径 \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 右键新建 DWORD 32位值&#xff0c;名称为FlightSettingsMaxPauseDays 根据需求填写数…

DIALOGPT:大规模生成式预训练用于对话响应生成

摘要 我们提出了一个大规模、可调节的神经对话响应生成模型&#xff0c;DIALOGPT&#xff08;对话生成预训练变换器&#xff09;。该模型训练于从2005年至2017年间Reddit评论链中提取的1.47亿次类似对话的交流&#xff0c;DIALOGPT扩展了Hugging Face的PyTorch变换器&#xff…

Mac端不显示正常用户名,变成192的解决方法

今天打开终端&#xff0c;本应该显示机器名的&#xff0c;但是此时显示了192。 问题原因&#xff1a; 当路由器的DNS使用默认的 192.168.1.1 或 192.168.0.1 的时候 Terminal 里的计算机名 会变成 localhost。当路由器的DNS使用自定义的 例如 运营商的DNS 或者 公共DNS的时候 …

SD 卡无屏安装启动树莓派5

最近想用一下树莓派5&#xff0c;拿出来一看&#xff0c;是 Micro-HMDI 的接口&#xff0c;手头正好没有这个接口线&#xff0c;便研究如何在没有显示屏的情况下&#xff0c;安装启动树莓派。 一、使用 Raspberry Pi Imager 烧录 SD 卡 选择 Raspberry Pi Imager 来烧录 SD 卡…

Xlua 编译 Windows、UWP、Android、iOS 平台支持库

Xlua 编译 Windows、UWP、Android、iOS 平台支持库 Windows&#xff1a; 安装 Visual Studio&#xff08;推荐 2017 或更高版本&#xff09; 安装 CMake&#xff08;https://cmake.org/&#xff09; macOS&#xff1a; 安装 Xcode 和命令行工具 安装 CMake 检查 cmake 是否安…

npm : 无法加载文件 E:\ProgramFiles\Nodejs\npm.ps1,因为在此系统上禁止运行脚本。

这个错误是因为 Windows 系统的 PowerShell 执行策略 限制了脚本的运行。默认情况下&#xff0c;PowerShell 的执行策略是 Restricted&#xff0c;即禁止运行任何脚本。以下是解决该问题的步骤&#xff1a; 1. 检查当前执行策略 打开 PowerShell&#xff08;管理员权限&#x…

基于专利合作地址匹配的数据构建区域协同矩阵

文章目录 地区地址提取完成的处理代码 在专利合作申请表中&#xff0c;有多家公司合作申请。在专利权人地址中&#xff0c; 有多个公司的地址信息。故想利用这里多个地址。想用这里的地址来代表区域之间的专利合作情况代表区域之间的协同、协作情况。 下图是专利合作表的一部分…

若依vue plus环境搭建

继前面文章若依系统环境搭建记录-CSDN博客 把ruoyi vue plus也摸索了下。 作者是疯狂的狮子&#xff0c;dromara/RuoYi-Vue-Plus 初始化文档&#xff1a;项目初始化&#xff0c;环境搭建的视频&#xff1a;RuoYi-Vue-Plus 5.0 搭建与运行_哔哩哔哩_bilibili 上来就列出了一…

在ubuntu如何安装samba软件?

我们在开发过程中&#xff0c;经常修改代码&#xff0c;可以安装samba文件来实现&#xff0c;把ubuntu的存储空间指定为我们win上的一个磁盘&#xff0c;然后我们在或者磁盘里面创建.c文件&#xff0c;进行代码修改和编写。samba能将linux的文件目录直接映射到windows&#xff…

论文阅读笔记:Deep Face Recognition: A Survey

论文阅读笔记&#xff1a;Deep Face Recognition: A Survey 1 介绍2 总览2.1 人脸识别组件2.1.1 人脸处理2.1.2 深度特征提取2.1.3 基于深度特征的人脸对比 3 网络结构和损失函数3.1 判别损失函数的演化3.1.1 基于欧式距离的损失3.1.2 基于角度/余弦边距的损失3.1.3 Softmax损失…

使用 Polars 进行人工智能医疗数据分析(ICU数据基本测试篇)

引言 在医疗领域&#xff0c;数据就是生命的密码&#xff0c;每一个数据点都可能蕴含着拯救生命的关键信息。特别是在 ICU 这样的重症监护场景中&#xff0c;医生需要实时、准确地了解患者的病情变化&#xff0c;以便做出及时有效的治疗决策。而随着医疗技术的飞速发展&#x…

Fiddler在Windows下抓包Https

文章目录 1.Fiddler Classic 配置2.配置浏览器代理自动代理手动配置浏览器代理 3.抓取移动端 HTTPS 流量&#xff08;可选&#xff09;解决抓取 HTTPS 失败问题1.Fiddler证书过期了 默认情况下&#xff0c;Fiddler 无法直接解密 HTTPS 流量。需要开启 HTTPS 解密&#xff1a; 1…

Anaconda安装 超详细版 (2025版)

目录 第一步&#xff1a;下载anaconda安装包 官网下载&#xff1a;Anaconda | Built to Advance Open Source AI 清华大学镜像站下载&#xff08;速度较快&#xff09; 第二步&#xff1a;安装anaconda 第三步&#xff1a;验证安装 扩展 创建conda基本环境 激活conda环…

想知道两轮差速方形底盘 URDF 咋做,ROS2 配 Rviz 咋显示吗?看这里!

视频讲解 想知道两轮差速方形底盘 URDF 咋做&#xff0c;ROS2 配 Rviz 咋显示吗&#xff1f;看这里&#xff01; 模型概述 一个方形底盘和两个差速驱动轮 URDF 代码 <?xml version"1.0" encoding"utf-8"?> <robot name"diff"> …

轻量化网络设计|ShuffleNet:深度学习中的轻量化革命

一、引言 在深度学习中&#xff0c;卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;无疑是大家最耳熟能详的算法之一。自诞生以来&#xff0c;CNN 在图像分类、目标检测、语义分割等众多计算机视觉任务中取得了令人瞩目的成就&#xff0c;…

最好Wordpree+Apache+PHP安装教程

前提需要 PHP的安装最少需要7.4以上Mysql的安装&#xff0c;直接默认最新版就行APache服务器&#xff08;HTTP服务器&#xff0c;只有用这个你的软件才能在服务器上运行&#xff09; 安装apache 安装 sudo apt install apache2查看防火墙 sudo ufw app list如果有 Apache那…

Linux实操——在服务器上直接从百度网盘下载(/上传)文件

Linux Linux实操——在服务器上直接从百度网盘下载&#xff08;/上传&#xff09;文件 文章目录 Linux前言一、下载并安装bypy工具二、认证并授权网盘账号三、将所需文件转移至目的文件夹下四、下载文件五、上传文件六、更换绑定的百度云盘账户 前言 最近收到一批很大的数据&…

一周一个Unity小游戏2D反弹球游戏 - 移动的弹板(触屏版)

前言 上文中实现了用鼠标移动控制弹板的移动&#xff0c;本文将实现手指触屏时弹板跟随手指移动的功能&#xff0c;并通过使用Unity自带的Device Simulator Devices Package来验证和模拟触屏设备的使用场景。 安装Device Simulator Devices Package 打开Unity Package Manager&…

DeepSeek 开源狂欢周(一)FlashMLA:高效推理加速新时代

上周末&#xff0c;DeepSeek在X平台&#xff08;Twitter&#xff09;宣布将开启连续一周的开源&#xff0c;整个开源社区为之沸腾&#xff0c;全球AI爱好者纷纷为关注。没错&#xff0c;这是一场由DeepSeek引领的开源盛宴&#xff0c;推翻了传统推理加速的种种限制。这周一&…