当前位置: 首页 > news >正文

来个网站急急急2020年/抖音seo排名

来个网站急急急2020年,抖音seo排名,有什么网站做可以国外的生意,绍兴网站设计🔥博客主页: 【小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录 1.0 RabbitMQ 初识 1.1 RabbitMQ 安装 2.0 数据隔离 2.1 用户管理 2.2 virtual host 虚拟主机 3.0 SpringAMQP 3.1 RabbitMQ 配置 3.2 发送消息 3.3 接收消息 3.4 Wor…

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 RabbitMQ 初识

        1.1 RabbitMQ 安装

        2.0 数据隔离

        2.1 用户管理

        2.2 virtual host 虚拟主机

        3.0 SpringAMQP

        3.1 RabbitMQ 配置

        3.2 发送消息

        3.3 接收消息

        3.4 WorkQueues 模式

        4.0 交换机类型

        4.1 Fanout 交换机

        4.2 Direct 交换机

        4.3 Topic 交换机

        5.0 声明队列和交换机

        5.1 基本的 API

        5.2 fanout 示例

        5.3 direct 示例

        5.4 基于注解声明

        5.4.1 Fanout 模式的交换机

        5.4.2 Direct 模式的交换机

        6.0 消息转换器

        6.1 配置 JSON 转化器

        6.2 实现业务幂等性


        1.0 RabbitMQ 初识

        RabbitMQ 是基于 Erlang 语言开发的开源消息通信中间件,官网地址:https://www.rabbitmq.com/

        RabbitMQ 中间件的使用目的,基于消息通知实现异步调用,一般包含三个角色:

        1)消息发送者:投递消息的人,就是原来的调用方。

        2)消息 Broker:管理、暂存、转发消息,可以理解为容器。

        3)消息接收者:接收和处理消息的人,服务提供方。

        在异步调用中,发送者不再直接同步调用接收者的业务接口,而是发送一条消息投递给消息 Broker,然后接收者根据自己的需求从消息 Broker 那里订阅消息。每当发送方发送消息后,接收者都能接收消息并处理,这样发送消息的人与接收消息的人完全解耦了。

RabbitMQ 对应的框架:

其中包含的几个概念:

        1)Publish:生产者,也就是发送消息的一方。

        2)consumer:消费者,也就是消费消息的一方。

        3)queue:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理。

        4)exchange:交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。

        5)virtual host:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的 exchange、queue

        1.1 RabbitMQ 安装

        1)基于 Docker 来安装 RabbitMQ,使用下面命令即可:

docker run \-e RABBITMQ_DEFAULT_USER=itheima \-e RABBITMQ_DEFAULT_PASS=123321 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq:3.8-management

        2)如果部署在云服务器中,则还需要添加管理员,命令如下:

1. 进入 RabbitMQ Docker 容器:

        首先找到正在运行的 RabbitMQ 容器的 ID 或名称:

docker ps

2. 然后使用 docker exec 命令进入容器:

docker exec -it mq /bin/bash

3. 添加用户:

rabbitmqctl add_user admin admin_password

4. 设置用户权限:

        将用户设置为超级管理员:

rabbitmqctl set_user_tags admin administrator

5. 配置用户虚拟主机权限:

        如果 RabbitMQ 使用虚拟主机 vhost,需要为用户设置相应的权限,假设虚拟主机为 “/”

rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

6. 退出容器:

exit

        可以看到安装命令中有两个映射的端口:

        15672:RabbitMQ 提供的管理控制台的端口。

        5672:RabbitMQ 的消息发送处理接口。

        安装完成后,访问自己的 "IP:15672" 即可看到管理控制台。

        如果是首次访问需要登录,默认的用户名和密码就是在设置超级管理员的时候所对应的用户名和密码。

        2.0 数据隔离

        2.1 用户管理

        点击 admin 选项卡,首先看到 RabbitMQ 控制台的用户管理界面:

        Name:用户名。

        Tages:administrator,说明该用户是超级管理员,拥有所有权限。

        Can access virtual host:/,可以访问的 virtual host,这里的 / 是默认的 virtual host 。

