RabbitMQ安装配置,封装工具类,发送消息及监听

1. Get-Started

docker安装rabbitmq

  1. 拉取镜像
[root@heima ~]# docker pull rabbitmq:3.8-management
3.8-management: Pulling from library/rabbitmq
7b1a6ab2e44d: Pull complete 
37f453d83d8f: Pull complete 
e64e769bc4fd: Pull complete 
c288a913222f: Pull complete 
13adc5da62c6: Pull complete 
bd67e639afcb: Pull complete 
9a48b5ad2519: Pull complete 
1cdfc59624be: Pull complete 
8f5ad79f0ad6: Pull complete 
Digest: sha256:543f7268600a27a39e2fdd532f8df479636fc0cf528aadde88d5fe718bed71e4
Status: Downloaded newer image for rabbitmq:3.8-management
docker.io/library/rabbitmq:3.8-management
  1. 创建目录
mkdir -p /home/apps/rabbitmq/data
  1. 运行容器
docker run \
-d \
--name rabbitmq \
--restart=always \
--privileged=true \
-p 5672:5672 \
-p 15672:15672 \
-v /home/apps/rabbitmq/data:/var/lib/rabbitmq \
-e RABBITMQ_DEFAULT_VHOST=vhost0 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin123 \
rabbitmq:3.8-management
  1. 启用web界面管理插件
docker exec -it rabbitmq rabbitmq-plugins enable rabbitmq_management
或
-- 进入容器
docker exec -it rabbitmq /bin/bash
-- 安装插件
rabbitmq-plugins enable rabbitmq_management

  1. 浏览器访问 http://虚拟机ip:15672/出现以下界面说明安装成功。

在这里插入图片描述
输入上面在初始化Rabbitmq容器时我们自己指定了默认账号和密码:admin/admin123,如果没有指定的话那么rabbitmq的默认账号密码是:guest/guest

扩展安装延迟队列

  • 去官网下载插件(v3.8.17)
  • 地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
# 将下载好的插件复制到mq容器内部
docker cp rabbitmq_delayed_message_exchange-3.8.17.8f537ac.ez rabbitmq:/plugins

# 进入mq容器
docker exec -it rabbitmq /bin/bash

# 开启插件支持 
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

# 查看插件列表
rabbitmq-plugins list

2. 生产者模块

2.1 项目引入依赖:

rabbitmq依赖包,使用RabbitMq这2个依赖就够了。

<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-amqp</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
    <scope>provided</scope>
</dependency>

封装工具类用到的包

<!--hutool工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.17</version>
</dependency>

2.2 工具类

  • RabbitMqHelper
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
public class RabbitMqHelper {

    public static final String REQUEST_ID_HEADER = "requestId";
    private final RabbitTemplate rabbitTemplate;
    private final MessagePostProcessor processor = new BasicIdMessageProcessor();
    private final ThreadPoolTaskExecutor executor;

    public RabbitMqHelper(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
        executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(10);
        //配置最大线程数
        executor.setMaxPoolSize(15);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("mq-async-send-handler");

        // 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
    }

    /**
     * 根据exchange和routingKey发送消息
     */
    public <T> void send(String exchange, String routingKey, T t) {
        log.debug("准备发送消息,exchange:{}, RoutingKey:{}, message:{}", exchange, routingKey, t);
        // 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理
        String id = UUID.randomUUID().toString(true);
        CorrelationData correlationData = new CorrelationData(id);
        // 2.设置发送超时时间为500毫秒
        rabbitTemplate.setReplyTimeout(500);
        // 3.发送消息,同时设置消息id
        rabbitTemplate.convertAndSend(exchange, routingKey, t, processor, correlationData);
    }

    /**
     * 根据exchange和routingKey发送消息,并且可以设置延迟时间
     */
    public <T> void sendDelayMessage(String exchange, String routingKey, T t, Duration delay) {
        // 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理
        String id = UUID.randomUUID().toString(true);
        CorrelationData correlationData = new CorrelationData(id);
        // 2.设置发送超时时间为500毫秒
        rabbitTemplate.setReplyTimeout(500);
        // 3.发送消息,同时设置消息id
        rabbitTemplate.convertAndSend(exchange, routingKey, t, new DelayedMessageProcessor(delay), correlationData);
    }


