谷粒商城のRabbitMQ基础篇

news2024/11/24 1:31:57

文章目录

  • 前言
  • 一、Rabbit MQ简介
    • 1、基本概念
    • 2、组件架构
  • 二、使用步骤
    • 1.引入依赖
    • 2.application.properties
    • 3、docker 安装Rabbit MQ
    • 3、使用案例
      • 3.1、定义队列
      • 3.2、定义交换机
      • 3.3、绑定
      • 3.4、发送消息
      • 3.5、接受消息
      • 3.5、自定义消息序列化方式
      • 3.6、演示Fanout 交换机模式
      • 3.7、演示Topic 交换机模式
  • 三、消息可靠性
    • 3.1、ConfirmCallback&ReturnCallback
    • 3.2、设置手动ACK
      • 3.2.1、演示自动签收消息丢失
      • 3.2.2、演示手动签收
  • 总结


前言

  本篇主要介绍消息中间件Rabbit MQ的基本概念及使用,以及保证消息的可靠投递
  对应视频p248-p261


一、Rabbit MQ简介

1、基本概念

  Rabbit MQ是一个消息代理 - 一个消息系统的媒介。它可以为你的应用提供一个通用的消息发送和接收平台,并且保证消息在传输过程中的安全。这是官方文档给予Rabbit MQ的定义。
  Rabbit MQ是基于AMQP 模型,AMQP(高级消息队列协议)是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。和JMS(Java Message Service)JAVA消息服务最大的区别在于,AMQP具有跨平台,跨语言的特性,并且支持更多的消息模型,并且简化了消息的类型(网络通信统一转换为流)。
  在企业级开发中运用Rabbit MQ,主要是为了达到异步削峰解耦的目的:

  • 异步:假设用户注册完成后需要发送短信和邮件,如果是同步模式,就需要注册->发送短信->发送邮件顺序执行。假设某一个步骤调用第三方的接口执行的时间较长,会极大地拖延整个业务流程的完成时间,需要等到最后一步执行完成后再返回结果,这样对于用户是非常不友好的。而引入Rabbit MQ,可以做到注册完成后直接返回结果给用户,异步通知其他的服务发送短信,发送邮件。
  • 削峰:对于一些电商平台,在双11,618等活动时,用户请求会达到峰值,服务器一般短时间内无法承受这样的负担,通过Rabbit MQ,可以将用户的请求暂时存放入队列中,然后负责业务处理的模块按照一定的规则从队列中获取请求进行处理,也就是起到一个缓冲的作用。
  • 解耦:假设现在有三个系统,B,C系统需要接受A系统的通知,如果后续又加入了一个D系统,那么A系统还需要编写针对D系统发送通知的接口。引入Rabbit MQ,A系统可以将通知发送到队列,需要接受消息的系统自行监听队列即可。

2、组件架构

  Rabbit MQ的基本架构,包括生产者消费者消息交换机队列通道虚拟主机

  • 生产者 是消息的生产方,负责编辑消息发送到交换机。
  • 消费者 是消息的消费方,负责监听队列,从队列中获取消息并处理。
  • 交换机 作为消息和队列的中间组件,消息需要经过交换机,而不是直接发送至队列。
  • 队列 用来保存消息直到发送给消费者。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
  • 通道信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。可以复用一条TCP连接。虽然客户端和 RabbitMQ 服务器之间只有一个 TCP 连接,但可以通过多个信道并行处理不同的消息通信任务。每个信道在逻辑上是独立的,它们互不干扰。在 RabbitMQ 中,客户端首先与服务器建立一个 TCP 连接,然后通过该连接创建多个信道来进行通信。
  • 虚拟主机 上述的交换机,队列,通道都是运行在虚拟主机上的。可以将虚拟主机理解成一个命名空间,允许你在同一个 RabbitMQ 实例中分隔不同的应用或项目。不同的队列、交换机等资源可以在不同的虚拟主机中拥有相同的名称,而不会产生冲突。并且每个虚拟主机都有一套独立的权限管理。

二、使用步骤

1.引入依赖

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

2.application.properties