添加用户:

        xbs 用户已经拥有了超级管理员的权限,但是没有属于自己的虚拟主机。 

        因此,可以通过添加用户,且创建另一个 virtual host,实现数据隔离。

        2.2 virtual host 虚拟主机

        先退出原先的用户,切换到刚刚创建的 xbs 用户登录,然后点击 Virtual Host 菜单,进入 virtual hsot 管理页:

创建虚拟主机:

        不同的虚拟主机之间,数据是隔离的,相互不会受到影响。简单理解成 MySQL 中的数据库,数据库与数据库之间的数据不会受到影响。

        3.0 SpringAMQP

        由于 RabbitMQ 采用了 AMQP 协议,因此具备跨语言的特性。任何语言只要遵循 AMQP 协议收发消息,都可以与 RabbitMQ 交互。并且 RabbitMQ 官方也提供了各种不同语言的客户端。但是,RabbitMQ 官方提供的 Java 客户端编码相对复杂,一般生产环境下更多结合 Spring 来使用,而 Spring 的官方刚好基于 RabbitMQ 提供了这样一套消息收发的模板工具:SpringAMQP,并且还基于 SpringBoot 对其实现了自动装配,使用起来非常方便。

        SpringAMQP 官方地址:Spring AMQP

        SpringAMQP 提供了三个功能:

        1)自动声明队列、交换机及其绑定关系。

        2)基于注解的监听器模式,异步接收消息。

        3)封装了 RabbitTemplate 工具,用于发送消息。

        3.1 RabbitMQ 配置

        1)依赖引入:

        <!--AMQP依赖,包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>

        2)application.yml 添加配置:

spring:rabbitmq:host: 113.45.166.112 # 你的虚拟机IPport: 5672 # 端口virtual-host: /tt # 虚拟主机username: xbs # 用户名password: ******** # 密码

        3.2 发送消息

        在发送消息之前,需要先根据图形化界面创建一个 queue1 队列:

成功创建 queue1 队列:

        接着在测试类 Test 中编写,并利用 RabbitTemplate 实现消息发送:

代码如下:

    @Testvoid contextLoads() {//发送消息sendMessage("hello world");}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;private void sendMessage(String massage){//需要明确发送的队列名称,以及发送的消息rabbitTemplate.convertAndSend("queue1",massage );}

测试结果:

        可以看到 queue1 队列中接收到消息了:

        3.3 接收消息

        刚刚发送的消息,已经到 queue1 队列中,但是没有消费者进行消费,所以现在创建消费者进行处理消息。

        在 SpringAMQP 中,是使用了监听器来进行对绑定的队列进行监听,将来一旦监听的队列中有了消息,就会推送给当前服务,调用当前方法,处理消息。

代码如下:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage(String massage) {//用什么类型发送,就接收什么类型log.info("接收到的消息为: " + massage);}
}

        注意:需要加上 @Component 注解,成为 IOC 容器中的 Bean 对象。 

执行结果:

        3.4 WorkQueues 模式

        Work queues,任务模型,简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息,如果直接将消息发送到队列中,则队列每一个消息只能被处理一次,每一个消息都不能被多个消费者同时消费。

        而在 WorkQueues 模式中,就是将消息直接发送到队列中,且多个消费者绑定同一个队列。

WorkQueues 应用场景:

        当消息处理比较耗时时的时候,可能产生消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。

        此时就可以使用 work 模型,多个消费者共同处理消息处理,消息处理的速度就能大大提高了。

        默认情况下,消息是平均分配每个消费者,并没有考虑到消费者的处理能力,没有充分利用每一个消费者的能力,这样显然是有问题的。在 Spring 中有一个简单的配置,可以解决这个问题,通过修改 application.yml 文件,添加配置:

spring:rabbitmq:host: 113.45.166.112 # 你的虚拟机IPport: 5672 # 端口virtual-host: /tt # 虚拟主机username: xbs # 用户名password: ****** # 密码listener:simple:prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

        能者多劳,只要完成处理了消息,就能从队列获取一条消息。

测试:

        1)定义了两个消费者进行对队列中的消息进行消费:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);//手动阻塞,模拟消息处理中Thread.sleep(200);}//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage2(String massage) throws InterruptedException {String coloredMessage = String.format("\\u001B[0m", massage); // 黄色字体//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);//手动阻塞,模拟消息处理中Thread.sleep(800);}
}

        2)发送多条消息到队列中:

    @Testvoid contextLoads() {//发送多条消息到queue1队列中setRabbitTemplate();}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;private void setRabbitTemplate(){for (int i = 1; i <= 100; i++) {rabbitTemplate.convertAndSend("queue1","接收到第"+i+"条消息");}}

测试结果:

        很明显看得出来,消费能力强的消费者处理队列中的消息越多。正所谓能者多劳,这样充分利用每一个消费者的处理能力,可以有效避免消息积压问题。

        4.0 交换机类型

        在之前没有交换机,都是生产者直接发送消息给队列,而一旦引入交换机,消息发送的模式会有很大变化:

        Exchange 交换机,只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失。

        而如果将消息直接发送到队列中,那么队列可以将消息进行存储,不会丢失。

交换机的类型有四种:

        1)Fanout:广播,将消息交给所有绑定到交换机的队列。

        2)Direct:订阅,基于 RoutingKey(路由 key)发送给订阅了消息的队列。

        3)Topic:通配符订阅,与 Direct 类型,只不过 RoutingKey 可以使用通配符。

        4)Headers:头匹配,基于 MQ 的消息匹配,用的较少。

        4.1 Fanout 交换机

        在广播模式下,消息发送流程是这样的:

  • 1) 可以有多个队列

  • 2) 每个队列都要绑定到 Exchange(交换机)

  • 3) 生产者发送的消息,只能发送到交换机

  • 4) 交换机把消息发送给绑定过的所有队列

  • 5) 订阅队列的消费者都能拿到消息

