2.9日学习打卡----初学RabbitMQ(四)

news2024/11/18 17:35:37

2.9日学习打卡

一.RabbitMQ 死信队列

在这里插入图片描述
在MQ中,当消息成为死信(Dead message)后,消息中间件可以将其从当前队列发送到另一个队列中,这个队列就是死信队列。而在RabbitMQ中,由于有交换机的概念,实际是将死信发送给了死信交换机(Dead Letter Exchange,简称DLX)。死信交换机和死信队列和普通的没有区别。
在这里插入图片描述
消息成为死信的情况

  1. 队列消息长度到达限制。
  2. 消费者拒签消息,并且不把消息重新放入原队列。
  3. 消息到达存活时间未被消费。

在这里插入图片描述
死信队列
死信交换机和普通交换机,只是名字不同,其余的都一样;就是说死信交换机绑定死信队列,普通交换机绑定普通队列。

并且普通队列需要在创建的同时绑定死信交换机和死信队列的路由关键字;就是说当普通队列的消息达到死信的条件时,会通过绑定的死信交换机根据绑定的死信队列路由关键字发送该普通队列的消息,这个就是成为死信的过程。

除了拒签成为死信的需要监听普通队列,其余俩个不需要到达消费者端就会成为死信。
死信会有专门的死信消费者去监听。

创建死信队列

package com.jjy.rabbitproducer;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitmqConfig4 {
    private final String DEAD_EXCHANGE = "dead_exchange";
    private final String DEAD_QUEUE = "dead_queue";

    private final String NORMAL_EXCHANGE = "normal_exchange";
    private final String NORMAL_QUEUE = "normal_queue";
    // 死信交换机
    @Bean(DEAD_EXCHANGE)
    public Exchange deadExchange(){
        return ExchangeBuilder
                .topicExchange(DEAD_EXCHANGE)
                .durable(true)
                .build();
    }

    // 死信队列
    @Bean(DEAD_QUEUE)
    public Queue deadQueue(){
        return QueueBuilder
                .durable(DEAD_QUEUE)
                .build();
    }

    // 死信交换机绑定死信队列
    @Bean
    public Binding bindDeadQueue(@Qualifier(DEAD_EXCHANGE) Exchange exchange, @Qualifier(DEAD_QUEUE)Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("dead_routing")
                .noargs();
    }

    // 普通交换机
    @Bean(NORMAL_EXCHANGE)
    public Exchange normalExchange(){
        return ExchangeBuilder
                .topicExchange(NORMAL_EXCHANGE)
                .durable(true)
                .build();
    }

    // 普通队列
    @Bean(NORMAL_QUEUE)
    public Queue normalQueue(){
        return QueueBuilder
                .durable(NORMAL_QUEUE)
                .deadLetterExchange(DEAD_EXCHANGE) // 绑定死信交换机
                .deadLetterRoutingKey("dead_routing") // 死信队列路由关键字
                .ttl(10000) // 消息存活10s
                .maxLength(10) // 队列最大长度为10
                .build();
    }

    // 普通交换机绑定普通队列
    @Bean
    public Binding bindNormalQueue(@Qualifier(NORMAL_EXCHANGE) Exchange exchange,@Qualifier(NORMAL_QUEUE)Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("my_routing")
                .noargs();
    }
}

测试死信队列

生产者发送消息

 @Test
    public void TestDlx(){
        // 存活时间过期后变成死信
          rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
        // 超过队列长度后变成死信
            for (int i = 0; i < 20; i++) {
               rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
            }
        // 消息拒签但不返回原队列后变成死信
        rabbitTemplate.convertAndSend("normal_exchange","my_routing","测试死信");
    }

消费者拒收消息

package com.jjy.rabbitconsumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class DlxConsumer {
    @RabbitListener(queues = "normal_queue")
    public void listenMessage(Message message, Channel channel) throws IOException {
        // 拒签消息
        channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,false);
    }
}

二. RabbitMQ 延迟队列

在这里插入图片描述
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。

例如:用户下单后,30分钟后查询订单状态,未支付则会取消订单
在这里插入图片描述
但RabbitMQ中并未提供延迟队列功能,我们可以使用死信队列实现延迟队列的效果。
在这里插入图片描述

延迟队列_死信队列实现

在这里插入图片描述
编写配置文件


