RabbitMQ实现消息的延迟推送或延迟发送

news2024/9/20 8:13:02

一、RabbitMQ是什么?

1.RabbitMQ简介

RabbitMQ是有erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列。
常见的消息队列有:RabbitMQ、Kafka 和 ActiveMQ

2.RabbitMQ的优点

RabbitMQ最初起源于金融系统,用于不同模块之间的消息通讯。

优点:

可靠性:可持久化,消息传输和发布确认。
灵活性:通过交换机将消息路由到对应的队列。
集群:多台mq可组成集群,对外提供整体服务
支持多语言:支持多种语言
可界面操作:提供简易的用户操作界面
等等。

3.常用组件

1.生产者(Producer):消息的制造者
2.消费者(Consumer):消息的消费者
3.消息(Message):消息对象,包括业务参数和mq的参数
4.队列(Queue):缓存或暂存储消息的容器
5.连接(Connection):应用服务和mq进行交互的tcp连接
6.信道(Channel):AMQP命令都是通过信道来完成的,它是一个虚拟的通道,来复用ctp连接。
7.交换机(Exchange):负责从生产者接收消息,根据路由规则发送到指定的队列里面。
8.路由键(Routing Key):交换机根据路由键将消息发往指定的队列。
9.虚拟主机(Virtual Host):就像是一个RabbitMQ 服务。

4.RabbitMQ的结构图

一张图介绍RabbitMQ组成以及各个组件之间的关系(参考网上画的)。
在这里插入图片描述

5.交换机的类型

交换机是用来发送消息到指定的队列里面的,它使用哪种路由算法是由交换机类型和绑定的规则所决定的。

A-直连交换机:
直连交换机是根据交换机和队列之间绑定的路由键,来将消息发往指定的队列里面。如果交换机与多个队列绑定,则在发送携带路由键的消息时,只发给此路由键的队列,每个队列都是相同副本(比较适合一对一)。

例如:我用直连交换机test-direct-exchange根据路由键test-direct发送一条消息,然后去队列里面看消息,如图所示

在这里插入图片描述
在这里插入图片描述

B-扇形交换机:
扇形交换机是将消息发往与它绑定的队列,而不去理会绑定的路由键是否一致。如果交换机与多个队列绑定,每个队列都是相同副本,起到广播的作用。

例如:我用扇形交换机test-fanout-exchange根据路由键test-fanout发送一条消息,然后去队列里面看消息,如图所示
在这里插入图片描述

但是三个队列都收到了消息,可见扇形交换机会忽略其路由键
在这里插入图片描述

C-主题交换机:
主题交换机是通过消息的路由键跟交换机和队列之间的绑定路由键进行匹配,将消息发给匹配上的队列,跟直连交换机的一对多相似,但是他的路由键可以支持模糊匹配。

例如:我用主题交换机test-topic-exchange根据路由键test.topic2发送一条消息,然后去队列里面看消息,如图所示
在这里插入图片描述
根据消息路由键和绑定的路由键进行模糊匹配,推送消息。
在这里插入图片描述

D-头交换机:
头交换机是主题交换机有点相似,主题交换机是基于路由键,而头交换机是基于消息的headers数据,所以在发送消息给头交换机时指定Routing key是不起作用的。头交换机在绑定队列时需要指定参数Arguments,发送消息时需要指定headers和Arguments相匹配,消息才能被推到相应的队列。

例如:我用头交换机test-headers-exchange根据路由键test-headers1发送一条消息,然后去队列里面看消息,如图所示
在这里插入图片描述

如果前两个队列能收到消息,证明路由键不生效。
在这里插入图片描述

二、定时推送思路实现

rabbitmq实现延时消息主要有两种方式:

死信消息(队列ttl+死信exchange)
延时插件 (rabbitmq-delayed-message-exchange)

rabbitmq 实现方式一:队列ttl+死信exchange