广播模式演示:

        先创建 Fanout 类型的交换机:

        再创建两个队列 queue1、queue2:

        接着 xbs.fanout 交换需要绑定 queue1、queue2 队列:

        再接着用代码实现发送消息、接收消息:

代码如下:

        1)接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue2")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

        2)发送消息:

    @Testvoid contextLoads() {//发送多条消息到queue1队列中sendMessageByExchange("广播通知:狼来啦!!!!!!");}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;//发送消息到交换机private void sendMessageByExchange(String msg){rabbitTemplate.convertAndSend("xbs.fanout","",msg);}

执行结果:

        4.2 Direct 交换机

        在 Fanout 模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到 Direct 类型的 Exchange 。

在Direct模型下:

        1)队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)

        2)消息的发送方在 向 Exchange 发送消息时,也必须指定消息的 RoutingKey。

        3)Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 RoutingKey 进行判断,只有队列的 RoutingKey 与消息的 RoutingKey 完全一致,才会接收到消息。

Direct 交换机演示:

        1)首先创建 xbs.direct 交换机:

        2)再创建两个队列 queue3、queue4:

        3)xbs.direct 交换机绑定两个队列:

        4)通过 RoutingKey 路由关键字来指定。对于 queue4 队列也是同样的道理:

        接着使用代码来实现发送消息、接收消息:

代码实现:

        1)接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue3")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue4")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

        2)发送消息:

    @Testvoid contextLoads() {sendMessageByDirect1();}@Testpublic void sendMessageByDirect1(){rabbitTemplate.convertAndSend("xbs.direct","red","指定 red 路由进行发送消息");}@Testpublic void sendMessageByDirect2(){rabbitTemplate.convertAndSend("xbs.direct","blue","指定 blue 路由进行发送消息");}

执行结果:

        当指定 red 的路由关键字,那么只有队列 queue1 才能接收得到,因此对应的消费者才能进行处理。

        同理,当指定 blue 的路由关键字,那么只有队列 queue2 才能接收得到,因此对应的消费者才能进行处理。

        4.3 Topic 交换机

        Topic 类型的 Exchange 与 Direct 相比,都是可以根据 RoutingKey 吧消息路由到不同的队列。只不过 Topic 类型 Exhchange 可以让队列在绑定 BindingKey 的时候使用通配符。

通配符规则:

        1)#:匹配一个或多个词。

        2)*:只匹配一个词。

Topic 交换机演示:

        1)创建 xbs.topic 交换机:

        2)创建两个队列 queue5、queue5:

        3)将该两个队列进行绑定到 xbs.topic 交换机上,并且指定对应通配符的 RoutingKey 关键字:

        4)使用代码实现发送消息、接收消息:

代码如下:

        发送消息:

    @Testvoid contextLoads() {//两个队列绑定同一个交换机sendMessageByTopic1();}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void sendMessageByTopic1(){rabbitTemplate.convertAndSend("xbs.topic","class.student","该消息通知学生以及老师:狼来啦!!");}@Testpublic void sendMessageByTopic2(){rabbitTemplate.convertAndSend("xbs.topic","class.teacher","该消息不会通知学生:狼来啦!!");}

        接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue5")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue6")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

测试结果:

        当发送的消息的关键字为 class.student 时,则会将该消息发送到 queue5、queue6 队列中,所以对应的两个消费者都能进行消费:

        当发送的消息的关键字为 class.teacher 时,则该消息只会发送到 queue6 队列中:

        5.0 声明队列和交换机

        在之前都是基于 RabbitMQ 控制台来创建队列、交换机。但是在实际开发时,队列和交换机是程序员定义的,将来项目上线,又要交给运维去创建,那么程序员就需要把程序中运行的所有队列和交换机都写下来,交给运维,在这个过程中是是很容易出错的。

        因此推荐的做法是由程序员启动时检查队列和交换机是否存在,如果不存在则自动创建。

        5.1 基本的 API

        1)SpringAMQP 提供了一个 Queue 类,用来创建队列:

        2)SpringAMQP 还提供了一个 Exchange 接口,来表示所有不同类型的交换机:

        我们可以自己创建队列和交换机,不过 SpringAMQP 还提供了 ExchangeBuilder 来简化这个过程:

        3)而在绑定队列和交换机时,则需要使用 BindingBuilder 来创建 Binding 对象:

        5.2 fanout 示例

        在 FanoutExchangeCom 中创建一个类,声明两个队列和一个交换机:

@Configuration
public class FanoutExchangeCom {/*** 声明交换机* @return Fanout类型交换机*/@Beanpublic FanoutExchange fanoutExchange(){return ExchangeBuilder.fanoutExchange("tt.fanout").build();}/*** 声明队列* @return Queue*/@Beanpublic Queue queue1(){return new Queue("tt.queue1");}/*** 声明队列* @return Queue*/@Beanpublic Queue queue2(){return new Queue("tt.queue2");}/*** 绑定第一个队列和交换机*/@Beanpublic Binding bindingQueue1(Queue queue1, FanoutExchange fanoutExchange){return BindingBuilder.bind(queue1).to(fanoutExchange);}/*** 绑定第二个队列和交换机*/@Beanpublic Binding bindingQueue2(Queue queue2, FanoutExchange fanoutExchange){return BindingBuilder.bind(queue2).to(fanoutExchange);}
}

        当代码运行起来,则会自动创建交换机和队列:

        5.3 direct 示例

        在 DirectExchangeCom 中创建一个类,声明两个队列和一个交换机:

@Configuration
public class DirectExchangeCom {/*** 创建一个direct交换机* @return DirectExchange*/@Beanpublic DirectExchange directExchange(){return ExchangeBuilder.directExchange("tt.direct").build();}/*** 创建一个队列* @return Queue*/@Beanpublic Queue queue3(){return new Queue("tt.queue3");}/*** 创建一个队列* @return Queue*/@Beanpublic Queue queue4(){return new Queue("tt.queue4");}/*** 绑定队列到交换机* @param queue1* @param directExchange* @return 且指定路由键 xbs*/@Beanpublic Binding binding1(Queue queue3,DirectExchange directExchange){return BindingBuilder.bind(queue3).to(directExchange).with("xbs");}/*** 绑定队列到交换机* @param queue2* @param directExchange* @return 且指定路由键 tt*/@Beanpublic Binding binding2(Queue queue4,DirectExchange directExchange){return BindingBuilder.bind(queue4).to(directExchange).with("tt");}}

程序执行结果:

        自动创建队列:

        自动创建交换机:

        5.4 基于注解声明

        基于注解 @Bean 的方式声明队列和交换机比价麻烦,Spring 还提供了基于注解方式来声明。在监听队列 @RabbitListener 注解上进行添加相关的注解来声明交换机或者队列。

        5.4.1 Fanout 模式的交换机