spring:
  rabbitmq:
    host: 192.168.66.100
    port: 5672
    username: jjy
    password: jjy
    virtual-host: /

# 日志格式
logging:
  pattern:
    console: '%d{HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'

创建队列和交换机

package com.jjy.yanchirabbitmqdemo;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class RabbitmqConfig {
    // 订单交换机和队列
    private final String ORDER_EXCHANGE = "order_exchange";
    private final String ORDER_QUEUE = "order_queue";
    // 过期订单交换机和队列
    private final String EXPIRE_EXCHANGE = "expire_exchange";
    private final String EXPIRE_QUEUE = "expire_queue";
    // 过期订单交换机
    @Bean(EXPIRE_EXCHANGE)
    public Exchange deadExchange(){
        return ExchangeBuilder
                .topicExchange(EXPIRE_EXCHANGE)
                .durable(true)
                .build();
    }
    // 过期订单队列
    @Bean(EXPIRE_QUEUE)
    public Queue deadQueue(){
        return QueueBuilder
                .durable(EXPIRE_QUEUE)
                .build();
    }
    // 将过期订单队列绑定到交换机
    @Bean
    public Binding bindDeadQueue(@Qualifier(EXPIRE_EXCHANGE) Exchange exchange,@Qualifier(EXPIRE_QUEUE) Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("expire_routing")
                .noargs();
    }

    // 订单交换机
    @Bean(ORDER_EXCHANGE)
    public Exchange normalExchange(){
        return ExchangeBuilder
                .topicExchange(ORDER_EXCHANGE)
                .durable(true)
                .build();
    }
    // 订单队列
    @Bean(ORDER_QUEUE)
    public Queue normalQueue(){
        return QueueBuilder
                .durable(ORDER_QUEUE)
                .ttl(10000) // 存活时间为10s,模拟30min
                .deadLetterExchange(EXPIRE_EXCHANGE) // 绑定死信交换机
                .deadLetterRoutingKey("expire_routing") // 死信交换机的路由关键字
                .build();
    }
    // 将订单队列绑定到交换机
    @Bean
    public Binding bindNormalQueue(@Qualifier(ORDER_EXCHANGE) Exchange exchange,@Qualifier(ORDER_QUEUE) Queue queue){
        return BindingBuilder
                .bind(queue)
                .to(exchange)
                .with("order_routing")
                .noargs();
    }
}

编写下单的控制器方法,下单后向订单交换机发送消息

@RestController
public class OrderController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/place/{orderId}")
    public String placeOrder(@PathVariable String orderId){
        System.out.println("处理订单数据");
        //将订单id发送到订单队列
        rabbitTemplate.convertAndSend("order_exchange","order_routing",orderId);
        return "下单成功,修改库存";
    }
    
}

编写监听死信队列的消费者

package com.jjy.yanchirabbitmqdemo.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class ExpireOrderConsumer {
   //监听过期订单队列
   @RabbitListener(queues = "expire_queue")
   public void listenMessage(String orderId){
       System.out.println("查询"+orderId+"订单状态,如果已支付无需处理,未支付,回退库存");
   }
   @RabbitListener(queues = "delayed_queue")
   public void listenMessage2(String orderId){
       System.out.println("查询"+orderId+"号订单的状态,如果已支付则无需处理,如果未支付则需要回退库存");
   }

}

RabbitMQ延迟队列_插件实现

在这里插入图片描述
在使用死信队列实现延迟队列时,会遇到一个问题:RabbitMQ只会移除队列顶端的过期消息,如果第一个消息的存活时长较长,而第二个消息的存活时长较短,则第二个消息并不会及时执行。
在这里插入图片描述
RabbitMQ虽然本身不能使用延迟队列,但官方提供了延迟队列插
件,安装后可直接使用延迟队列。
在这里插入图片描述
安装延迟队列插件

  1. 使用rz将rabbitmq_delayed_message_exchange-3.10.2.ez 插件上传到虚拟机
  2. 安装插件
# 将插件放入RabbitMQ插件目录中
mv rabbitmq_delayed_message_exchange-
3.9.0.ez /usr/local/rabbitmq/plugins/
# 启用插件
rabbitmq-plugins enable
rabbitmq_delayed_message_exchange
  1. 重启RabbitMQ服务