简述:使用两个队列,一个队列接收消息不消费,等待指定时间后消息死亡,再由该队列绑定的死信exchange再次将其路由到另一个队列提供业务消费。

ttl 和 死信exchange 相关知识
ttl
先贴两个个rabbitmq官方文档:

Time-To-Live and Expiration:https://www.rabbitmq.com/ttl.html
Dead Letter Exchangeshttps:https://www.rabbitmq.com/dlx.html
我这里也简单介绍下:
rabbitmq 可以给 消息 和 队列 设置 ttl(生存时间)
队列设置:x-message-ttl=60000 (队列中所有消息都只有60s存活时间)
指定消息设置:expire=60000 指定消息只有60s存活时间
如果队列和消息同时设置了ttl,则取较小的那个作为ttl。消息死亡后不会被消费者消费。

死信exchange
死信(死亡的消息):

消费者使用 basic.reject 或 basic.nack 并将requeue参数设置为 false 来否定的消息
ttl到期的消息
队列超过长度限制被丢弃的消息
当一个队列设置了死信exchange 后,这个队列的死信都会被投递到死信exchange中,然后可以再次路由到其他队列中(如果指定了死信routing key 则死信消息routing key 变为设置的routing key,未设置则为原始 routing key)。

使用介绍
先声明一个消费队列 queue_dlx,用来接收死信消息,并提供消费;
然后声明一个死信exchange_dlx, 绑定 queue_dlx,接收消息后路由至queue_dlx;
声明一个延迟队列,queue_delay, 用来接收业务消息,但不提供消费,等待消息死亡后转至死信exchange。(即延迟)
声明一个exchange,由业务发送消息到exchange,然后转至queue_delay.
一个消息的流程大概是:
在这里插入图片描述
在这里插入图片描述

简单分析
缺点:
1.只能支持固定延迟等级的消息
2.使用较复杂,得声明一堆队列&exchange
3.一个致命的问题就是消息顺序,不会按照延迟时间的先后顺序输出,而是按照queue本身先进先出的规则。即10秒延迟的消息如果是在20秒延迟消息后扔入的,那么也要等20秒延迟的消息输出后才能输出。除非消息的延迟时间是一致的否则无法满足业务要求

优点:
1.支持镜像队列复制,实现高可用
2.支持大量消息(成千上万)
3.适用场景: 使用固定延迟时间的场景。

备注:对于高版本(3.6及以上)的rabbitmq建议使用lazy-mode作为延迟队列,防止大量延时消息堆积而占用大量内存,从而触发rabbitmq换页阻塞队列。 (如果使用spring的话,即使低版本rabbitmq也不用太担心:spring-amqp默认发送持久化消息,即使触发换页,也只是把消息从内存中逐出而已。)

rabbitmq 实现方式二:rabbitmq延时插件

简述:延时消息不直接投递到队列中,而是先转储到本地Mnesia数据库中,然后定时器在消息到期后再将其投递到队列中。

延时插件使用
关于用法可以直接看这个文档或者网上搜一搜,这里就不介绍了。
github地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

其大概原理就是:指定了延时的消息,会被先保存在 Mnesia (erlang编写的数据库管理系统)中,然后有一个定时器去查询最近需要被投递的消息,将其投递到目标队列中。

简单分析
优点:
基本支持任意延迟时间(最大延迟时间不应超过 24.8 天)
使用 rabbitmq 的延迟插件(rabbitmq delayed message plugin)时,可以设置延迟的最大时间。但是,该最大时间会受到 rabbitmq 版本、erlang vm 版本以及操作系统等因素的影响。

通常情况下,最大延迟时间不应超过 24.8 天(2147483647 毫秒),这是由于 erlang vm 定时器精度和 rabbitmq 的实现限制所导致的。对具体实现来说,还需要考虑消息数量、消息大小和队列容量等因素,以确保系统性能和可靠性。