代码如下:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue5"),exchange = @Exchange(name = "tt.fanoutExchange", type = ExchangeTypes.FANOUT)))private void sendMessage(String massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage);}

        通过注解 @Queue 声明队列,@Exchange 声明交换机,可以指定类型,默认的类型为 Direct 类型的交换机。最后再通过 @QueueBinding 注解进行绑定队列到交换机中。

当程序运行起来:

        tt.fanoutExchange 交换机:

       tt.queue5 队列:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue6"),exchange = @Exchange(name = "tt.directExchange",type = ExchangeTypes.DIRECT),key = "tt.xbs"))private void listenMessageByDirect(String massage) throws InterruptedException {log.info("sendMessageByFanout发送的消息为: " + massage);}

        5.4.2 Direct 模式的交换机

代码如下:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue6"),exchange = @Exchange(name = "tt.directExchange",type = ExchangeTypes.DIRECT),key = "tt.xbs"))private void listenMessageByDirect(String massage) throws InterruptedException {log.info("sendMessageByFanout发送的消息为: " + massage);}

        通过 @Queue 注解声明队列,@Exchange 注解声明交换机,且指定交换机类型。还通过 @QueueBinding 注解将队列绑定到交换机中,通过 key 指定路由关键字。

程序执行结果:

        tt.directExchange 交换机:

        tt.queue6 队列:

        6.0 消息转换器

        Spring 的消息发送代码接收的消息体是一个 Object:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("text",user);}

        此外,还需要确保 User 类实现 Serializable 接口,这是因为 SimpleMessageConverter 只支持 String、byte[] 和 Serializable 类型的消息负载。

此时队列中的消息:

        在数据传输时,SimpleMessageConverter 会把发送的消息序列化为字节发送给 MQ,接收消息的时候,还会把字节反序列化为 Java 对象。

        只不过,默认情况下 Spring 采用的序列化方式是 JDK 序列化。从所周知,JDK 序列化存在以下问题:

        1)数据体积过大。

        2)有安全漏洞。

        3)可读性差。

        6.1 配置 JSON 转化器

        显然,JDK 序列化方式并不合适,期望消息体的体积更小、可读性更高,因此可以使用 JSON 方式来做序列化和反序列化。

        1)引入依赖:

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.10</version>
</dependency>

        注意,如果项目中引入了 Spring-boot-start-wed 依赖,则无需再次引入 Jackson 依赖。

        2)添加新 MessageConverter 类型

    @Beanpublic MessageConverter messageConverter(){//1.定义消息转化器Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();//2.配置自定创建消息id,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息converter.setCreateMessageIds(true);return converter;}

        消息转换器中添加的 messageId 可以便于我们将来做幂等性判断。

配置好了 Json 消息转化器之后,进行测试:

        发送的消息是 User 类型,且该 User 类不需要再实现 SimpleMessageConverter 接口。

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("text",user);}

执行结果:

        可以很容易看到消息体中的内容,且字节只需要 81 个。

        6.2 实现业务幂等性

        幂等是一个数学概念,用函数表达式来描述:f(x)=f(f(x)) 。在程序开发中,则是指同一个业务,执行一次或多次对业务状态的影响是一致的。

        实现业务幂等可以通过唯一消息 id,是给每个消息都设置一个唯一 ID,利用 id 区分是否是重复消息:

        1)每一条消息都生成一个唯一的 id,与消息一起投递给消费者。

        2)消费者接收到消息后处理自己的业务,业务处理成功后将消息 ID 保存到数据库。

        3)如果下次又收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理。

        那么通过配置 JSON 转化器中,设置 setCreateMessageIds(true) 方法,接收者都可以在消息中获取到唯一 id 。