#停止rabbitmq
rabbitmqctl stop
#启动rabbitmq
rabbitmq-server restart -detached

此时登录管控台可以看到交换机类型多了延迟消息
管控台地址(https://自己地址:15672)默认账号和密码都是 guest
在这里插入图片描述
此时交换机是x-delayed-message类型的;延迟队列就是说发送延迟交换机及其绑定延迟队列的路由关键字和MessagePostProcessor对象,MessagePostProcessor对象是用来设置延迟时长等属性的。

延迟队列只是创建延迟交换机时跟普通不同,其他基本相同。

执行流程就是浏览器通过url映射到Controller的@RequestMapping注解的value中,然后执行方法体,执行到发送MQ,然后结束方法体;MQ此时延迟方法体中设置的时长,再发送给消费端,消费端监听到再处理延迟队列的MQ。
使用延迟队列

package com.jjy.yanchirabbitmqdemo;

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;

@Configuration
public class RabbitmqConfig2 {
    public final String DELAYED_EXCHANGE = "delayed_exchange";
    public final String DELAYED_QUEUE = "delayed_queue";

    //1.延迟交换机
    @Bean(DELAYED_EXCHANGE)
    public Exchange delayedExchange() {
        // 创建自定义交换机
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "topic"); // topic类型的延迟交换机
        return new CustomExchange(DELAYED_EXCHANGE, "x-delayed-message", true, false, args);
    }

    //2.延迟队列
    @Bean(DELAYED_QUEUE)
    public Queue delayedQueue() {
        return QueueBuilder
                .durable(DELAYED_QUEUE)
                .build();
    }

    // 3.绑定
    @Bean
    public Binding bindingDelayedQueue(@Qualifier(DELAYED_QUEUE) Queue queue, @Qualifier(DELAYED_EXCHANGE) Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("order_routing").noargs();
    }
}

编写下单的控制器方法

package com.jjy.yanchirabbitmqdemo.controller;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @GetMapping("/place2/{orderId}")
    public String placeOrder2(@PathVariable String orderId) {
        System.out.println("处理订单数据...");


        // 设置消息延迟时间为10秒
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setDelay(10000);
                return message;
            }
        };
        // 将订单id发送到订单队列
        rabbitTemplate.convertAndSend("delayed_exchange", "order_routing", orderId, messagePostProcessor);
        return "下单成功,修改库存";
    }

}

编写消费者

package com.jjy.yanchirabbitmqdemo.consumer;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class ExpireOrderConsumer {
    //监听过期订单队列
    @RabbitListener(queues = "expire_queue")
    public void listenMessage(String orderId){
        System.out.println("查询"+orderId+"订单状态,如果已支付无需处理,未支付,回退库存");
    }
    @RabbitListener(queues = "delayed_queue")
    public void listenMessage2(String orderId){
        System.out.println("查询"+orderId+"号订单的状态,如果已支付则无需处理,如果未支付则需要回退库存");
    }

}

三. RabbitMQ集群

在这里插入图片描述
在生产环境中,当单台RabbitMQ服务器无法满足消息的吞吐量及
安全性要求时,需要搭建RabbitMQ集群。

搭建RabbitMQ集群

设置两个RabbitMQ服务

# 关闭RabbitMQ服务
rabbitmqctl stop
# 设置服务一
RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit1 rabbitmq-server
start -detached
# 设置服务二
RABBITMQ_NODE_PORT=5674 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener
[{port,15674}]" RABBITMQ_NODENAME=rabbit2 rabbitmq-server start -detached

将两个服务设置到同一集群中

# 关闭服务2
rabbitmqctl -n rabbit2 stop_app
# 重新设置服务2
rabbitmqctl -n rabbit2 reset
# 将服务2加入服务1中
rabbitmqctl -n rabbit2 join_cluster rabbit1@localhost(自己的主机名)
# 启动服务2
rabbitmqctl -n rabbit2 start_app

这个主机名可以到管控台看
在这里插入图片描述
@后面的就是主机名

镜像队列

在这里插入图片描述

搭建了集群后,虽然多个节点可以互相通信,但队列只保存在了一个节点中,如果该节点故障,则整个集群都将丢失消息。

# 关闭服务1
rabbitmqctl -n rabbit1 stop_app

在这里插入图片描述