    /**
     * 根据exchange和routingKey 异步发送消息,并指定一个延迟时间
     *
     * @param exchange   交换机
     * @param routingKey 路由KEY
     * @param t          数据
     * @param <T>        数据类型
     */
    public <T> void sendAsync(String exchange, String routingKey, T t, Long time) {
        String requestId = MDC.get(REQUEST_ID_HEADER);
        CompletableFuture.runAsync(() -> {
            try {
                MDC.put(REQUEST_ID_HEADER, requestId);
                // 发送延迟消息
                if (time != null && time > 0) {
                    sendDelayMessage(exchange, routingKey, t, Duration.ofMillis(time));
                } else {
                    send(exchange, routingKey, t);
                }
            } catch (Exception e) {
                log.error("推送消息异常,t:{},", t, e);
            }
        }, executor);
    }

    /**
     * 根据exchange和routingKey 异步发送消息
     *
     * @param exchange   交换机
     * @param routingKey 路由KEY
     * @param t          数据
     * @param <T>        数据类型
     */
    public <T> void sendAsync(String exchange, String routingKey, T t) {
        sendAsync(exchange, routingKey, t, null);
    }
}
  • MqConfig
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.MDC;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.ContainerCustomizer;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;


@Configuration
@ConditionalOnClass(value = {MessageConverter.class, AmqpTemplate.class})
public class MqConfig implements EnvironmentAware{

    public static final String REQUEST_ID_HEADER = "requestId";
    public static final String ERROR_EXCHANGE = "error.topic";
    public static final String ERROR_KEY_PREFIX = "error.";
    public static final String ERROR_QUEUE_TEMPLATE = "error.{}.queue";
    private String defaultErrorRoutingKey;
    private String defaultErrorQueue;

    @Bean(name = "rabbitListenerContainerFactory")
    @ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple",
            matchIfMissing = true)
    SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
            SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory,
            ObjectProvider<ContainerCustomizer<SimpleMessageListenerContainer>> simpleContainerCustomizer) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        simpleContainerCustomizer.ifUnique(factory::setContainerCustomizer);
        factory.setAfterReceivePostProcessors(message -> {
            Object header = message.getMessageProperties().getHeader(REQUEST_ID_HEADER);
            if(header != null) {
                MDC.put(REQUEST_ID_HEADER, header.toString());
            }
            return message;
        });
        return factory;
    }

    @Bean
    public MessageConverter messageConverter(ObjectMapper mapper){
        // 1.定义消息转换器
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(mapper);
        // 2.配置自动创建消息id,用于识别不同消息
        jackson2JsonMessageConverter.setCreateMessageIds(true);
        return jackson2JsonMessageConverter;
    }

    /**
     * <h1>消息处理失败的重试策略</h1>
     * 本地重试失败后,消息投递到专门的失败交换机和失败消息队列:error.queue
     */
    @Bean
    @ConditionalOnClass(MessageRecoverer.class)
    @ConditionalOnMissingBean
    public MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){
        // 消息处理失败后,发送到错误交换机:error.direct,RoutingKey默认是error.微服务名称
        return new RepublishMessageRecoverer(
                rabbitTemplate, ERROR_EXCHANGE, defaultErrorRoutingKey);
    }

    /**
     * rabbitmq发送工具
     *
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnClass(RabbitTemplate.class)
    public RabbitMqHelper rabbitMqHelper(RabbitTemplate rabbitTemplate){
        return new RabbitMqHelper(rabbitTemplate);
    }

    /**
     * 专门接收处理失败的消息
     */
    @Bean
    public DirectExchange errorMessageExchange(){
        return new DirectExchange(ERROR_EXCHANGE);
    }

    @Bean
    public Queue errorQueue(){
        return new Queue(defaultErrorQueue, true);
    }

    @Bean
    public Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){
        return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with(defaultErrorRoutingKey);
    }

    @Override
    public void setEnvironment(Environment environment) {
        String appName = environment.getProperty("spring.application.name");
        this.defaultErrorRoutingKey = ERROR_KEY_PREFIX + appName;
        this.defaultErrorQueue = StrUtil.format(ERROR_QUEUE_TEMPLATE, appName);
    }
}
  • Processor
import cn.hutool.core.lang.UUID;
import org.slf4j.MDC;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;