代码如下:

        1)发送消息:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("textExchange", "xbs", user, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setMessageId("123456");return message;}});}

         通过匿名类 MessagePostProcessor 重写 postProcessMessage 方法,将消息发送之前对消息的属性进行修改,比如说配置 id 属性、配置消息头等等,而对于 postProcessMessage 方法不能携带消息本体,只能在 convertAndSend() 方法中属性进行配置,比如说发送 user 实体类。

        2)接收消息:

    @AutowiredMessageConverter messageConverter;@RabbitListener(queues = "text")private void listenMessage(Message massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage.getMessageProperties().getMessageId());User user = (User) messageConverter.fromMessage(massage);log.info("sendMessage发送的消息为: " + user);}

        监听 text 队列中的消息,接收的参数不再是 user,而是 Message 类型,通过 massage.getMessageProperties().getMessageId() 获取唯一 id,再通过 MessageConverter 消息转化器将 massage 的本体消息转化为 User 类型。

接收消息的结果:

相关文章:

RabbitMQ 篇-深入了解 RabbitMQ 安装以及 SpringAMQP 的基础使用(声明队列和交换机、发送接收消息、配置 JSON 消息转化器)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 RabbitMQ 初识 1.1 RabbitMQ 安装 2.0 数据隔离 2.1 用户管理 2.2 virtual host 虚拟主机 3.0 SpringAMQP 3.1 RabbitMQ 配置 3.2 发送消息 3.3 接收消息 3.4 Wor…...

在 WPF 中,绑定机制是如何工作的?WPF数据绑定机制解析

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;数据绑定机制是其核心功能之一&#xff0c;广泛用于连接应用程序的UI&#xff08;用户界面&#xff09;和应用程序的业务逻辑层。数据绑定允许你将UI元素与数据源&#xff08;如对象、集合或其他数…...

pwn学习笔记(12)--Chunk Extend and Overlapping

pwn学习笔记&#xff08;12&#xff09;–Chunk Extend and Overlapping ​ chunk extend 是堆漏洞的一种常见利用手法&#xff0c;通过 extend 可以实现 chunk overlapping&#xff08;块重叠&#xff09; 的效果。这种利用方法需要以下的时机和条件&#xff1a; 程序中存在…...

java基础面试题六集合框架

目录 1. List&#xff0c;Set&#xff0c;Map是否继承自collection接口&#xff1f; 2. 说说List,Set,Map三者的区别 3. 写出list、map、set接口的实现类&#xff0c;并说出其特点 4. 常见集合类的区别和适用场景 5. 集合的父类是谁&#xff1f;哪些安全的&#xff1f; 6…...

2024年12月一区SCI-指数-三角优化算法ETO-附Matlab免费代码

引言 本期介绍了一种基于数学概念的元启发式优化算法&#xff0c;称为指数-三角优化算法Exponential-trigonometric optimization algorithm&#xff0c;ETO。该算法基于指数函数和三角函数的复杂组合&#xff0c;于2024年12月最新发表在中JCR1区、 中科院1区 SCI期刊Computer…...

设置服务器ssh连接超时时间

在Linux服务器上&#xff0c;您可以通过修改SSH服务器配置文件来设置SSH连接的超时时间。以下是设置SSH连接超时时间的一些步骤&#xff1a; 打开SSH服务器配置文件。这个文件通常是/etc/ssh/sshd_config。sudo nano /etc/ssh/sshd_config在配置文件中&#xff0c;您可以设置以…...

Dubbo分布式日志跟踪实现

前言 随着越来越多的应用逐渐微服务化后&#xff0c;分布式服务之间的RPC调用使得异常排查的难度骤增&#xff0c;最明显的一个问题&#xff0c;就是整个调用链路的日志不在一台机器上&#xff0c;往往定位问题就要花费大量时间。如何在一个分布式网络中把单次请求的整个调用日…...

EPSON机械手与第三方相机的校准功能设计By python

EPSON机械手与第三方相机的校准功能设计By python 使用Python来实现EPSON机械手与第三方相机的校准功能是一个复杂但可行的任务。这通常涉及以下几个步骤:硬件接口通信、图像处理、标定算法实现和控制逻辑编写。 1. 环境准备 首先,库 pip install numpy opencv-python pyse…...

探索 Java 23:新时代的编程利器

一、引言 随着技术的不断发展&#xff0c;Java 作为一种广泛应用的编程语言也在不断演进。Java 23 的推出带来了许多令人兴奋的新特性和改进&#xff0c;为开发者提供了更多的工具和功能&#xff0c;以应对日益复杂的软件开发挑战。本文将深入介绍 Java 23 的各个方面。 二、J…...