spring.rabbitmq.host=自己的地址
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/

  还需要在启动类上加入@EnableRabbit注解

3、docker 安装Rabbit MQ

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p
25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

3、使用案例

  同样的Rabbit MQ提供了两个模版:AmqpAdminRabbitTemplateAmqpAdmin 可用于队列,交换机,绑定关系的定义。RabbitTemplate可以用于发送消息。

3.1、定义队列

	 /**
     * 创建队列
     */
    @Test
    public void createQueue() {
    		 //参数二控制是否持久化
        amqpAdmin.declareQueue(new Queue("hello-java-queue", true, false, false,null));
        System.out.println("queue created");
    }

3.2、定义交换机

  常见的交换机有:

  • Direct 交换机 根据精确匹配的路由键来将消息路由到特定队列。
  • Fanout 交换机 将消息广播到所有绑定的队列,无需考虑路由键。
  • Topic 交换机 根据模式匹配的路由键,将消息路由到匹配的队列。
  • Headers 交换机 根据消息头中的属性值匹配,而非路由键。

  前三种模式最为常用,Topic 交换机模式匹配 也称为通配符模式,一般有两种,#匹配0个或多个单词,*匹配一
个单词。

	 /**
     * 创建交换机(直连模式)
     */
    @Test
    public void createExchange() {
    	    //参数二控制是否持久化
        amqpAdmin.declareExchange(new DirectExchange("hello-java-exchange", true, false,null));
        System.out.println("exchange created");
    }

3.3、绑定

 	 /**
     * 将队列绑定到交换机上
     */
    @Test
    public void createBinding() {
    		//参数一 队列名 参数二:绑定类型 参数三:交换机名 参数四:路由键
        amqpAdmin.declareBinding(new Binding("hello-java-queue",
                Binding.DestinationType.QUEUE,
                "hello-java-exchange",
                "hello.java",
                null));
        System.out.println("binding created");
    }

3.4、发送消息

  发送的消息通过路由键找到对应的交换机。

    /**
     * 发送消息
     */
    @Test
    public void sendMessage() {
    			//参数一:交换机名 参数二:路由键
        rabbitTemplate.convertAndSend("hello-java-exchange","hello.java","这是一个测试信息");
    }

3.5、接受消息

  需要在方法上加入@RabbitListener注解,指定监听的队列。

	 @RabbitListener(queues = "hello-java-queue")
    public void listenMessage(Message message, Channel channel) {

	 }

3.5、自定义消息序列化方式

@Configuration
public class MyRabbitConfig {

 	  @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

}

3.6、演示Fanout 交换机模式

    /**
     * 创建交换机(广播模式)
     */
    @Test
    public void createFanoutExchange() {
        amqpAdmin.declareExchange(new FanoutExchange("hello-java-fanout-exchange", true, false,null));
    }
    
	  @Test
    public void createQueue2() {
        amqpAdmin.declareQueue(new Queue("hello-java-fanout-queue", true, false, false,null));
    }

    /**
     * 将队列绑定到交换机上
     */
    @Test
    public void createBindingFanout() {
        amqpAdmin.declareBinding(new Binding("hello-java-queue",
                Binding.DestinationType.QUEUE,
                "hello-java-fanout-exchange",
                "hello.java",
                null));
        amqpAdmin.declareBinding(new Binding("hello-java-fanout-queue",
                Binding.DestinationType.QUEUE,
                "hello-java-fanout-exchange",
                "hello.java",
                null));
        System.out.println("binding created");
    }
    
    /**
     * 发送消息
     */
    @Test
    public void sendMessage() {
        rabbitTemplate.convertAndSend("hello-java-fanout-exchange","hello.js","这是一个测试信息");
    }
    
    @RabbitListener(queues = {"hello-java-queue","hello-java-fanout-queue"})
    public void receiveQueue(String message) {
        System.out.println(message);
    }

  广播模式下,在发送消息时,即使指定的路由键和队列绑定在交换机上的不同,监听相关队列的消费者也可以接收到消息。即:fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。
在这里插入图片描述