public class BasicIdMessageProcessor implements MessagePostProcessor {
    public static final String REQUEST_ID_HEADER = "requestId";
    @Override
    public Message postProcessMessage(Message message) throws AmqpException {
        String requestId = MDC.get(REQUEST_ID_HEADER);
        if (requestId == null) {
            requestId = UUID.randomUUID().toString(true);
        }
        // 写入RequestID标示
        message.getMessageProperties().setHeader(REQUEST_ID_HEADER, requestId);
        return message;
    }
}

----------------------------------------------------------------------

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import java.time.Duration;

public class DelayedMessageProcessor extends BasicIdMessageProcessor {

    private final long delay;

    public DelayedMessageProcessor(Duration delay) {
        this.delay = delay.toMillis();
    }

    @Override
    public Message postProcessMessage(Message message) throws AmqpException {
        // 1.添加消息id
        super.postProcessMessage(message);
        // 2.添加延迟时间
        message.getMessageProperties().setHeader("x-delay", delay);
        return message;
    }
}

2.3 配置

通常mq是放在common模块中,别的模块需要mq时就引入该模块,因此需要把MqConfig放在IOC容器里加载。

  • resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 com.tianji.common.autoconfigure.mq.MqConfig

配置rabbitmq的地址等信息

  • boostrap.yml
spring:
  rabbitmq:
    host: ${mydemo.mq.host:192.168.150.101}
    port: ${mydemo.mq.port:5672}
    virtual-host: ${mydemo.mq.vhost:/vhost0}
    username: ${mydemo.mq.username:admin}
    password: ${mydemo.mq.password:admin123}
    listener:
      simple:
        retry:
          enabled: ${mydemo.mq.listener.retry.enable:true} # 开启消费者失败重试
          initial-interval: ${mydemo.mq.listener.retry.interval:1000ms} # 初始的失败等待时长为1秒
          multiplier: ${mydemo.mq.listener.retry.multiplier:2} # 失败的等待时长倍数,下次等待时长 = multiplier * last-interval
          max-attempts: ${mydemo.mq.listener.retry.max-attempts:3} # 最大重试次数
          stateless: ${mydemo.mq.listener.retry.stateless:true} # true无状态;false有状态。如果业务中包含事务,这里改为false

2.4 测试文件

TestController

@RestController
public class TestController {

    @Autowired
    RabbitMqHelper rabbitMqHelper;

    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }

    @GetMapping("/sendMsg")
    public String sendMsg(){
        rabbitMqHelper.send(
                "order.topic", // "order.topic"
                "order.pay", // "order.pay"
                UserDto.builder()
                        .id(10001)
                        .name("gz")
                        .age(23)
                        .phone("15500000001")
                        .email("123@qq.com")
                        .build()
        );
        return "success";
    }
}

UserDto

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class UserDto {
    private Integer id;
    private String name;
    private String phone;
    private Integer age;
    private String email;
}

2.5 生产者测试

生产者模块目录结构
在这里插入图片描述

启动生产者服务,访问http://127.0.0.1:8080/sendMsg
在这里插入图片描述报错了,原因是没有消费者消费消息
但是发现创建了新的错误消息exchange和queue
在这里插入图片描述
因此,接下来创建消费者监听消息。

3. 消费者

3.1 引入通用模块

由于消费者也需要使用刚刚的工具类和UserDto用来接收消息。
在这里插入图片描述
而这些工具类和UserDto都在模块rabbitmq-demo中。因此,在消费者模块中引入该生产者模块(如果这些通用的配置和实体类dto都在一个通用的模块common中,哪些模块需要发送消息,哪些模块需要监听消息,就都引入common模块就行了)

    <groupId>com.gzdemo</groupId>
    <artifactId>rabbitmq-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

因为rabbitmq-demo模块中已经引入了RabbitMq相关依赖,因此消费者模块不需要重复引入RabbitMq相关的依赖。只需要把依赖包添加到classpath下,再import就行了
在这里插入图片描述
在这里插入图片描述

3.2 配置yml

与生产者的配置相同,通常只有消费者才需要配置listener,生产者不需要。
在这里插入图片描述

3.3 编写listener