CSS3_3D变换(七)

1、CSS3_3D变换 1.1 3D空间与景深 3D空间&#xff1a;在父元素中将属性transform-style设置为preserve-3d开启3D空间&#xff0c;默认值为flat&#xff08;开启2D空间&#xff09;&#xff1b; 景深&#xff1a;人眼与平面的距离&#xff0c;产生透视效果&#xff0c;使得效果…...

Mesh网格

Mesh(网格) 定义&#xff1a;Mesh 是一个包含顶点、三角形、顶点法线、UV坐标、颜色和骨骼权重等数据的对象。它定义了3D模型的几何形状。 功能&#xff1a; 顶点&#xff08;Vertices&#xff09;&#xff1a;构成3D模型的点。 三角形&#xff08;Triangles&#xff09;&…...

LeetCode 509.斐波那契数

动态规划思想 五步骤&#xff1a; 1.确定dp[i]含义 2.递推公式 3.初始化 4.遍历顺序 5.打印dp数组 利用状态压缩&#xff0c;简化空间复杂度。在原代码中&#xff0c;dp 数组保存了所有状态&#xff0c;但实际上斐波那契数列的计算只需要前两个状态。因此&#xff0c;我们…...

SQL Server 数据太多如何优化

大家好&#xff0c;我是 V 哥。讲了很多数据库&#xff0c;有小伙伴说&#xff0c;SQL Server 也讲一讲啊&#xff0c;好吧&#xff0c;V 哥做个听话的门童&#xff0c;今天要聊一聊 SQL Server。 在 SQL Server 中&#xff0c;当数据量增大时&#xff0c;数据库的性能可能会受…...

关于word 页眉页脚的一些小问题

去掉页眉底纹&#xff1a; 对文档的段落边框和底纹进行设置&#xff0c;也是页眉横线怎么删除的一种解决方式&#xff0c;具体操作如下&#xff1a; 选中页眉中的横线文本&#xff1b; 点击【开始】选项卡&#xff0c;在【段落】组中点击【边框】按钮的下拉箭头&#xff1b; …...

【高等数学学习记录】连续函数的运算与初等函数的连续性

一、知识点 &#xff08;一&#xff09;连续函数的和、差、积、商的连续性 定理1 设函数 f ( x ) f(x) f(x) 和 g ( x ) g(x) g(x) 在点 x 0 x_0 x0​ 连续&#xff0c;则它们的和&#xff08;差&#xff09; f g f\pm g fg、积 f ⋅ g f\cdot g f⋅g 及商 f g \frac{f…...

【抖音直播间弹幕】protobuf协议分析

将Uint8Array变成 PushFrame格式&#xff0c;里面的payload就存放着弹幕消息 点进去就可以看到其定义的proto结构 headers是一个自定义类型 将测试数据保存一下&#xff0c;等下做对比 先将PushFrame的 payload 内容进行gzip解压 然后再解析为响应 可以看到里面有对应的消…...

Swift 开发教程系列 - 第11章:内存管理和 ARC(Automatic Reference Counting)

在 Swift 中&#xff0c;内存管理由 ARC&#xff08;自动引用计数&#xff09;机制自动处理。ARC 通过追踪和管理对象的引用计数来确保分配的内存得到有效释放。尽管 ARC 在大多数情况下能够高效地管理内存&#xff0c;但理解其工作原理仍然十分重要&#xff0c;因为不当的引用…...

C#中 layout的用法

在C#中&#xff0c;layout并不是一个直接用于C#语言本身的关键字或特性。然而&#xff0c;layout在与C#紧密相关的某些上下文中确实有其用途&#xff0c;特别是在涉及用户界面&#xff08;UI&#xff09;设计和数据展示时。以下是几个常见的与layout相关的用法场景&#xff1a;…...

【编程概念基础知识】

、编程基础 一、面向对象的三大特性 1、封装&#xff1a; 盒子、零件、按钮 隐藏对象 的内部状态&#xff0c;并且只通过对象的方法来访问数据 想象你有一个小盒子&#xff08;这个盒子就是一个类&#xff09;&#xff0c;里面装着一些零件&#xff08;这些零件就是数据&a…...