如果需要发送长时间的延迟消息,可以使用缩短延迟时间的方法来实现,例如将一个延迟很长的消息分拆成多个延迟较短的子消息,在消息附加属性中指定分片信息,同时接收方在收到所有分片信息后再合并消息。

综上所述,建议根据实际需求和系统限制,仔细评估延迟时间的设定,并进行充分测试和优化,以确保系统的高效运行。

缺点:
延时不可靠,存在消息数量较大或使用很久后延迟不准确(会推迟), 无备份机制,延时消息存在单个节点磁盘中,不支持ram类型的节点 (数据得存磁盘里面)增加大量内存的占用 (经测试发现,发送大量延时消息后,rabbitmq内存占用明显增高,比普通消息还要高很多那种。)

延时消息时开启事务机制会事务无效

    @Bean("rabbitTransactionManager")
    public RabbitTransactionManager rabbitTransactionManager() {
        RabbitTransactionManager manager = new RabbitTransactionManager();
        ConnectionFactory factory = rabbitTemplate.getConnectionFactory();
        manager.setConnectionFactory(factory);
        return manager;
    }

RabbitTransactionManager 会导致spring事务也无效,可能多数据源的问题

安装插件需要重启
适用场景::如果不是无关紧要的小业务,不建议使用。

3.基于队列ttl+死信exchange代码实现

实现消息延迟发送的具体思路是:首先创建一个交换机来当做死信交换机,再创建一个队列与这个死信交换机进行绑定就称作死信队列。其次创建一个交换机来当做正常交换机,在创建一个队列与这个正常交换机进行绑定,同时将死信交换机和死信路由键配置到这个正常队列里面。这样,当一条带有存活时间的消息通过正常交换机发送过来时,首先进入正常队列里面,然后到了存活时间,就会通过死信交换机根据路由键发送到死信队列里面,然后消费者消费死信队列里的消息,就达到了延迟消费的目的。

java代码实现。

1.首先创建maven项目,导入pom文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2.配置配置文件(如果是yml,就用对应的书写规则)

spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/

3.配置mq的相关组件

package com.wps.cn.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author wangps
 * @date 2022年11月22日 14:44
 */

@Configuration
public class QueueConfig {
    public static final String NORMAL_QUEUE_NAME = "normal_queue_name";
    public static final String NORMAL_EXCHANGE_NAME = "normal_exchange_name";
    public static final String NORMAL_ROUTING_KEY = "normal_routing_key";
    public static final String DLX_QUEUE_NAME = "dlx_queue_name";
    public static final String DLX_EXCHANGE_NAME = "dlx_exchange_name";
    public static final String DLX_ROUTING_KEY = "dlx_routing_key";

    /**
     * 死信队列
     * @return
     */
    @Bean
    Queue dlxQueue() {
        return new Queue(DLX_QUEUE_NAME, true);
    }

    /**
     * 死信交换机
     * @return
     */
    @Bean
    DirectExchange dlxExchange() {
        return new DirectExchange(DLX_EXCHANGE_NAME);
    }

    /**
     * 绑定死信队列和死信交换机
     * @return
     */
    @Bean
    Binding dlxBinding() {
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange())
                .with(DLX_ROUTING_KEY);
    }

    /**
     * 普通消息队列
     * @return
     */
    @Bean
    Queue normalQueue() {
        Map<String, Object> args = new HashMap<>();
        //设置消息过期时间,此方法是在队列的颗粒度设置,比较局限,所以在消息上设置过期时间
//        args.put("x-message-ttl", 1000*5);
        //设置死信交换机
        args.put("x-dead-letter-exchange", DLX_EXCHANGE_NAME);
        //设置死信 routing_key
        args.put("x-dead-letter-routing-key", DLX_ROUTING_KEY);
        return new Queue(NORMAL_QUEUE_NAME, true, false, false, args);
    }

    /**
     * 普通交换机
     * @return
     */
    @Bean
    DirectExchange normalExchange() {
        return new DirectExchange(NORMAL_EXCHANGE_NAME);
    }

    /**
     * 绑定普通队列和与之对应的交换机
     * @return
     */
    @Bean
    Binding nomalBinding() {
        return BindingBuilder.bind(normalQueue())
                .to(normalExchange())
                .with(NORMAL_ROUTING_KEY);
    }
}