3.7、演示Topic 交换机模式

    /**
     * 创建交换机(通配符模式)
     */
    @Test
    public void createExchangeTopic() {
        amqpAdmin.declareExchange(new TopicExchange("hello-java-topic-exchange", true, false,null));
        System.out.println("exchange created");
    }

    /**
     * 创建通配符队列1
     */
    @Test
    public void createTopicQueue1() {
        amqpAdmin.declareQueue(new Queue("hello-java-topic-queue1", true, false, false,null));
    }

    /**
     * 创建通配符队列2
     */
    @Test
    public void createTopicQueue2() {
        amqpAdmin.declareQueue(new Queue("hello-java-topic-queue2", true, false, false,null));
    }

    /**
     * 将队列绑定到交换机上(通配符模式)
     */
    @Test
    public void createBindingTopic() {
        amqpAdmin.declareBinding(new Binding("hello-java-topic-queue1",
                Binding.DestinationType.QUEUE,
                "hello-java-topic-exchange",
                "#.java",
                null));
        amqpAdmin.declareBinding(new Binding("hello-java-topic-queue2",
                Binding.DestinationType.QUEUE,
                "hello-java-topic-exchange",
                "*.java",
                null));
        System.out.println("binding created");
    }
    
    @GetMapping("/send")
    public void sendMessage() {
        rabbitTemplate.convertAndSend("hello-java-topic-exchange", "java", "这是一个测试信息");
        rabbitTemplate.convertAndSend("hello-java-topic-exchange", "hello.world.java", "这是一个测试信息");
    }

    /**
     * 监听通配符队列1 和交换机绑定的路由键是 #.java
     * @param message
     * @param channel
     */
    @RabbitListener(queues = "hello-java-topic-queue1")
    public void receiveTopicQueue(String message,Channel channel) {
        System.out.println("监听hello-java-topic-queue1的消息是" + message);
    }

  首先向通配符模式的交换机发送了两个消息,消费者选择监听与交换机绑定了#.java路由键的队列hello-java-topic-queue1,由于发送的消息javahello.world.java#通配符能匹配上(前者匹配0个,后者匹配2个),所以两条消息消费者都接受到了。
在这里插入图片描述
  接下来测试*通配符,由于*通配符只能匹配单个单词,所以只监听到了hello.java

    @GetMapping("/send")
    public void sendMessage() {
        rabbitTemplate.convertAndSend("hello-java-topic-exchange", "java", "这是一个测试信息java");
        rabbitTemplate.convertAndSend("hello-java-topic-exchange", "hello.world.java", "这是一个测试信息hello.world.java");
        rabbitTemplate.convertAndSend("hello-java-topic-exchange", "hello.java", "这是一个测试信息hello.java");
    }
    
    /**
     * 监听通配符队列2 和交换机绑定的路由键是 *.java
     * @param message
     * @param channel
     */
    @RabbitListener(queues = "hello-java-topic-queue2")
    public void receiveTopicQueue(String message,Channel channel) {
        System.out.println("监听hello-java-topic-queue2的消息是" + message);
    }


三、消息可靠性

  在Rabbit MQ中,消息首先需要发送到交换机,再由交换机发送到队列,最后消费者从队列中读取消息。中转的步骤较多,其中每一步都有可能发生消息丢失的问题。Rabbit MQ采用消息投递回调消息确认机制分别保证生产者方和消费者方的消息可靠性。

3.1、ConfirmCallback&ReturnCallback

  ConfirmCallback是消息发送到交换机的回调,无论成功或者失败都会触发。ReturnCallback是消息从交换机到队列的回调,只有失败了才会触发。
  在使用之前,需要在配置文件中加上:

spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true

  可以在自定义配置类中为RabbitTemplate加上对应的配置:

    /**
     * 消息发送到交换机的回调 ConfirmCallback(成功和失败都会触发)
     * 从交换机到队列投递失败的回调ReturnCallback
     * 从队列到消费者是 ack机制 只要消费者没有手动ack,消息就默认未被消费,是unacked状态,服务器宕机,消息会重置为ready状态
     */
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

            /**
             * Confirmation callback.
             *
             * @param correlationData correlation data for the callback.
             * @param ack             true for ack, false for nack
             * @param cause           An optional cause, for nack, when available, otherwise null.
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("触发ConfirmCallback:correlationData:"+correlationData+",ack:"+ack+",cause:"+cause);
            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {

            /**
             * Returned message callback.
             *
             * @param message    the returned message.
             * @param replyCode  the reply code.
             * @param replyText  the reply text.
             * @param exchange   the exchange.
             * @param routingKey the routing key.
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("触发ReturnCallback:message"+message+"replyCode:"+replyCode+",exchange:"+exchange+",routingKey:"+routingKey);
            }
        });
    }

3.2、设置手动ACK

  如果没有进行任何设置,在Rabbit MQ中默认消息都是自动签收的:

3.2.1、演示自动签收消息丢失

    @GetMapping("/send")
    public void sendMessage() {
        for (int i = 0; i < 5; i++) {
            rabbitTemplate.convertAndSend("hello-java-exchange","hello.java","这是一个测试信息"+i);
        }
    }
    
    @RabbitListener(queues = "hello-java-queue")
    public void listenMessage(Message message, Channel channel) {
        System.out.println("listenMessage接收到的消息" + message);
        throw new NullPointerException();
    }

在这里插入图片描述
  上图的1条消息已经被自动签收
在这里插入图片描述
  模拟出现异常,消息不会丢失。在这里插入图片描述在这里插入图片描述
  假设此时服务器宕机了,队列中剩下未处理的消息会丢失:

    /**
     * 同一个消息只能被一个监听者接受
     * 一个消息处理完成后,才能处理下一个消息
     * @param message
     * @param channel
     */
    @RabbitListener(queues = "hello-java-queue")
    public void listenMessage(Message message, Channel channel) {
        System.out.println("listenMessage接收到的消息" + message);
    }

  通过jps -l找到当前服务进程,并Stop-Process -Id 25364 -Force强行终止:

在这里插入图片描述在这里插入图片描述  发现剩下的消息全部丢失:

在这里插入图片描述

3.2.2、演示手动签收

  我们可以设置消息手动确认,在配置文件中加入:spring.rabbitmq.listener.simple.acknowledge-mode=manual,打上断点:
在这里插入图片描述  队列中有5条unack(未确认)的消息。
在这里插入图片描述  接下来处理了两个消息,但是没有手动确认:
在这里插入图片描述  可以看到在控制台中依旧是5条unack状态:
在这里插入图片描述  假设此时服务器宕机了:
在这里插入图片描述  可以看到控制台中这5条消息回到了ready状态,没有丢失:
在这里插入图片描述  如果需要手动签收,需要在消费者方监听的代码中,使用channelbasicAckbasicNack方法,前者是签收,后者是拒绝,其中拒绝又有两种模式,一种是重新放回队列,另一种是丢弃:

  • 拒绝并丢弃
    @RabbitListener(queues = "hello-java-queue")
    public void listenMessage(Message message, Channel channel) {
        System.out.println("监听hello-java-queue的消息是"+message);
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //手动确认
            if (deliveryTag % 2 == 0) {
                //签收
                channel.basicAck(deliveryTag, false);
                System.out.println("接收到的消息" + deliveryTag);
            } else {
                //拒绝(丢弃)
                channel.basicNack(deliveryTag, false, false);
                System.out.println("拒绝了消息" + deliveryTag);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

  重新启动项目,消息一部分被拒绝,一部分被签收并消费
在这里插入图片描述  队列清空
在这里插入图片描述

  • 拒绝并重新放回队列:当一个消息被拒绝又重新放回队列时,会被再次消费,创建两个消费者监听hello-java-queue
    @RabbitListener(queues = "hello-java-queue")
    public void listenMessage(Message message, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            //手动确认
            if (deliveryTag % 2 == 0) {
                //签收
                channel.basicAck(deliveryTag, false);
                System.out.println("listenMessage接收到的消息" + deliveryTag);
            } else {
                //拒绝
                channel.basicNack(deliveryTag, false, true);
                System.out.println("listenMessage拒绝了消息" + deliveryTag);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @RabbitListener(queues = {"hello-java-queue","hello-java-fanout-queue"})
    public void receiveQueue(Message message,Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            channel.basicAck(deliveryTag, false);
            System.out.println("receiveQueue接收到的消息"+deliveryTag);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

  拒绝的消息可以放回队列被再次消费,直到有消费者签收为止。
在这里插入图片描述

总结

  下面总结一下Rabbit MQ 的工作流程

  1. 生产者与虚拟主机建立连接,经过信道,发送消息到交换机(带有路由键)。
  2. 交换机根据绑定和路由规则,将消息路由到一个或多个队列。
  3. 队列存储消息,等待消费者处理。
  4. 消费者从队列中接收消息,并处理。
  5. 消费者处理消息后,发送 ACK 确认,RabbitMQ 从队列中删除该消息。
  6. 如果消费者拒绝消息或处理失败,RabbitMQ 可能会将消息重新投递,或者发送到死信队列。

  其他诸如死信队列,延迟队列,消息幂等性,消息积压等将在高级篇中讲解。
  下一篇:订单服务&分布式事务

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

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

相关文章

总结TypeScript相关知识

目录 引入认识特点安装使用变量声明类型推导 JS 和 TS 共有类型number类型boolean类型string类型Array类型null和undefined类型object类型symbol类型对象类型函数类型 可选和只读type 和 interface索引签名类型断言非空类型断言类型缩小严格赋值检测现象TS 新增类型字面量类型a…

[统计分析] 出现典型锯齿图的一种情况;资源泄露

接上回说&#xff0c;https://mzhan017.blog.csdn.net/article/details/142689870&#xff1b; 在问题分析的过程中发现产品里的一个统计计数出现了下面的锯齿型。而这个问题的表象之一是每隔一段时间&#xff0c;业务程序重启。所以产生这个锯齿形的原因之一就是业务程序重启&…

【C++ STL算法】二分查找 lower_bound、upper_bound、equal_range、binary_search

文章目录 【 1. 首个不小于 lower_bound 】【 2. 首个大于 upper_bound 】【 3. 所有等于 equel_range 】【 4. 二分查找 binary_search 】 当 指定区域内的数据处于有序状态 时&#xff0c;如果想查找某个目标元素&#xff0c;更推荐使用二分查找的方法&#xff08;相比顺序查…

openpnp - juki吸嘴尺寸

文章目录 openpnp - juki吸嘴尺寸概述笔记吸嘴可以对应的最小元件尺寸END openpnp - juki吸嘴尺寸 概述 在网上买的juki吸嘴的商品页面&#xff0c;并没有具体的吸嘴尺寸。 现在贴片时&#xff0c;要根据吸嘴外径大小来决定具体元件要用哪种吸嘴&#xff0c;先自己量一下。 …

研究生系统化入门教程(四)【机器学习】分类算法:决策树(信息熵,信息增益);集成学习方法之随机森林:估计器的工作流程是什么?为何采用BootStrap抽样?

“一般人都不是他们想要做的那种人,而是他们不得不做的那种人。——毛姆《月亮与六便士》” 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 �…

鸿蒙next开发第一课03.ArkTs语法介绍-案例

前面已经学习了ArkTs的基本语法和DevEcoStudio的基本操作&#xff0c;接下来按照官方提示开发一个基本案例。 该案例是系统自带的demo&#xff0c;下载下来源代码后可以直接运行。 接下来我来演示如何运行demo。我在demo中加入了自己的注释。 切记&#xff1a;文件夹不能有中…

Crypto虐狗记---”你“和小鱼(七)

前言&#xff1a;剧情七 提示&#xff1a; 下载&#xff1a; 脚本&#xff1a; cyberpeace{125631357777427553} RSA算法_百度百科 (baidu.com)

VMware Tools 安装和配置

1. 使用 ISO 映射文件&#xff0c;并且选择.iso文件 2. 启动虚拟机&#xff0c;如果 VMware Tools 是灰色的&#xff0c;那么卸载 open-vm-tools&#xff08;不要重装&#xff09;&#xff0c;重新启动虚拟机。卸载可以参考&#xff1a;重装 open-vm-tools-CSDN博客 3. 拷贝挂载…

[C++]使用纯opencv部署yolov8-cls图像分类onnx模型

【算法介绍】 使用纯OpenCV部署YOLOv8-cls图像分类ONNX模型涉及几个关键步骤。 首先&#xff0c;你需要将YOLOv8-cls模型从PyTorch格式转换为ONNX格式&#xff0c;这是为了确保模型在不同深度学习框架之间的互操作性。这个转换过程通常是通过ultralytics框架中的model.export…

请求响应-08.响应-案例

一.案例 获取员工数据&#xff0c;返回统一响应结果&#xff0c;在页面上渲染展示 二.展示最终效果 三.步骤 步骤一&#xff1a; <dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3<…

C语言 | Leetcode C语言题解之第460题LFU缓存

题目&#xff1a; 题解&#xff1a; /* 数值链表的节点定义。 */ typedef struct ValueListNode_s {int key;int value;int counter;struct ValueListNode_s *prev;struct ValueListNode_s *next; } ValueListNode;/* 计数链表的节点定义。 其中&#xff0c;head是数值链表的头…

【Canvas与色彩】十六等分多彩隔断圆环

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>隔断圆环Draft5十六等分多彩</title><style type"text…

MyBatis-MP这个ORM框架强过你写的100行SQL,操作简单到不敢相信

MyBatis-MP这个ORM框架强过你写的100行SQL&#xff0c;操作简单到不敢相信 在繁杂的 Java 项目中&#xff0c;如何优雅、高效地进行数据库操作&#xff1f;MyBatis-MP&#xff0c;一个基于 MyBatis 的轻量级 ORM 框架&#xff0c;或许就是你的救星&#xff01;本文将介绍 MyBat…

Android车载——VehicleHal运行流程(Android 11)

1 概述 本篇主要讲解VehicleHal的主要运行流程&#xff0c;包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。 2 获取属性流程 2.1 获取属性流程源码分析 作为服务注册到hwServiceManager中的类是VehicleHalManager&#xff0c;所以&#xff0c;CarServic…

第十四章 Redis之全局唯一ID(分布式集群)

目录 一、概念 ‌二、全局唯一ID的生成方法‌ 三、Redis生成全局ID 3.1. 生成策略 3.2. 代码 一、概念 全局唯一ID是指在分布式系统中&#xff0c;每个实体都有一个唯一的标识符&#xff0c;确保在不同的节点或服务之间能够唯一标识一个实体。这种唯一性对于数据的一致性…

软件系统建设方案案例(word原件,文档系统)

文档管理系统建设的主要意义在于提升组织内部文档管理的效率、安全性和便利性。首先&#xff0c;通过集中存储和分类管理&#xff0c;文档管理系统能够迅速检索和共享文件&#xff0c;大幅提高工作效率。其次&#xff0c;系统内置的权限控制功能确保文档的安全&#xff0c;防止…

QT调用最新的libusb库

一&#xff1a;下载libusb文件 下载最新的库的下载网站&#xff1a;https://libusb.info/ 下载&#xff1a; 解压后目录如下&#xff1a; 二&#xff1a;库文件添加QT中 根据自己的编译器选择库&#xff1a; ①将头文件中添加libusb.h ②源文件中添加libusb-1.0.lib ③添加…

Linux:进程的创建、终止和等待

一、进程创建 1.1 fork函数初识 #include pid_t fork(void); 返回值&#xff1a;子进程中返回0&#xff0c;父进程返回子进程id&#xff0c;出错返回-1 调用fork函数后&#xff0c;内核做了下面的工作&#xff1a; 1、创建了一个子进程的PCB结构体、并拷贝一份相同的进程地址…

【C++】继承(万字详细总结)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C &#x1f308;喜欢的诗句:天行健,君子以自强不息. 前言&#xff1a;面向对象三大特性是&#xff1a;封装、继承、多态&#xff0c;今天的篇…

【社保通-注册安全分析报告-滑动验证加载不正常导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…