【React】深入理解 JSX语法

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解 JSX语法1. JSX 简介2. JSX 的基本语法2.1 基本结构2.2 与普通 JavaScr…...

【Linux】从零开始使用多路转接IO --- 理解EPOLL的 LT水平触发模式 与 ET边缘触发模式

当你偶尔发现语言变得无力时&#xff0c; 不妨安静下来&#xff0c; 让沉默替你发声。 --- 里则林 --- 从零开始认识多路转接 1 EPOLL优缺点2 EPOLL工作模式 1 EPOLL优缺点 poll 的优点(和 select 的缺点对应) 接口使用方便&#xff1a;虽然拆分成了三个函数&#xff0c;…...

QtLua

描述 QtLua 库旨在使用 Lua 脚本语言使 Qt4/Qt5 应用程序可编写脚本。它是 QtScript 模块的替代品。 QtLua 不会为 Qt 生成或使用生成的绑定代码。相反&#xff0c;它提供了有用的 C 包装器类&#xff0c;使 C 和 lua 对象都可以从 lua 和 C 访问。它利用 Qt 元对象系统将 QOb…...

c++-有关计数、双变量累加、半衰、阶乘、变量值互换的基础知识

C是一种非常强大和灵活的编程语言&#xff0c;它包含了许多重要的概念和技巧。在本文中&#xff0c;我们将重点讨论五个主题&#xff1a;计数、双变量累加、半衰、阶乘和变量值的互换。我们将介绍这些概念的定义、用法、题目、答案和解释&#xff0c;以帮助读者更好地理解和运用…...

MyBatis3-获取参数值的方式、查询功能及特殊SQL执行

目录 准备工作 获取参数值的方式&#xff08;重点&#xff09; 查询功能 查询一个实体类对象 查询一个list集合 查询单个数据 查询一条数据为map集合 查询多条数据为map集合 特殊SQL执行 模糊查询 批量删除 动态设置表名 添加功能获取自增的主键 准备工作 模块My…...

web——[SUCTF 2019]EasySQL1——堆叠注入

这个题主要是讲述了堆叠注入的用法&#xff0c;来复现一下 什么是堆叠注入 堆叠注入&#xff1a;将多条SQL语句放在一起&#xff0c;并用分号;隔开。 1.查看数据库的名称 查看数据库名称 1;show databases; 发现有名称为ctftraining的数据库 2.对表进行查询 1;show tabl…...

【Ubuntu学习】Ubuntu无法使用vim命令编辑

问题 在VMware首次安装Ubuntu&#xff0c;使用vi指令对文件进行编辑&#xff0c;按i键后无法更改文件内容。 原因 由于Ubuntu中预装的是vim-tiny&#xff0c;平时开发中需要使用vim-full。 解决方案 卸载预装vim sudo apt-get remove vim-common安装vim-full sudo apt-get …...

UniAPP u-popup 禁止背景滑动

增加class .NoScroll {overflow: hidden;position: fixed; }在外层div上增加该class判断条件...

F5全新报告揭示AI时代API安全面临严峻挑战

F5 《2024年应用策略现状报告:API安全》揭示了 API 保护中的漏洞以及对全面安全措施的迫切需求 西雅图,2024年11月11日 – F5(NASDAQ: FFIV)日前发布《2024年应用策略现状报告:API 安全》(以下简称为“报告”),揭示了跨行业API安全面临的严峻现状。该报告强调了企业API保护方面…...

使用C语言进行信号处理:从理论到实践的全面指南

1. 引言 在现代操作系统中&#xff0c;信号是一种进程间通信机制&#xff0c;它允许操作系统或其他进程向一个进程发送消息。信号可以用来通知进程发生了一些重要事件&#xff0c;如用户请求终止进程、硬件异常、定时器超时等。掌握信号处理技术对于开发健壮、高效的系统程序至…...

什么是工单管理系统?全面认识指南

在现代企业中&#xff0c;客户服务和支持是业务成功的关键因素之一。为了有效地管理客户请求和问题&#xff0c;许多公司采用了工单管理系统。本文将深入探讨工单管理系统的定义、功能、优势。 一、工单管理系统的定义 工单管理系统是一种软件工具&#xff0c;旨在帮助企业管…...