4.创建消费者

package com.wps.cn.consumer;

import com.rabbitmq.client.Channel;
import com.wps.cn.config.QueueConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author wangps
 * @date 2022年11月22日 14:55
 */
@Component
public class DlxConsumer {
    private static final Logger logger = LoggerFactory.getLogger(DlxConsumer.class);

    @RabbitListener(queues = QueueConfig.DLX_QUEUE_NAME)
    public void process(String order, Message message, @Headers Map<String, Object> headers, Channel channel) {
        logger.info("订单号消息",  order);

        System.out.println("执行结束...."+message);

    }
}

5.创建controller当做生产者

package com.wps.cn.controller;

import com.wps.cn.config.QueueConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * @author wangps
 * @date 2022年11月22日 15:50
 */
@RestController
@RequestMapping("/producer")
public class TestProducer {

    private static final Logger logger =  LoggerFactory.getLogger(TestProducer.class);

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @GetMapping("/sendMessage")
    public Object submit(){

        String orderId = UUID.randomUUID().toString();
        logger.info("提交订单消息========",orderId);
        rabbitTemplate.convertAndSend(
                QueueConfig.NORMAL_EXCHANGE_NAME,QueueConfig.NORMAL_ROUTING_KEY,
                orderId,
                message -> {
                    message.getMessageProperties().setExpiration(1000*5+"");
                    return message;
                });
        return "{'orderId':'"+orderId+"'}";
    }

}

6.创建启动类

package com.wps.cn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DxlRabbitmqTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(DxlRabbitmqTestApplication.class, args);
    }

}

7.运行启动类,然后在页面访问,模拟推送消息

http://localhost:8080/producer/sendMessage
观察日志可以看出,消息发出后,在5s后消费者收到消息,从而达到延迟消费的情况。
在这里插入图片描述

在这里插入图片描述

4.基于rabbitmq延时插件代码实现

环境准备

1.安装有 RabbitMQ 的服务器。

2.下载延时消息插件:rabbitmq_delayed_message_exchange
下载地址:https://www.rabbitmq.com/community-plugins.html
在这里插入图片描述

RabbitMQ延时队列插件下载页面

点击下载之后下载 rabbitmq_delayed_message_exchange-3.8.0.ez 这个文件。

3.将下载的文件复制到 RabbitMQ 的插件目录下(一般是:/opt/rabbitmq/plugins)。

4.启用插件:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

代码实现

1、导入 MAVEN 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、定义交换机、队列、路由KEY

/**
 * RabbitMQ常量
 *
 * @author ZhengNC
 * @date 2020/9/21 11:40
 */
public interface RabbitConstant {
    /**
     * 交换机
     */
    interface Exchanges{
        /**
         * 延时交换机(通过延时插件实现 rabbitmq_delayed_message_exchange)
         */
        String delayedExchange = "spring.boot.delayed.exchange";
    }

    /**
     * 队列
     */
    interface Queues{
        /**
         * 延时队列(通过延时插件实现)
         */
        String delayedQueue = "spring.boot.delayed.queue";
    }

    /**
     * 路由key
     */
    interface RouterKey{
        /**
         * 延时路由key(通过延时插件实现)
         */
        String delayedRouteKey = "delayed.route.key";
    }
}

3、配置 RABBITMQ 绑定关系

package com.qixi.mq.delay.config;

import com.qixi.mq.delay.common.constant.RabbitConstant;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * RabbitMQ配置
 *
 * @author ZhengNC
 * @date 2020/9/14 10:40
 */
@Configuration
public class RabbitConfig {