此时我们需要引入镜像队列机制,它可以将队列消息复制到集群中的其他节点上。如果一个节点失效了,另一个节点上的镜像可以保证服务的可用性。
在这里插入图片描述

在管此时某个节点故障则不会影响整个集群。控台点击Admin—>Policies设置镜像队列
在这里插入图片描述

Name为镜像策略的名字;Pattern(模式)的值为^,即应用于所有交换机和队列, ^xxx则是应用到以xxx开头交换机和队列。
ha-mode=all则是镜像模式:所有,即将队列镜像到集群的所有服务器中。
ha-sync-mode: automatic设置为自动同步,即当某一服务器宕机,而有新的消息发送到来,当该服务器再次启动后,它已经拿不到该消息了,但是通过设置该参数可以自动同步消息。

负载均衡

在这里插入图片描述

无论是生产者还是消费者,只能连接一个RabbitMQ节点,而在我们使用RabbitMQ集群时,如果只连接一个RabbitMQ节点,会造成该节点的压力过大。我们需要平均的向每个RabbitMQ节点发送请求,此时需要一个负载均衡工具帮助我们分发请求,接下来使用Haproxy做负载均衡:
安装Haproxyyum -y install haproxy
配置Haproxyvim /etc/haproxy/haproxy.cfg
添加下面内容

# 以下为修改内容
defaults
        # 修改为tcp
        mode tcp


# 以下为添加内容
listen rabbitmq_cluster
        # 对外暴露端口
     bind 0.0.0.0:5672
     mode tcp
     balance roundrobin
     # 代理RabbitMQ的端口
     server node1 127.0.0.1:5673 check inter 5000 rise 2 fall 2
     server node2 127.0.0.1:5674 check inter 5000 rise 2 fall 2


listen stats
        # Haproxy控制台路径
     bind 自己地址:8100
     mode http
     option httplog
     stats enable
     stats uri /rabbitmq-stats
     stats refresh 5s

访问Haproxy控制台:http://自己地址/rabbitmq-stats
在这里插入图片描述

生产者连接Haproxy发送消息

// 生产者
public class Producer {
  public static void main(String[] args) throws IOException, TimeoutException {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("192.168.66.100");
    connectionFactory.setPort(5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");


    Connection conn = connectionFactory.newConnection();
    Channel channel = conn.createChannel();
    channel.queueDeclare("simple_queue", false, false, false, null);


    channel.basicPublish("", "simple_queue", null, "hello!rabbitmq!".getBytes());


    channel.close();
    conn.close();
   }
}

总结

  1. 由Springboot整合RabbitMQ实现通配符模式,通配符模式使用topic交换机,展示消息从生产者到消费者的一个过程。
    生产者由Config配置类+yml配置文件还有发送消息的一个调用操作组成;而消费者由yml配置文件和多个监听指定队列方法的类组成。
    RabbitMQ的Config配置类用来创建交换机、创建队列和将队列绑定到交换机;此时yml配置文件的内容是用来连接RabbitMQ的信息和一些模式的开关。
    模式有确认模式(Confirm),回退模式(Return),消费者消息确认(Consumer Ack) 三种;前俩种用在生产者,第三种用在消费者。
  • 确认模式是用以消息发送到交换机,此时不论成功或失败都会调用setConfirmCallback返回发送的结果(成功或失败),在失败时打印结果并处理失败的业务,成功时处理成功后的业务。
  • 回退模式则是消息从交换机发到队列的过程,当失败时调用setReturnsCallback方法,输出失败的原因等信息和失败后的处理操作。
  • 消费者消息确认可以设置手动和自动确认,自动确认因消费者监听方法内出现问题或者其他原因会导致消息丢失,所以一般是手动确认,在正常处理后确认签收,在出现异常时捕获异常,处理异常并拒签。
    整个过程总而言之,即从生产者发送消息到指定交换机,交换机再根据生产者给的路由键发送到指定队列,队列再发送到消费者。
  1. 在RabbitMQ的高级属性中,有消费端限流、公平与不公平分发、消息存活时间、优先级队列、死信队列、延迟队列。
  • 消费端限流就是说从队列到消费者的消息,消费者最多只能存在x条未处理的消息。
  • 公平分发就是每个消费者都得到平均分发的消息,一般不设置限流,不公平分发则是处理快的继续处理,没有数额限制,但是需要设置限流且值为1。
  • 消息存活时间可以设置队列消息的存活时间时需要在配置类在创建队列时指定,也可以设置单条消息的存活时间,但是需要在发送消息时创建并添加MessageProperties对象设置存活时间作为参数,俩这都是存活时间一过,消息等到队列顶端后移除。
  • 优先级队列,即需要在创建队列时指定队列的最大优先级,然后在发送消息时创建并添加MessageProperties对象设置优先级数作为参数。
  • 死信队列,即分别创建俩个交换机和俩个队列并将队列绑定到交换机,一个作为死信队列和交换机,一个作为普通队列和交换机,但是普通队列创建时需要绑定死信交换机和死信队列的路由键,并且当普通队列消息过期、拒收并且不重回普通队列、超过普通队列设置的最大消息长度时,将消息发送到死信交换机,死信交换机再发到死信队列,死信队列有专门的消费者进行监听。
  • 延迟队列有2种实现,一种是通过死信队列,设置普通队列的存活时间为延迟的时长,通过死信消费者处理延迟的消息;第二种是通过rabbitmq的延迟队列插件实现延迟队列。

3. RabbitMQ搭建集群及其实现镜像队列、负载均衡