import com.gzdemo.rabbitmq.pojos.UserDto;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class UserListener {
    /**
     * 监听消息
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "learning.lesson.pay.queue", durable = "true"),
            exchange = @Exchange(name = "order.topic", type = ExchangeTypes.TOPIC),
            key = "order.pay"
    ))
    public void listenLessonPay(UserDto userDto){
        System.out.println(userDto);
    }
}

3.4 消费者目录结构

这里是随便找了个同工程中的子模块作为消费者测试,在UserListener类中监听消息
在这里插入图片描述

4 测试消息发送、监听

启动这两个服务,浏览器访问http://127.0.0.1:8080/sendMsg,多访问几次后发现消费者接收到了消息
在这里插入图片描述生产者console

2024-06-14 05:46:39.187  INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [192.168.150.101:5672]
2024-06-14 05:46:39.210  INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory       : Created new connection: rabbitConnectionFactory#399d82:0/SimpleConnection@1673b17 [delegate=amqp://admin@192.168.150.101:5672/vhost0, localPort= 61317]

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

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

相关文章

第104天: 权限提升-Linux 系统环境变量定时任务权限配置不当MDUT 自动化

目录 案例一&#xff1a;Linux-环境变量文件配合 SUID-本地 案例二&#xff1a;Linux-定时任务打包配合 SUID-本地 案例三&#xff1a;Linux-定时任务文件权限配置不当-WEB&本地 案例四&#xff1a;Linux-第三方软件 MYSQL 数据库提权-WEB&本地 隧道出网 手工提权…

解决外网404:清除DNS缓存并配置host主机使用知名公共DNS服务

在 Windows 上清除/刷新 DNS 缓存 对于所有Windows版本&#xff0c;清除DNS缓存的过程都是相同的。你需要使用管理员权限打开命令提示符并运行ipconfig /flushdns。 浏览器清除DNS缓存 大多数现代的Web浏览器都有一个内置的DNS客户端&#xff0c;以防止每次访问该网站时…

男士穿什么内裤最透气?舒适透气的男士内裤推荐

作为一名专业的测评博主&#xff0c;我深知男士内裤对于日常穿着的重要性。因此&#xff0c;我决定深入挖掘男士内裤的细节之处&#xff0c;为大家带来最真实、最客观的评测体验。通过对比不同品牌、不同材质的男士内裤&#xff0c;我希望能帮助大家找到真正适合自己的那一款。…

Plonky3和Binius中的Brakedown多项式承诺协议解析及优化(3)

3.2 Expander Graph and Linear-Time Encodable Linear Code 线性时间编码是线性纠错码的一种&#xff0c;核心是扩展图&#xff08;Expander Graph&#xff09;&#xff0c;如下图所示&#xff1a; Figure 3 Expander Graph Expander Graph是一种具有强连通性的稀疏图&#…

CV预测:快速使用DenseNet神经网络

AI预测相关目录 AI预测流程&#xff0c;包括ETL、算法策略、算法模型、模型评估、可视化等相关内容 最好有基础的python算法预测经验 EEMD策略及踩坑VMD-CNN-LSTM时序预测对双向LSTM等模型添加自注意力机制K折叠交叉验证optuna超参数优化框架多任务学习-模型融合策略Transform…

App首页,美不胜收呀,虽说app没落了,但设计思想通用呀。

一个精心设计的首页仍然能够吸引用户的注意力。一个美观而富有创意的首页可以提升用户体验&#xff0c;增加用户的留存率和活跃度。 当我们打开一个app时&#xff0c;首页是用户第一眼看到的界面&#xff0c;因此设计师需要在有限的空间内展示出app的核心功能和特点。一个好的首…

短视频五大要素:成都科成博通文化传媒公司

短视频五大要素&#xff1a;揭秘成功视频的关键 在数字媒体时代&#xff0c;短视频已成为人们生活中不可或缺的一部分。无论是社交平台的日常分享&#xff0c;还是品牌营销的重要工具&#xff0c;短视频都以其短小精悍、内容丰富的特点赢得了广泛的关注和喜爱。然而&#xff0…

《数据安全产品及服务购买决策参考》

“新全球化”下的数据安全威胁态势与挑战 随着中国企业数字化转型和数字经济的高速发展&#xff0c;数据要素和数据安全的战略价值正不断提升。 同时&#xff0c;在“脱钩”与“新全球化”的全球政治经济博弈中&#xff0c;中国作为全球重要的数据安全市场之一&#xff0c;其…

软件构造 | Equality in ADT and OOP

软件构造 | Equality in ADT and OOP &#x1f9c7;1 Three ways to regard equality 1.1 Using AF to define the equality ADT是对数据的抽象&#xff0c; 体现为一组对数据的操作 抽象函数AF&#xff1a;内部表示→抽象表示 基于抽象函数AF定义ADT的等价操作&#xff0…

如何确保远程桌面安全

在数字化快速发展的今天&#xff0c;远程桌面技术广泛应用于企业办公、技术支持以及个人使用等领域。然而&#xff0c;随之而来的安全问题也不容忽视。白名单技术作为一种重要的安全防护手段&#xff0c;在确保远程桌面安全方面发挥着至关重要的作用。 一、白名单技术概述 白名…

[Qt] Qt Creator 以及 Qt 在线安装教程

一、Qt Creator 下载及安装 1、从以下镜像源下载安装包常规安装即可 Qt Creator 也可以在第二步Qt 在线安装时一次性勾选安装&#xff0c;见后文 Qt Creator 中科大源下载地址 二、Qt 在线安装 1、根据所在平台选择对应的安装器下载 Qt 在线安装器下载 2、可能的安装报错…

[华为北向网管NCE开发教程(6)消息订阅

1.作用 之前介绍的都是我们向网管NCE发起请求获取数据&#xff0c;消息订阅则反过来&#xff0c;是网管NCE系统给我们推送信息。其原理和MQ&#xff0c;JMS这些差不多&#xff0c;这里不过多累述。 2.场景 所支持订阅的场景有如下&#xff0c;以告警通知为例&#xff0c;当我…

建筑工程软件Revit中复杂大模型如何实现Web端轻量化?| HOOPS技术应用

建筑信息模型&#xff08; BIM&#xff09;技术在建筑工程中扮演着越来越重要的角色&#xff0c;而Autodesk Revit作为主流的BIM软件&#xff0c;被广泛应用于设计、施工和管理。然而&#xff0c;Revit生成的复杂大模型常常由于数据量庞大而难以直接在Web端展示和操作。这时&am…

linux日志管理之journalctl命令

一、日志查询 1.输出所有日志或按相关要求输出 输出所有日志 #journalctl查看实时日志 #journalctl -f查看最后n行 #journalctl -n 10不分页显示 #journalctl --no-pager适合阅读模式 #journalctl -p 3 -o json-pretty 查看内核日志 #journalctl -k 2.按服务查询 #journal…

植物大战僵尸杂交版最新pvzHE_v2.1.0含游戏窗口放大工具

植物大战僵尸杂交版是由B站”潜艇伟伟迷”UP主制作的一款同人策略塔防游戏&#xff0c;也叫pvzHE&#xff0c;该游戏由《植物大战僵尸》原版魔改而来&#xff0c;引入了创新的杂交合成系统&#xff0c;让玩家可以将不同植物进行杂交&#xff0c;创造出具有全新能力和外观的植物…

【低级错误笔记】debug的步入按钮为灰色

访问login方法没有加RestController 难怪点击登录的时候总是显示404资源错误 看到闪过一秒的无法访问/employee/login路径才发现这个思路……………………………………………………………………………………………………………………………………太无语了 拖拖拉拉从6.3到今天…

Digital Video Repair3.7.1.0 --一款免费的视频文件修复工具,供大家学习研究参考

下载地址&#xff1a; https://download.csdn.net/download/weixin_43097956/89431959

领夹麦克风哪个品牌音质最好?轻揭无线麦克风哪个品牌性价比高!

​随着短视频热潮的兴起&#xff0c;越来越多的人倾向于用vlog记录日常生活&#xff0c;同时借助短视频和直播平台开辟了副业。在这一过程中&#xff0c;麦克风在近两年内迅速发展&#xff0c;从最初的简单收音功能演变为拥有多样款式和功能&#xff0c;以满足视频创作的需求。…

用 微 / 积分思想妙解关于等比数列的和

同理&#xff0c;也是微积分思想&#xff1a; 求 (\sum_{k1}^n q^k) 的和&#xff1a; 我们知道几何级数的求和公式&#xff1a; ∑ k 0 n q k 1 − q n 1 1 − q (对于 q ≠ 1 ) \sum_{k0}^n q^k \frac{1-q^{n1}}{1-q} \quad \text{(对于 } q \neq 1\text{)} k0∑n​qk…

算法02 递归算法及其相关问题【C++实现】

递归 在编程中&#xff0c;我们把函数直接或者间接调用自身的过程叫做递归。 递归处理问题的过程是&#xff1a;通常把一个大型的复杂问题&#xff0c;转变成一个与原问题类似的&#xff0c;规模更小的问题来进行求解。 递归的三大要素 函数的参数。在用递归解决问题时&…