    /**
     * 延时队列(通过延时插件实现)
     *
     * @return
     */
    @Bean("delayedQueue")
    public Queue delayedQueue(){
        return new Queue(RabbitConstant.Queues.delayedQueue);
    }

      /**
     * 延时交换机Direct交换机 起名:(通过延时插件实现)
     *定义了一个x-delayed-message类型的交换机,由于Spring AMQP中没有这个类型的交换机,
     * 所以我们使用一个CustomExchange来定义这个插件构建的交换机,
     * 它和其它交换机相同,实现了AbstructExchange。
     * 唯一的区别是没有指定type类型。type类型可以自定义,
     *  这样我们就可以通过构造方法自定义交换机的类型。
     * 在使用到延迟交换机插件的时候,我们使用插件新添加了一个x-delayed-message类型的交换机。
     * @return
     */
    @Bean("delayedExchange")
    public CustomExchange delayedExchange(){
        Map<String, Object> map = new HashMap<>();
        map.put("x-delayed-type", "direct");
        return new CustomExchange(RabbitConstant.Exchanges.delayedExchange,
                "x-delayed-message", true, false, map);
    }

    /**
     * 绑定延时队列和延时交换机(延时插件实现方式)
     *
     * @param delayedQueue
     * @param delayedExchange
     * @return
     */
    @Bean
    public Binding delayedQueue_delayedExchange(
            @Qualifier("delayedQueue") Queue delayedQueue,
            @Qualifier("delayedExchange")CustomExchange delayedExchange){
        return BindingBuilder.bind(delayedQueue)
                .to(delayedExchange)
                .with(RabbitConstant.RouterKey.delayedRouteKey)
                .noargs();
    }
}

4、延时消息生产者

package com.qixi.mq.delay.producer;

import com.qixi.mq.delay.common.constant.RabbitConstant;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 延时消息生产者
 *
 * @author ZhengNC
 * @date 2020/9/21 14:15
 */
@Service
public class TTLProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送一条延时消息(延时插件的实现方式)
     *
     * @param message
     */
    public void sendDelayedMessage(String message){
        rabbitTemplate.convertAndSend(
                RabbitConstant.Exchanges.delayedExchange,
                RabbitConstant.RouterKey.delayedRouteKey,
                message,
                msg -> {
                    //设置此消息延时十秒
                    msg.getMessageProperties()
                            .setHeader("x-delay", 10000);
                    return msg;
                });
    }
}

5、延时消息的消费者

package com.qixi.mq.delay.consumer;

import com.qixi.mq.delay.common.constant.RabbitConstant;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * 延时消息的消费者
 *
 * @author ZhengNC
 * @date 2020/9/21 14:08
 */
@Component
public class TTLConsumer {
    /**
     * 消费延时消息(延时插件实现)
     *
     * @param message
     */
    @RabbitListener(queues = RabbitConstant.Queues.delayedQueue)
    public void delayedConsumer(String message){
        System.out.println("消费了一条消息,消费时间:"
                + DateTimeFormatter.ofPattern("HH:mm:ss")
                .format(LocalTime.now()));
        System.out.println(message);
    }
}

6、编写接口测试发送消息

package com.qixi.mq.delay.controller;

import com.qixi.mq.delay.common.dto.ResponseEntity;
import com.qixi.mq.delay.producer.TTLProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 *
 * @author ZhengNC
 * @date 2020/9/21 14:27
 */
@RestController
@RequestMapping("ttl")
public class TTLProducerController {

    @Autowired
    private TTLProducer producer;

    /**
     * 发送延时消息(延时插件实现方式)
     *
     * @return
     */
    @GetMapping("sendDelayedMsg")
    public ResponseEntity<String> sendDelayedMsg(){
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        StringBuilder message = new StringBuilder("这是一条延时消息,消息的发送时间为:");
        message.append(timeFormatter.format(LocalTime.now()));
        producer.sendDelayedMessage(message.toString());
        return ResponseEntity.success();
    }
}