  • 搭建集群就是多个独立的服务器在一个系统中组成一个群体;用以提高系统的可用性、处理能力、响应能力,防止宕机出现数据丢失,和解决服务器无法承受高并发的等问题。
  • 镜像队列就是复制主节点的数据到其他的从节点中,防止因为主节点宕机导致数据丢失。
  • 负载均衡就是将请求访问平均分发到集群的每个节点中,从而提高系统的性能,不会导致某个节点因为访问量过大从而出现系统性能降低或者该节点宕机。

关于RabbitMQ的学习就告一段落了 ,下次就是学习第二个消息中间件RocketMQ

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!
在这里插入图片描述

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

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

相关文章

Linux运行级别 | 管理Linux服务

Linux运行级别 级别&#xff1a; 0关机1单用户2多用户但是不运行nfs网路文件系统3默认的运行级别&#xff0c;给一个黑的屏幕&#xff0c;只能敲命令4未使用5默认的运行级别&#xff0c;图形界面6重启切换运行级别&#xff1a; init x管理Linux服务 systemctl命令&#xf…

springboot169基于vue的工厂车间管理系统的设计

基于VUE的工厂车间管理系统设计与实现 摘 要 社会发展日新月异&#xff0c;用计算机应用实现数据管理功能已经算是很完善的了&#xff0c;但是随着移动互联网的到来&#xff0c;处理信息不再受制于地理位置的限制&#xff0c;处理信息及时高效&#xff0c;备受人们的喜爱。本…

微信小程序(四十)API的封装与调用

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.在单独的js文件中写js接口 2.以注册为全局wx的方式调用接口 源码&#xff1a; utils/testAPI.js const testAPI{/*** * param {*} title */simpleToast(title提示){//可传参&#xff0c;默认为‘提示’wx.sho…

新春快乐(烟花、春联)【附源码】

新春快乐 一&#xff1a; C语言 -- 烟花二&#xff1a;Python -- 春联三&#xff1a;Python -- 烟花四&#xff1a;HTML -- 烟花 一&#xff1a; C语言 – 烟花 运行效果&#xff1a; #include <graphics.h> #include <math.h> #include <time.h> #include…

BUUCTF LKWA

1.访问页面。 2.选择 Variables variable 关卡 3.获得flag http://357dab81-78b8-4d74-976a-4a69dd894542.node5.buuoj.cn:81/variables/variable.php?funcpassthru&inputcat%2Fflagflag{0020ced6-8166-4fa5-87a7-7d93ee687c3e}

跟着pink老师前端入门教程-day23

苏宁网首页案例制作 设置视口标签以及引入初始化样式 <meta name"viewport" content"widthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0"> <link rel"stylesheet" href"css/normaliz…

Days 26 ElfBoard RGB接口

1. RGB接口介绍 ELF 1支持并行24位LCD,最大RGB888 24位&#xff0c;最高分辨率支持WXGA&#xff08;1366x768&#xff09;&#xff0c;刷新频率为60Hz&#xff0c;底板采用的是RGB565的连接方式&#xff0c;通过一个0.5mm间距54P的FPC座&#xff08;P9&#xff09;引出&#x…

用python写个类

对类的简单概述 面向过程编程,是按照预定的顺序一步步执行,面向对象编程则是将相似的对象归为一类,定义它们的属性和行为,例如人有姓名,性别,年龄等属性,有吃饭,睡觉,学习等行为.这便是类,类是对象的模版,对象是类的实例,例如人是一个抽象的类,而张三是一个具体的对象. 学会用…

Spring Boot3统一结果封装

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 前置条件 封装目的 常用格式 定义返回结果枚举类 定义返回结果封装类 对返回结果封装 测试封装 前置条件 已…

黄金交易策略(Nerve Knife):反趋势锁定单的处理机制

锁定单是由大趋势反转之后原来的趋势单转变而来的&#xff0c;会在趋势再次反转时变为趋势单或者转入保留单&#xff0c;也有很大概率在趋势识别到转变之前就被减仓减掉了。 完整EA&#xff1a;Nerve Knife.ex4黄金交易策略_黄金趋势ea-CSDN博客 一、锁定单怎么样来的&#xf…

旅游|基于Springboot的旅游管理系统设计与实现(源码+数据库+文档)

旅游管理系统目录 目录 基于Springboot的旅游管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户管理 2、景点分类管理 3、景点信息管理 4、酒店信息管理 5、景点信息 6、游记分享管理 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xf…

MySQL-视图(VIEW)

文章目录 1. 什么是视图&#xff1f;2. 视图 VS 数据表3. 视图的优点4. 视图相关语法4.1 创建视图4.2 查看视图4.3 修改视图4.4 删除视图4.5 检查选项 5. 案例6. 注意事项 1. 什么是视图&#xff1f; MySQL 视图&#xff08; View&#xff09;是一种虚拟存在的表&#xff0c;同…

Python实现计数排序

对于如果存在使用两个数组成员进行比较的操作&#xff0c;该方法会导致排序算法的时间复杂度会存在一个下界问题&#xff0c;该问题在使用计数排序的时候可以突破这个理论下界&#xff0c;也即是不适用元素比对的操作的算法之一&#xff0c;计数排序。 添加图片注释&#xff0c…

关于数字图像处理考试

我们学校这门科目是半学期就完结哦&#xff0c;同学们学习的时候要注意时间哦。 选择题不用管&#xff0c;到时候会有各种版本的复习资料的。 以下这些东西可能会是大题的重点&#xff1a; 我根据平时代码总结的&#xff0c;供参考 基本操作&#xff1a; 1.读图&#xff1a;…

【数据结构与算法】【腾讯阿里链表面试题】算法题--链表易懂版讲解

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

【正式】今年第一篇CSDN(纯技术教学)

一、文件上传简介 文件上传漏洞是指用户上传了一个可执行的脚本文件&#xff08;木马、病毒、恶意脚本、webshell等&#xff09;&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力。上传点一般出现在头像、导入数据、上传压缩包等地方&#xff0c;由于程序对用户上传…

CSP-202009-1-称检测点查询

CSP-202009-1-称检测点查询 解题思路 本题的时间复杂度貌似没有限制&#xff0c;直接暴力枚举就能知识盲点&#xff1a;sort()函数-升序排序 #include <algorithm>给名为dis&#xff0c;长度为n的数组排序sort(new_dis, new_dis n); #include <iostream> #inc…

三、OpenAI所有模型介绍

1. 综述 OpenAI API开发了具有各种能力的模型。可以根据不同的需求选择不同的模型并进行精调。 模型描述GPT-4、GPT-4 Turbo一组从GPT-3.5升级后的模型&#xff0c;能够生成自然语言和代码GPT-3.5 Turbo一组从GPT-3.5升级后的模型&#xff0c;能够生成自然语言和代码DALL.E能…

搭建yum仓库服务器

安装 1.安装linux 1.1安装依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel 1.2下载 cd /opt/nginx wget http://nginx.org/download/nginx-1.25.3.tar.gz 1.3解压 tar -xvf nginx-1.25.3.tar.gz 1.4配置 cd nginx-1.25.3 ./configure --pre…

python高校实验室管理系统的django设计与实现81txp

技术栈 后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django Python版本&#xff1a;python3.7 数据库&#xff1a;mysql5.7 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm .本高校实验室管理系统采用python语言、MySQL数据库&…