7、测试结果

消费了一条消息,消费时间:10:00:11
这是一条延时消息,消息的发送时间为:10:00:01

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

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

相关文章

设计模式 --- 创建者模式

一、概述 创建型模式的主要关注点是“怎样创建对象&#xff1f;”&#xff0c;它的主要特点是“将对象的创建与使用分离”。 这样可以降低系统的耦合度&#xff0c;使用者不需要关注对象的创建细节。 创建型模式分为&#xff1a; 单例模式 工厂方法模式 抽象工程模式 原型…

【linux】yum “应用商店” 的基本用法

好多工具 yum软件包查看软件包安装软件卸载软件 yum 通俗的讲&#xff0c;这就似我们手机上的应用商店&#xff0c;只不过是在linux下的。 我们可以用yum来下载东西。 软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序。 但是这样…

【Linux 命令】chroot

文章目录 一、背景二、语法三、案例3.1 命令3.2 系统调用3.3 查找服务是否存在于 chrooted 监禁内 四、注意事项 chroot 用来在指定根目录运行命令&#xff08;即指定 / 的位置&#xff09;&#xff0c;不可操作指定目录之外的地方。其是一种非常简单的资源隔离化操作&#xff…

idea 配置docker 进行上传镜像,部署启动容器

前言 在我们开发测试过程中&#xff0c;需要频繁的更新docker镜像&#xff0c;然而默认情况下&#xff0c;docker的2375端口是关闭的&#xff0c;下面介绍如何打开端口。 修改docker配置文件 操作步骤&#xff1a; 1.1、修改配置 登录docker所在服务器&#xff0c;修改docker…

web前端实验5

实 验 报 告 课 程 Web前端应用开发 实验项目 Jquery AJAX编程 成 绩 专业班级 班内序号 指导教师 姓 名 学 号 实验日期 实验目的及要求&#xff1a; &#xff08;1&#xff09; 理解和掌握Jquery AJAX的get方式请求 &#xff08;2&#xff09; 理解和掌握Jquery AJAX的pos…

释放ChatGPT潜能:4款高效插件让你的AI助手更强大

你的ChatGPT页面是什么样的&#xff1f;是这样的吗&#xff1f; 今天我要向大家推荐四款「ChatGPT免费插件」。这些插件是我们在与GPT日常交流中&#xff0c;使用频率最高的四个工具。 一旦安装这些插件&#xff0c;你的GPT将立即变身为「超级ChatGPT」。使用起来更为流畅&am…

BEV+Transformer对无人驾驶硬件体系的巨大改变

摘要&#xff1a; BEVTransformer彻底终结了2D直视图CNN时代&#xff0c;BEVTransformer对智能驾驶硬件系统有着什么样的影响&#xff1f;背后的受益者又是谁&#xff1f; 图片来源&#xff1a;特斯拉 BEVTransformer是目前智能驾驶领域最火热的话题&#xff0c;没有之一&…

【大数据平台开发】【Web框架】001Django框架简介

【大数据平台开发】【Web框架】001Django框架简介 文章目录 【大数据平台开发】【Web框架】001Django框架简介一. Django简介与安装1.1 Django安装1.2 Django简介1.3 框架功能简介 二. Django框架的最小程序2.1 新建工程2.2 修改工程2.3 运行工程2.4 django-admin与manage.py2.…

(二)EalsticSearch 辅助工具 Kibana 介绍与安装

1、什么是 kibana &#xff1f; Kibana 是一个针对Elasticsearch的开源分析及可视化平台&#xff0c;用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana&#xff0c;可以通过各种图表进行高级数据分析及展示。 Kibana让海量数据更容易理解。它操作简单&#xff…

Python高光谱遥感数据处理与机器学习

Python高光谱遥感数据处理与机器学习 第一章、高光谱基础 高光谱遥感简介 什么是高光谱遥感&#xff1f; 高光谱遥感为什么重要&#xff1f; 高光谱遥感与其他遥感技术的区别是什么&#xff1f; 高光谱遥感的历史和发展 高光谱传感器与数据获取 高光谱传感器类型 如何获…

RHCE第五次作业

目录 一、判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间 1.创建脚本test1.sh 2.下载邮件服务并执行 3.测试 4.做计划 二、 判断web服务是否运行&#xff08;1、查看进程的方式判断该程序…

Node内置模块 【操作系统os模块】

文章目录 &#x1f31f;前言&#x1f31f;os模块&#x1f31f;使用&#x1f31f;属性&#x1f31f;方法&#x1f31f;获取操作系统临时目录&#x1f31f;获取操作系统主机名&#x1f31f;获取操作系统CPU架构&#x1f31f;识别操作系统平台&#x1f31f;获取操作系统发行版本&a…

MPLS VPN 实验

目录 MPLS VPN 实验 拓扑图 实验目的 基础配置 R2 R3 R4 公网部分配置IGP&#xff08;ospf&#xff09; R2 R3 R4 激活MPLS R2 R3 R4 VRF创建 R2 R4 将接口画入VRF空间 R2 R4 配置接口IP地址 R2 R4 站点1基本配置 站点2基本配置 通过静态路由在CE和…

【SPSS】两独立样本的极端反应检验和两配对样本的非参数检验详细操作教程(附案例实战)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

链式二叉树的查找,遍历(递归实现)等接口的实现

目录 前言: 一&#xff1a;二叉树的建立 (1)本文采用的二叉树表示方法 (2)手动建立一颗二叉树 二&#xff1a;二叉树的遍历 (1)二叉树的三种遍历方式 (2)分治思想 (3)前序遍历 (4)中序遍历 (5)后序遍历 三&#xff1a;求二叉树的节点和高度(深度) (1)求二叉树节点 ①…

python+vue 餐饮食品安全监管投诉平台

系统主要包括个人中心、用户管理、餐饮类型管理、餐饮企业管理、案例类型管理、案例展示管理、法规分类管理、法律法规管理、在线投诉管理、查处信息管理、系统管理等功能模块。 绪论 网站的开发背景&#xff0c;意义和系统状况等&#xff0c;详细讲述了系统的用处&#xff0c;…

基于构效关系模型的药物设计(QSAR)

基于构效关系模型的药物设计&#xff08;QSAR&#xff09; 定量构效关系&#xff08;QSAR&#xff0c;Quantitative Structure-Activity Relationship&#xff09;分析是指利用理论计算和统计分析工具来研究系列化合物结构&#xff08;包括二维分子结构、三维分子结构和电子结…

Latex数学公式排版

文章目录 Latex使用最佳方式&#xff1a;读官方文档Latex中的字符数学公式排版1.引入宏包:2.公式排版基础3.数学符号(1).希腊字母(2).指数,上下标,导数(3).分式和根式(4).关系符(5).算符(6).巨算符(7).箭头 Latex使用 最佳方式&#xff1a;读官方文档 The not so short intro…

案例分析:真实案例对“引用类型”的思考

在一个风和日丽的早上&#xff0c;我刚坐到工位上&#xff0c;正准备美美的享受早餐时&#xff0c;我的测试小兄弟杨过火急火燎的来找我说&#xff1a;“小米&#xff0c;不好了&#xff0c;运营童鞋反馈&#xff0c;咱们商城小程序金刚区的新店专区&#xff0c;新开的店铺无法…

mac上 qt与mysql的连接问题

经过两天的折磨和挣扎&#xff0c;将Mac上QT与mysql数据库链接问题的解决做一个梳理&#xff0c;以防忘记&#xff0c;并供他人借鉴&#xff1b; 环境版本&#xff1a;Mac10.14&#xff0c;MySQL5.7.24&#xff0c;Qt5.14.2 首先我重新下载安装了Qt&#xff08;安装了源码&am…