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

谷粒商城-消息队列Rabbitmq

RabbitMq参考文档

在谷粒商城项目中使用消息队列主要有以下几个重要原因:

  1. 异步处理提高性能
    • 场景示例:在订单系统中,当用户提交订单后,系统需要完成多个操作,如更新库存、生成订单记录、发送订单通知等。如果这些操作都同步进行,用户需要等待所有操作完成才能得到响应,这会导致响应时间变长。
    • 使用消息队列后的优化:通过消息队列,如 RabbitMQ,可以将这些操作中的部分任务异步处理。例如,当订单创建成功后,将更新库存和发送通知的任务以消息的形式发送到消息队列。库存服务和通知服务可以从消息队列中获取消息并进行处理,而订单系统不需要等待这些操作完成就可以返回给用户订单创建成功的信息,大大提高了系统的响应速度和吞吐量。
  1. 流量削峰填谷
    • 高并发场景示例:在电商平台的促销活动期间,如 “双 11”“618” 等,会出现大量的订单请求。如果没有消息队列,这些请求直接涌向数据库和后端服务,可能会导致数据库和服务器不堪重负,出现系统崩溃或者响应缓慢的情况。
    • 消息队列的缓冲作用:消息队列可以作为一个缓冲层。在流量高峰时,订单请求等消息先堆积在消息队列中,后端服务按照自己的处理能力从消息队列中获取消息并处理。这样就避免了瞬间的高流量对系统造成过大的压力,实现了流量的削峰。而在流量低谷时,消息队列中的消息可以继续被处理,达到填谷的效果。
  1. 解耦服务之间的依赖关系
    • 服务耦合问题示例:假设谷粒商城中的商品服务和库存服务紧密耦合,商品服务在更新商品信息后直接调用库存服务来更新库存。如果库存服务出现故障或者需要升级,商品服务可能会受到影响。
    • 通过消息队列解耦:引入消息队列后,商品服务只需要将更新库存的消息发送到消息队列,而不需要关心库存服务的具体实现和状态。库存服务从消息队列中获取消息并进行处理,这样两个服务之间的依赖关系得到了解耦。即使库存服务出现故障,商品服务仍然可以正常运行,消息会在库存服务恢复后继续被处理。

1、MQ简介

1)、为什么要用MQ:

  • 异步处理
  • 应用解耦
  • 流量控制

2)、概述:

1.大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力
2.消息服务中两个重要概念:
2.1消息代理(message broker)和目的地(destination)
2.2当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。
3.消息队列主要有两种形式的目的地
3.1队列(queue):点对点消息通信(point-to-point)
3.2主题(topic):发布(publish)/订阅(subscribe)消息通信
4.点对点式:
4.1消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列
4.2消息只有唯一的发送者和接受者,但并不是说只能有一个接收者
5.发布订阅式
5.1发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息
6.JMS(Java Message Service)JAVA消息服务:
6.1基于JVM消息代理的规范。ActiveMQ、HornetMQ是JMS实现
7.AMQP(Advanced Message Queuing Protocol)
7.1高级消息队列协议,也是一个消息代理的规范,兼容JMS
7.2RabbitMQ是AMQP的实现


8.Spring支持
8.1spring-jms提供了对JMS的支持
8.2spring-rabbit提供了对AMQP的支持
8.3需要ConnectionFactory的实现来连接消息代理
8.4提供JmsTemplate、RabbitTemplate来发送消息
8.5@JmsListener( JMS )、 @RabbitListener ( AMQP)注解在方法上监听消息(代理发布的消息)
8.6@EnableJms、 @EnableRabbit开启支持
9.Spring Boot自动配置
9.1JmsAutoConfiguration
9.2RabbitAutoConfiguration
10.市面的MQ产品
ActiveMQ、RabbitMQ、RocketMQ、Kafka


2、RabbitMQ概念

1)、RabbitMQ简介:

RabbitMQ 是一个由 erlang 开发的 AMQP(Advanved Message Queue Protocol) 的开源实现。


2)、核心概念


Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key (路由键)、 priority (相对于其他消息的优先权)、 delivery-mode (指出该消息可能需要持久性存储)等。


Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。


Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange 有 4 种类型: direct( 默认 ) ,fanout,topic和 headers ,不同类型的 Exchange 转发消息的策略有所区别。


Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。


Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
Exchange 和 Queue 的绑定可以是多对多的关系。


Connection
网络连接,比如一个 TCP 连接。


Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的 TCP 连接内的虚拟连接, AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。


Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。


Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。


Broker
表示消息队列服务器实体

3、docker安装RabbitMQ

docker pull rabbitmq:3.8.2


不下载镜像,直接安装。默认会帮你下载

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:3.8.2
  • 4369, 25672 (Erlang发现&集群端口)
  • 5672, 5671 (AMQP端口)
  • 15672 (web管理后台端口)
  • 61613, 61614 (STOMP协议端口)
  • 1883, 8883 (MQTT协议端口)
  • 官网地址:https://www.rabbitmq.com/networking.html

修改只要启动docker自动重启rabbitMQ

docker update rabbitmq --restart=always

登录rabbitmq控制台: http://192.168.119.127:15672

账号:guest
密码:guest

若没反应

进入rabbitmq的插件管理中

docker exec -it xxx /bin/bash

rabbitmq-plugins list

故障排除

[root@VM-8-12-centos ~]# docker exec -it 3e9 /bin/bash
root@3e959345fd18:/# rabbitmq-plugins list
Listing plugins with pattern ".*" ...Configured: E = explicitly enabled; e = implicitly enabled| Status: * = running on rabbit@3e959345fd18|/
[  ] rabbitmq_amqp1_0                  3.8.2
[  ] rabbitmq_auth_backend_cache       3.8.2
[  ] rabbitmq_auth_backend_http        3.8.2
[  ] rabbitmq_auth_backend_ldap        3.8.2
[  ] rabbitmq_auth_backend_oauth2      3.8.2
[  ] rabbitmq_auth_mechanism_ssl       3.8.2
[  ] rabbitmq_consistent_hash_exchange 3.8.2
[  ] rabbitmq_event_exchange           3.8.2
[  ] rabbitmq_federation               3.8.2
[  ] rabbitmq_federation_management    3.8.2
[  ] rabbitmq_jms_topic_exchange       3.8.2
[  ] rabbitmq_management               3.8.2
[  ] rabbitmq_management_agent         3.8.2
[  ] rabbitmq_mqtt                     3.8.2
[  ] rabbitmq_peer_discovery_aws       3.8.2
[  ] rabbitmq_peer_discovery_common    3.8.2
[  ] rabbitmq_peer_discovery_consul    3.8.2
[  ] rabbitmq_peer_discovery_etcd      3.8.2
[  ] rabbitmq_peer_discovery_k8s       3.8.2
[  ] rabbitmq_prometheus               3.8.2
[  ] rabbitmq_random_exchange          3.8.2
[  ] rabbitmq_recent_history_exchange  3.8.2
[  ] rabbitmq_sharding                 3.8.2
[  ] rabbitmq_shovel                   3.8.2
[  ] rabbitmq_shovel_management        3.8.2
[  ] rabbitmq_stomp                    3.8.2
[  ] rabbitmq_top                      3.8.2
[  ] rabbitmq_tracing                  3.8.2
[  ] rabbitmq_trust_store              3.8.2
[  ] rabbitmq_web_dispatch             3.8.2
[  ] rabbitmq_web_mqtt                 3.8.2
[  ] rabbitmq_web_mqtt_examples        3.8.2
[  ] rabbitmq_web_stomp                3.8.2
[  ] rabbitmq_web_stomp_examples       3.8.2

发现[ ] rabbitmq_management ==》代表没开启web页面访问权限

解决:

#容器内执行
rabbitmq-plugins enable rabbitmq_management

执行完后,管理页面就可以成功访问了。

不想每次都执行?那就自己加工一下即可

vim Dockerfile
FROM rabbitmq:latest
# 启用 RabbitMQ 管理插件
RUN rabbitmq-plugins enable rabbitmq_management
然后,在构建 Docker 镜像时使用该 Dockerfile:docker build -t my-rabbitmq-image .

1)、创建一个交换机

2)、创建队列

3)、交换机绑定队列

删除交换机,先双击点击要删除的交换机,接着

4、Exchange类型

1)、RabbitMQ运行机制

AMQP中消息的路由过程和 Java 开 发者熟悉的 JMS 存在一些差别,AMQP中增加了 Exchange和 Binding 的角色。生产者把消息发布 到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到那个队列。

2)、Exchange 类型

Exchange分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、 fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由键, headers 交换器和 direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接 看另外三种类型:

Direct Exchange(点对点)

消息中的路由键( routing key )如果和Binding 中的 binding key 一致,交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog” ,则只转发 routing key 标记为“ dog” 的消息,不会转发“dog.puppy” ,也不会转发“ dog.guard”等等。它是完全匹配、单播的模式。

Fanout Exchange

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

Topic Exchange

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。 它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开 。它同样也会识别两个通配符:符号“#” 和符号 “* ”。

# 匹配 0 个或多个单词, * 匹配一个单词。

发消息是发给exchange(交换机),

接收信息是接收queue(队列)

3)、测试

根据下图要求我们创建交换机和队列

1、exchange.direct

创建交换机

创建队列

绑定队列

发送消息

消费消息

2、exchange.fanout

创建交换机

绑定队列

发送消息

消费消息

3、exchange.topic

创建交换机

绑定队列

发送消息

(1)发送hello.news只匹配*.news的队列

(2)发送atguigu.news的匹配atguigu.#和*.news的队列

5、SpringBoot整合RabbitMQ


RabbitMQ的使用

1、引入amqp;RabbitAutoConfiguration就会自动生效
2、给容器中自动配置了RabbitTemplate、AmqpAdmin、CachingConnectionFactory、RabbitMessagingTemplate所有的属性都是@ConfigurationProperties(prefix = "spring.rabbitmq")public class RabbitProperties
3、给配置文件中配置 spring.rabbitmq 信息
4、@EnableRabbit 开启功能

gulimall-order

1)、pom导入amqp依赖

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

2)、添加配置(@ConfigurationProperties(prefix = "spring.rabbitmq"))注意配置前缀一定是spring.rabbitmq

spring.rabbitmq.host=192.168.119.127
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/


3)、主启动类添加@EnableRabbit注解

@EnableRabbit
@SpringBootApplication
public class GulimallOrderApplication { public static void main(String[] args) {SpringApplication.run(GulimallOrderApplication.class, args);}}


6、AmqpAdmin使用

作用:

创建exchange 交换机

创建queue 队列

创建binding 绑定


durable 持久化

autoDelete 交换机是否自动删除

@Slf4j
@SpringBootTest
class GulimallOrderApplicationTests {@Autowired
AmqpAdmin amqpAdmin;/*** 1、如何创建Exchange[hello-java-exchange]、Queue、Binding*      1)、使用AmqpAdmin进行创建* 2、如何收发消息*/
@Test
void contextLoads() {DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);amqpAdmin.declareExchange(directExchange);log.info("Exchange[{}]创建成功","hello-java-exchange");
}@Test
public void createQueue(){Queue queue = new Queue("hello-java-queue",true,false,false);amqpAdmin.declareQueue(queue);log.info("Queue[{}]创建成功","hello-java-queue");
}@Test
public void createBinding(){Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE,"hello-java-exchange","hello.java",null);amqpAdmin.declareBinding(binding);log.info("Binding[{}]创建成功","hello-java-binding");
}
}

7、RabbitTemplate使用

作用:发送消息

@Slf4j
@SpringBootTest
class GulimallOrderApplicationTests {
@Autowired
AmqpAdmin amqpAdmin;@Autowired
RabbitTemplate rabbitTemplate;/*** 发送消息*/
@Test
public void sendMessageTest() {OrderReturnApplyEntity orderReturnApplyEntity = new OrderReturnApplyEntity();orderReturnApplyEntity.setId(1L);orderReturnApplyEntity.setCreateTime(new Date());orderReturnApplyEntity.setReturnName("哈哈哈");//1、发送消息,如果发送的消息是个对象,我们会使用序列化机制,将对象写出去。对象必须实现SerializableString msg = "hello word";//2、配置MyRabbitConfig,让发送的对象类型的消息,可以是一个jsonrabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", orderReturnApplyEntity);log.info("消息发送完成{}", orderReturnApplyEntity);
}
}

注意:

配置MyRabbitConfig,让发送的对象类型的消息,可以是一个json

添加“com.atguigu.gulimall.order.config.MyRabbitConfig”类,代码如下:

序列化

@Configuration
public class MyRabbitConfig {
/*** 使用JSON序列化机制,进行消息转换* * @return*/
@Bean
public MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();
}
}

8、RabbitListener&RabbitHandler

作用:

接收消息


监听消息:使用@RabbitListener; 主启动类必须有@EnableRabbit

@RabbitListener:类+方法上(监听哪些队列即可)
@RabbitHandler:标在方法上(重载区分不同的消息)

RabbitListener用法

queues:声明需要监听的所有队列

org.springframework.amqp.core.Message

参数可以写以下内容:

1、Message message:原生消息详细信息。头+体

2、T<发送的消息类型> OrderReturnApplyEntity content

3、Channel channel 当前传输数据的通道

Queue:可以很多人都来监听。只要收到消息,队列删除消息,而且只能有一个收到此消息

场景:

1)、订单服务启动多个;同一个消息,只能有一个客户端收到

2)、只有一个消息完全处理完,方法运行结束,我们就可以接收到下一个消息

修改“com.atguigu.gulimall.order.GulimallOrderApplicationTests”类,代码如下:

/*** 同一个队列发送不同消息*/
@Test
public void sendUniqueMessageTest() {for (int i = 0; i < 10; i++) {if (i % 2 == 0) {OrderReturnApplyEntity orderReturnApplyEntity = new OrderReturnApplyEntity();orderReturnApplyEntity.setId(1L);orderReturnApplyEntity.setCreateTime(new Date());orderReturnApplyEntity.setReturnName("哈哈哈");// 配置MyRabbitConfig,让发送的对象类型的消息,可以是一个jsonrabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", orderReturnApplyEntity, new CorrelationData(UUID.randomUUID().toString()));} else {OrderEntity entity = new OrderEntity();entity.setOrderSn(UUID.randomUUID().toString());rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", entity, new CorrelationData(UUID.randomUUID().toString()));}}
}

修改“com.atguigu.gulimall.order.service.impl.OrderItemServiceImpl”类,代码如下:

package com.atguigu.gulimall.order.service.impl;import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderReturnApplyEntity;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.service.OrderItemService;import javax.swing.*;@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {/*** queues:声明需要监听的所有队列** org.springframework.amqp.core.Message* @param message** 参数可以写以下内容* 1、Message message:原生消息详细信息。头+体* 2、T<发送的消息类型> OrderReturnApplyEntity content* 3、Channel channel 当前传输数据的通道** Queue:可以很多人都来监听。只要收到消息,队列删除消息,而且只能有一个收到此消息* 场景:*       1)、订单服务启动多个;同一个消息,只能有一个客户端收到*       2)、只有一个消息完全处理完,方法运行结束,我们就可以接收到下一个消息**///@RabbitListener(queues = {"hello-java-queue"})@RabbitHandlerpublic void receiverMessage(Message message,OrderReturnApplyEntity content,Channel channel) throws InterruptedException {//消息体byte[] body = message.getBody();//消息头属性信息MessageProperties properties = message.getMessageProperties();System.out.println("接收到消息...内容:" + content);
//        Thread.sleep(3000);System.out.println("消息处理完成=》"+content.getReturnName());}@RabbitHandlerpublic void receiverMessage(OrderEntity orderEntity){System.out.println("接收到消息...内容:" + orderEntity);}
}

创建contorller

package com.atguigu.gulimall.order.controller;import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderReturnReasonEntity;
import org.springframework.amqp.rabbit.connection.CorrelationData;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.UUID;@RestController
public class RabbitmqController {@AutowiredRabbitTemplate rabbitTemplate;@GetMapping("/sendMessage")public String sendMessage(@RequestParam(value = "num",defaultValue = "10") Integer num){for (int i = 0; i < 10; i++) {if (i % 2 == 0){// 测试返回实体类===>引入OrderReturnReasonEntityOrderReturnReasonEntity reasonEntity = new OrderReturnReasonEntity();reasonEntity.setId(1L);reasonEntity.setName("哈哈哈");rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", reasonEntity,new CorrelationData(UUID.randomUUID().toString()));}else {OrderEntity orderEntity = new OrderEntity();orderEntity.setOrderSn("12345678");rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", orderEntity,new CorrelationData(UUID.randomUUID().toString()));}}return "ok";}
}

结果(重载实现)==》不同参数类型对应到不同的方法,返回不同的值

9、消息确认机制-发送端确认

RabbitMQ消息确认机制:

保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍,为此引入确认机制
publisher confirmCallback 确认模式
publisher returnCallback 未投递到 queue 退回模式
consumer ack机制
 

1)、ConfirmCallback

spring.rabbitmq.publisher-confirms=true
在创建 connectionFactory 的时候设置 PublisherConfirms(true) 选项,开启confirmcallback 。
CorrelationData:用来表示当前消息唯一性。
消息只要被 broker 接收到就会执行 confirmCallback,如果是 cluster 模式,需要所有 broker 接收到才会调用 confirmCallback。
被 broker 接收到只能表示 message 已经到达服务器,并不能保证消息一定会被投递到目标 queue 里。所以需要用到接下来的 returnCallback 。


2)、ReturnCallback

  • spring.rabbitmq.publisher-returns=true
  • spring.rabbitmq.template.mandatory=true
    • confrim 模式只能保证消息到达 broker,不能保证消息准确投递到目标 queue 里。在有 些业务场景下,我们需要保证消息一定要投递到目标 queue 里,此时就需要用到return 退回模式。
    • 这样如果未能投递到目标 queue 里将调用 returnCallback ,可以记录下详细到投递数据,定期的巡检或者自动纠错都需要这些数据。
定制RabbitTemplate服务器收到消息就回调1、开启发送端确认1、spring.rabbitmq.publisher-confirms=true2、设置确认回调2、消息抵达队列就回调1、#开启发送端抵达队列确认spring.rabbitmq.publisher-returns=true#只要抵达队列,以异步发送优先回调我们这个returnConfirmspring.rabbitmq.template.mandatory=true2、设置确认回调ReturnCallback
3、消费端确认(保证每个消息被正确消费,此时才可以保证broker删除这个消息)

修改application.properties

#开启发送端确认
#spring.rabbitmq.publisher-confirms=true #老版本springboot配置写法
spring.rabbitmq.publisher-confirm-type=correlated #新版本springboot配置写法
#开启发送端抵达队列确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步发送优先回调我们这个returnConfirm
spring.rabbitmq.template.mandatory=true


添加“com.atguigu.gulimall.order.config.MyRabbitConfig”类,代码如下:

package com.atguigu.gulimall.order.config;import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;@Configuration
public class MyRabbitConfig {@AutowiredRabbitTemplate rabbitTemplate;/*** 使用JSON序列化机制,进行消息转换** @return*/@Beanpublic MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();}/*** 定制RabbitTemplate* 服务器收到消息就回调* 1、开启发送端确认*      1、spring.rabbitmq.publisher-confirms=true*      2、设置确认回调* 2、消息抵达队列就回调*      1、#开启发送端抵达队列确认*         spring.rabbitmq.publisher-returns=true*         #只要抵达队列,以异步发送优先回调我们这个returnConfirm*         spring.rabbitmq.template.mandatory=true*      2、设置确认回调ReturnCallback*  3、消费端确认(保证每个消息被正确消费,此时才可以保证broker删除这个消息)*/@PostConstruct   //MyRabbitConfig对象创建完以后,执行这个方法public void initRabbitTemplate(){//设置确认回调rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {/*** 只要消息抵达Broker就b = true* @param correlationData 当前消息的唯一关联数据(这个消息的唯一id)* @param b  消息是否成功收到* @param s 失败的原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean b, String s) {System.out.println("confirm...correlationData["+correlationData+"]==>b["+b+"]s==>["+s+"]");}});//设置消息没有抵达队列的回调rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {/*** 只要消息没有投递给指定的队列,就触发这个失败回调* @param message 投递失败的消息详细信息* @param i 回复的状态码* @param s 回复的文本内容* @param s1 当时这个消息发给哪个交换机* @param s2 当时这个消息用哪个路由键*/@Overridepublic void returnedMessage(Message message, int i, String s, String s1, String s2) {System.out.println("Fail Message["+message+"]==>i["+i+"]==>s["+s+"]==>s1["+s1+"]==>s2["+s2+"]");}});}
}

bug:启动发现出现了循环依赖现象

***************************
APPLICATION FAILED TO START
***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
|  myRabbitConfig (field private org.springframework.amqp.rabbit.core.RabbitTemplate com.atguigu.gulimall.order.config.MyRabbitConfig.rabbitTemplate)
↑     ↓
|  rabbitTemplate defined in class path resource [org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration$RabbitTemplateConfiguration.class]
↑     ↓
|  rabbitTemplateConfigurer defined in class path resource [org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration$RabbitTemplateConfiguration.class]
└─────┘

我们可以看到自定义的mq配置中,注入了一个 MessageConverter

再来看RabbitTemplateConfiguration也有一个 MessageConverter

于是乎形成了

MyRabbitConfig 需要 RabbitTemplate

RabbitTemplate 需要 MessageConverter

MyRabbitConfig 又定义了 MessageConverter

反反复复,就形成了循环依赖

解决方法:

只要把 自定义 MessageConverter 与自定义 RabbitTemplate 不写在同一个类MyRabbitConfig 就行了

添加“com.atguigu.gulimall.order.config.MyMessageConverterConfig”类,代码如下:

@Configuration
public class MyMessageConverterConfig {
/*** 使用JSON序列化机制,进行消息转换*/
@Bean
public MessageConverter messageConverter(){return new Jackson2JsonMessageConverter();
}

}
把MyRabbitConfig配置类中的消息转换配置删掉即可。

发送消息打印结果

confirm...correlationData[CorrelationData [id=17a39c15-d309-45a9-b93d-a0e67151a878]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=37d1b2dc-ddb9-40eb-933f-96729cb4cf36]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=4a86e085-6785-4c86-8b0f-22ac68b2d333]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=82026297-a94b-410c-a592-70f7709acad2]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=81ae0e70-156f-4afe-8f16-af16c1b9ff31]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=e97032ed-e460-474f-b518-e3bc472f3a6e]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=f88d5c09-c881-4193-b902-e3974895687e]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=84289b43-9025-4088-98b7-d7fb8f53355b]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=7cb6b25d-67e9-426f-9947-1cd6826f2dc4]]==>ack[true]s==>[null]
confirm...correlationData[CorrelationData [id=8cd46d86-2e73-4747-8192-e4d725173b02]]==>ack[true]s==>[null]

制造错误

打印

10、消息确认机制-消费端确认

Ack消息确认机制

消费者获取到消息,成功处理,可以回复Ack给Broker
basic.ack用于肯定确认;broker将移除此消息
basic.nack用于否定确认;可以指定broker是否丢弃此消息,可以批量
basic.reject用于否定确认;同上,但不能批量
默认自动ack,消息被消费者收到,就会从broker的queue中移除(重启立即删除队列)
queue无消费者,消息依然会被存储,直到消费者消费


消费者收到消息,默认会自动ack。但是如果无法确定此消息是否被处理完成,或者成功处理。我们可以开启手动ack模式
消息处理成功,ack(),接受下一个消息,此消息broker就会移除
消息处理失败,nack()/reject(),重新发送给其他人进行处理,或者容错处理后ack
消息一直没有调用 ack/nack 方法, broker 认为此消息正在被处理,不会投递给别人,此时客户端断开,消息不会被broker 移除,会投递给别人


消费端确认(保证每个消息被正确消费,此时才可以保证broker删除这个消息)

1、默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息

问题:

我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了。发生消息丢失

手动确认模式。只要我们没有明确告诉MQ,货物被签收,没有ACK,消息就一直unacked状态,即使Consumer宕机。消息不会丢失,会重新变为Ready,下一次有新的Consumer连接进来就发给他。

2、 1)、#手动确认收货(ack)

spring.rabbitmq.listener.simple.acknowledge-mode=manual

2)、channel.basicAck(deliveryTag,false);签收;业务成功完成就应该签收

channel.basicNack(deliveryTag,false,true);拒签:业务失败,拒签

添加application.properties

#手动确认收货(ack)
spring.rabbitmq.listener.simple.acknowledge-mode=manual
    @RabbitHandlerpublic void receiverMessage(Message message, OrderReturnApplyEntity content, Channel channel) {// 消息体byte[] body = message.getBody();// 消息头属性信息MessageProperties properties = message.getMessageProperties();System.out.println("接收到消息...内容:" + content);
//        Thread.sleep(3000);System.out.println("消息处理完成=》" + content.getReturnName());// channel内按顺序自增的long deliveryTag = message.getMessageProperties().getDeliveryTag();System.out.println("deliveryTag:" + deliveryTag);// 签收货物,非批量模式try {if (deliveryTag % 2 == 0) {// 收货channel.basicAck(deliveryTag, false);System.out.println("签收了货物。。。" + deliveryTag);} else {// 退货requeue=false 丢弃  requeue=true发挥服务器,服务器重新入队。channel.basicNack(deliveryTag, false, true);System.out.println("没有签收货物..." + deliveryTag);}} catch (Exception e) {//网络中断}}
接收到消息...内容:OrderReturnApplyEntity(id=1, orderId=null, skuId=null, orderSn=null, createTime=Mon May 20 16:31:49 CST 2024, memberUsername=null, returnAmount=null, returnName=哈哈哈0, returnPhone=null, status=null, handleTime=null, skuImg=null, skuName=null, skuBrand=null, skuAttrsVals=null, skuCount=null, skuPrice=null, skuRealPrice=null, reason=null, description述=null, descPics=null, handleNote=null, handleMan=null, receiveMan=null, receiveTime=null, receiveNote=null, receivePhone=null, companyAddress=null)
消息处理完成=》哈哈哈0
deliveryTag:1
没有签收货物...1
接收到消息...内容:OrderReturnApplyEntity(id=1, orderId=null, skuId=null, orderSn=null, createTime=Mon May 20 16:31:49 CST 2024, memberUsername=null, returnAmount=null, returnName=哈哈哈2, returnPhone=null, status=null, handleTime=null, skuImg=null, skuName=null, skuBrand=null, skuAttrsVals=null, skuCount=null, skuPrice=null, skuRealPrice=null, reason=null, description述=null, descPics=null, handleNote=null, handleMan=null, receiveMan=null, receiveTime=null, receiveNote=null, receivePhone=null, companyAddress=null)
消息处理完成=》哈哈哈2
deliveryTag:2
签收了货物。。。2
接收到消息...内容:OrderReturnApplyEntity(id=1, orderId=null, skuId=null, orderSn=null, createTime=Mon May 20 16:31:49 CST 2024, memberUsername=null, returnAmount=null, returnName=哈哈哈4, returnPhone=null, status=null, handleTime=null, skuImg=null, skuName=null, skuBrand=null, skuAttrsVals=null, skuCount=null, skuPrice=null, skuRealPrice=null, reason=null, description述=null, descPics=null, handleNote=null, handleMan=null, receiveMan=null, receiveTime=null, receiveNote=null, receivePhone=null, companyAddress=null)
消息处理完成=》哈哈哈4
deliveryTag:3
没有签收货物...3
接收到消息...内容:OrderReturnApplyEntity(id=1, orderId=null, skuId=null, orderSn=null, createTime=Mon May 20 16:31:49 CST 2024, memberUsername=null, returnAmount=null, returnName=哈哈哈6, returnPhone=null, status=null, handleTime=null, skuImg=null, skuName=null, skuBrand=null, skuAttrsVals=null, skuCount=null, skuPrice=null, skuRealPrice=null, reason=null, description述=null, descPics=null, handleNote=null, handleMan=null, receiveMan=null, receiveTime=null, receiveNote=null, receivePhone=null, companyAddress=null)
消息处理完成=》哈哈哈6
deliveryTag:4
签收了货物。。。4
接收到消息...内容:OrderReturnApplyEntity(id=1, orderId=null, skuId=null, orderSn=null, createTime=Mon May 20 16:31:49 CST 2024, memberUsername=null, returnAmount=null, returnName=哈哈哈8, returnPhone=null, status=null, handleTime=null, skuImg=null, skuName=null, skuBrand=null, skuAttrsVals=null, skuCount=null, skuPrice=null, skuRealPrice=null, reason=null, description述=null, descPics=null, handleNote=null, handleMan=null, receiveMan=null, receiveTime=null, receiveNote=null, receivePhone=null, companyAddress=null)
消息处理完成=》哈哈哈8
deliveryTag:5
没有签收货物...5

相关文章:

谷粒商城-消息队列Rabbitmq

RabbitMq参考文档 在谷粒商城项目中使用消息队列主要有以下几个重要原因&#xff1a; 异步处理提高性能 场景示例&#xff1a;在订单系统中&#xff0c;当用户提交订单后&#xff0c;系统需要完成多个操作&#xff0c;如更新库存、生成订单记录、发送订单通知等。如果这些操作…...

python-爬虫入门指南

前言&#xff1a;由于个人负责的运维组&#xff0c;其中有个同事每回在某个项目发版更新后&#xff0c;需手动在k8s容器平台web界面上复制出几百个微服务的名称以及镜像版本等信息&#xff0c;用来更新微服务清单&#xff0c;个人决定抽时间写个爬虫脚本自动完成手动执行的任务…...

力扣—53. 最大子数组和

53. 最大子数组和 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4…...

STM32H7开发笔记(2)——H7外设之多路定时器中断

STM32H7开发笔记&#xff08;2&#xff09;——H7外设之多路定时器中断 文章目录 STM32H7开发笔记&#xff08;2&#xff09;——H7外设之多路定时器中断0.引言1.CubeMX配置2.软件编写 0.引言 本文PC端采用Win11STM32CubeMX4.1.0.0Keil5.24.2的配置&#xff0c;硬件使用STM32H…...

kafka如何知道哪个消费者消费哪个分区?

在Kafka中&#xff0c;消费者和分区之间的分配是通过一个称为“消费者组协调器”&#xff08;Consumer Group Coordinator&#xff09;的组件来管理的。 以下是Kafka如何确定哪个消费者消费哪个分区的步骤&#xff1a; 消费者加入消费者组&#xff1a; 当消费者启动时&#xf…...

Dockerfile构建报错【ERROR: failed to solve: process】的解决办法

报错信息如下 ERROR: failed to solve: process “/bin/sh -c yarn install” did not complete successfully: exit code: 1 解决 从阿里云等镜像站点下载CentOS-7.repo文件 ‌下载CentOS-7.repo文件‌&#xff1a;可以从阿里云等镜像站点下载CentOS-7.repo文件&#xff0c;…...

html渲染优先级

在前端开发中&#xff0c;优先布局是指在设计和构建页面时&#xff0c;将页面的各个部分按照其重要性和优先级进行排序&#xff0c;并依次进行布局和开发。这种方法可以帮助开发团队在项目初期就确定页面结构的核心部分&#xff0c;从而更好地掌控项目的整体进度和优先级。且确…...

AIX下crs-5005 ip address is aready in use in the network的解决办法

某业务生产系统中&#xff0c;三节点的rac数据库中3号节点因故障停机后&#xff0c;进行crs的重启。重启完成后&#xff0c;发现数据库的监听未起来&#xff0c;启动的过程中并提示crs-5005错误。 一、问题过程 查看监听&#xff0c;发现监听no service ywdb03/oracle/grid/c…...

Docker--通过Docker容器创建一个Web服务器

Web服务器 Web服务器&#xff0c;一般指网站服务器&#xff0c;是驻留于因特网上某种类型计算机的程序。 Web服务器可以向浏览器等Web客户端提供文档&#xff0c;也可以放置网站文件以供全世界浏览&#xff0c;或放置数据文件以供全世界下载。 Web服务器的主要功能是提供网上…...

C#里怎么样自己实现10进制转换为二进制?

C#里怎么样自己实现10进制转换为二进制&#xff1f; 很多情况下&#xff0c;我们都是采用C#里类库来格式化输出二进制数。 如果有人要你自己手写一个10进制数转换为二进制数&#xff0c;并格式化输出&#xff0c; 就可以采用本文里的方法。 这里采用求模和除法来实现的。 下…...

sql 查询语句:将终端数据形式转换成insert语句

文本转换&#xff1a;sql 查询语句&#xff1a;将终端数据形式转换成insert语句 如上&#xff0c;写过后端的都知道&#xff0c;从生产或其他地方拿到的数据&#xff0c;有可能会是图一&#xff1b;但实际上&#xff0c;我们需要图二的数据&#xff1b; 不废话&#xff0c;直接…...

Spring Boot 应用开发:构建高效、可扩展的 Java 微服务

以下是一个简单的 Spring Boot 小项目示例&#xff0c;该项目是一个基于 Spring Boot 的博客系统后端部分。这个项目将展示如何使用 Spring Boot 框架来创建一个基本的 RESTful API 服务&#xff0c;以管理博客文章。 项目结构 spring-boot-blog ├── src │ ├── main…...

【Linux】安装 openssh-server 并打开 ssh 服务(Ubuntu 22.04)

引言 openssh-server是OpenSSH套件的一部分&#xff0c;它是SSH协议的开源实现。SSH&#xff0c;全称为Secure Shell&#xff0c;是一种网络协议&#xff0c;用于安全地在不安全的网络环境中执行远程命令和传输数据。 配置步骤 在Ubuntu系统中安装openssh-server。 sudo apt…...

开源IM,为你的项目增加聊天功能

现在大多数APP&#xff0c;或多或少都会涉及到聊天功能&#xff0c;而大部分APP的选择则是接入TIM等三方IM服务&#xff0c;但是&#xff0c;这种方式对于大多数刚刚起步的APP来说&#xff0c;费用是非常昂贵的&#xff0c;基本上每月都有好几百的支出&#xff0c;并且&#xf…...

跨平台应用开发框架(1)----Qt(组件篇)

目录 1.Qt 1.Qt 的主要特点 2.Qt的使用场景 3.Qt的版本 2.QtSDK 1.Qt SDK 的组成部分 2.安装 Qt SDK 3.Qt SDK 的优势 3.Qt初识 1.快速上手 widget.cpp mian.cpp widget.h Helloworld.pro 2.对象树 3.坐标系 4.信号和槽 1. 信号和槽的基本概念 2. 信号和槽的…...

Java基础 设计模式——针对实习面试

目录 Java基础 设计模式单例模式工厂模式观察者模式策略模式装饰器模式其他设计模式 Java基础 设计模式 单例模式 单例模式&#xff08;Singleton Pattern&#xff09; 定义&#xff1a;确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。适用场景&…...

C++ —— 以真我之名 如飞花般绚丽 - 智能指针

目录 1. RAII和智能指针的设计思路 2. C标准库智能指针的使用 2.1 auto_ptr 2.2 unique_ptr 2.3 简单模拟实现auto_ptr和unique_ptr的核心功能 2.4 shared_ptr 2.4.1 make_shared 2.5 weak_ptr 2.6 shared_ptr的缺陷&#xff1a;循环引用问题 3. shared_ptr 和 unique_…...

k8s中部署filebeat进行日志监听并发送到es中

注意事项 1. 需要将namespace修改为自己项目中的命名空间 2. es换成对应的地址 3. filebeat-inputs中的两个配置&#xff08;根据需要用任意一个就可以&#xff09; 3.1 第一个配置是监听docker日志&#xff0c;由于系统日志太多所以这里只监听项目部署命名空间下的内容 -…...

HTTP Accept用法介绍

一、HTTP Accept是什么 HTTP协议是一个客户端和服务器之间进行通信的标准协议&#xff0c;它定义了发送请求和响应的格式。而HTTP Accept是HTTP协议中的一个HTTP头部&#xff0c;用于告诉服务器请求方所期望的响应格式。这些格式可以是媒体类型、字符集、语言等信息。 HTTP A…...

软件工程设计模式--结构型设计模式

设计模式的核心思想 &#xff1a;  广义——软件设计模式是可解决一类软件问题并能重 复使用的软件设计方案  狭义——设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述&#xff0c;是在类和对象的层次描述的可重复使用的软件设计问题的解决方…...

HTML5好看的音乐播放器多种风格(附源码)

文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&…...

【FPGA】Verilog:利用 4 个串行输入- 串行输出的 D 触发器实现 Shift_register

0x00 什么是寄存器 寄存器(Register)是顺序逻辑电路中使用的基本组成部分之一。寄存器用于在数字系统中存储和处理数据。寄存器通常由位(bit)构成,每个位可以存储一个0或1的值。通过寄存器,可以设计出计数器、加法器等各种数据处理电路。 0x01 寄存器的种类 基于 D 触发…...

快速排序算法-C语言

第一步&#xff1a;实现分区函数 根据题目中的“快速排序”&#xff0c;我们需要实现一个分区函数&#xff0c;这个功能的实现&#xff1a; 设定基准值 pivot。使用两个指针 low 和 high&#xff0c;分别从数组的两端向中间移动&#xff0c;进行元素交换。 int part(int A[]…...

SuperMap Objects组件式GIS开发技术浅析

引言 随着GIS应用领域的扩展&#xff0c;GIS开发工作日显重要。一般地&#xff0c;从平台和模式上划分&#xff0c;GIS二次开发主要有三种实现方式&#xff1a;独立开发、单纯二次开发和集成二次开发。上述的GIS应用开发方式各有利弊&#xff0c;其中集成二次开发既可以充分利…...

极简开源Windows桌面定时提醒休息python程序

当我们长期在电脑面前坐太久后&#xff0c;会产生一系列健康风险&#xff0c;包括干眼症&#xff0c;颈椎&#xff0c;腰椎&#xff0c;肌肉僵硬等等。解决方案是在一定的时间间隔内我们需要have a break, 远眺可以缓解干眼症等眼部症状&#xff0c;站起来走动两步&#xff0c;…...

SpringBoot源码解析(五):准备应用环境

SpringBoot源码系列文章 SpringBoot源码解析(一)&#xff1a;SpringApplication构造方法 SpringBoot源码解析(二)&#xff1a;引导上下文DefaultBootstrapContext SpringBoot源码解析(三)&#xff1a;启动开始阶段 SpringBoot源码解析(四)&#xff1a;解析应用参数args Sp…...

JAVA笔记 | 策略模式+枚举Enum简单实现策略模式(可直接套用)

本篇为更为简单的策略模式应用&#xff0c;使用枚举来进行策略分配 上一篇(链接如下)更像是策略工厂模式来分配策略 JAVA笔记 | 实际上用到的策略模式(可直接套用)-CSDN博客 先创建策略相关类 //策略类 public interface PetStrategy {/*** 执行动作 - 跑RUN*/String run(Str…...

SpringBoot集成 Jasypt 实现数据源连接信息进行加密

SpringBoot集成 Jasypt 实现数据源连接信息进行加密 在实际项目中&#xff0c;敏感信息&#xff08;如数据库连接的 URL、用户名、密码等&#xff09;直接暴露在配置文件中可能导致安全隐患。为了解决这一问题&#xff0c;可以使用 Jasypt 来加密敏感信息&#xff0c;并在运行…...

大数据新视界 -- Hive 数据桶原理:均匀分布数据的智慧(上)(9/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

优化Docker镜像:提升部署效率与降低资源消耗

目录 1. 最小化镜像层 2. 使用轻量级基础镜像 3. 多阶段构建 4. 清理不必要的文件和依赖 5. 使用.dockerignore文件 6. 压缩和优化文件系统 7. 外部化配置和数据 8. 限制容器资源 9. 定期清理未使用的镜像和容器 结论 在云计算和微服务架构的浪潮中&#xff0c;Docke…...

strupr(arr);模拟实现(c基础)

hi , I am 36 适合对象c语言初学者 strupr(arr);函数是把arr数组变为大写字母&#xff0c;并返回arr 介绍一下strupr(arr)&#xff1b;(c基础&#xff09;-CSDN博客 现在进行My__strupr(arr);模拟实现 #include<stdio.h>//My__strupr(arr); //返回值为arr(地址),于是…...

skywalking es查询整理

索引介绍 sw_records-all 这个索引用于存储所有的采样记录&#xff0c;包括但不限于慢SQL查询、Agent分析得到的数据等。这些记录数据包括Traces、Logs、TopN采样语句和告警信息。它们被用于性能分析和故障排查&#xff0c;帮助开发者和运维团队理解服务的行为和性能特点。 …...

AI时代的软件工程:迎接LLM-DevOps的新纪元

在科技日新月异的今天&#xff0c;GPT的问世无疑为各行各业带来了一场深刻的变革&#xff0c;而软件工程领域更是首当其冲&#xff0c;正式迈入了软件工程3.0的新纪元。2024年&#xff0c;作为软件工程3.0的元年&#xff0c;伴随着软件工程3.0宣言的震撼发布&#xff0c;一个全…...

【机器学习】——卷积与循环的交响曲:神经网络模型在现代科技中的协奏

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…...

详解Servlet的使用

目录 Servlet 定义 动态页面 vs 静态页面 主要功能 Servlet的使用 创建Maven项目 引入依赖 创建目录 编写代码 打war包 部署程序 验证程序 Smart Tomcat 安装Smart Tomcat 配置Smart Tomcat插件 启动Tomcat 访问页面 路径对应关系 Servlet运行原理 Tomcat的…...

使用Java代码操作Kafka(五):Kafka消费 offset API,包含指定 Offset 消费以及指定时间消费

文章目录 1、指定 Offset 消费2、指定时间消费 1、指定 Offset 消费 auto.offset.reset earliest | latest | none 默认是 latest &#xff08;1&#xff09;earliest&#xff1a;自动将偏移量重置为最早的偏移量&#xff0c;–from-beginning &#xff08;2&#xff09;lates…...

MAC 怎么终端怎么退出和进入Anaconda环境

mac安装完anaconda 后&#xff0c;命令行窗口默认使用conda的&#xff0c;取消默认&#xff0c;用以下一行代码在命令行运行即可&#xff0c;重启终端&#xff1a; conda config --set auto_activate_base false # 将false改为true设置默认环境为conda进入conda环境&#xff…...

如何在 .gitignore 中仅保留特定文件:以忽略文件夹中的所有文件为例

在日常的开发工作中&#xff0c;使用 Git 来管理项目是不可或缺的一部分。项目中的某些文件夹可能包含大量的临时文件、生成文件或不需要版本控制的文件。在这种情况下&#xff0c;我们通常会使用 .gitignore 文件来忽略这些文件夹。然而&#xff0c;有时我们可能希望在忽略整个…...

USRP:B205mini-i

USRP B205mini-i B205mini-i都是采用工业级的FPGA芯片(-I表示industrial-grade)&#xff0c;所以价格贵。 这个工业级会让工作温度从原来 0 – 45 C 变为 -40 – 75 C. 温度的扩宽&#xff0c;会让工作的稳定性变好。但是前提是你需要配合NI的外壳才行&#xff0c;你如果只买一…...

Oracle SQL优化②——访问路径

前言 访问路径指的就是通过哪种扫描方式获取数据&#xff0c;比如全表扫描、索引扫描或者直接通过ROWID获取数据。想要完成SQL优化&#xff0c;就必须深入理解各种访问路径。本文章详细介绍常见的访问路径。 一.常见访问路径 1.TABLE ACCESS FULL 表示全表扫描&#xff0c;…...

k8s1.30.0高可用集群部署

负载均衡 nginx负载均衡 两台nginx负载均衡 vim /etc/nginx/nginx.conf stream {upstream kube-apiserver {server 192.168.0.11:6443 max_fails3 fail_timeout30s;#server 192.168.0.12:6443 max_fails3 fail_timeout30s;#server 192.168.0.13:6443 max_fails3…...

Jenkins的环境部署

day22 回顾 Jenkins 简介 官网Jenkins Jenkins Build great things at any scale The leading open source automation server, Jenkins provides hundreds of plugins to support building, deploying and automating any project. 用来构建一切 其实就是用Java写的一个项目…...

Android开发教程案例源码分享-匹配动画多个头像飘动效果

Android开发教程案例源码分享-匹配动画多个头像飘动效果 匹配往往出现多个头像飘动&#xff0c;吸引人点击&#xff0c;有时出现的位置还不固定 一、思路&#xff1a; 用MotionLayout 二、效果图&#xff1a; 看视频更直观点&#xff1a; Android开发教程案例源码分享-匹配…...

微信分账系统供应链分润微信支付 (亲测源码)

搭建环境&#xff1a;nginxphp7.2mysql5.7 1.上传源码到网站根目录并解压 2.导入数据库文件到数据库 3.修改数据库链接文件/.env 4.设置运行目录为/public 5.伪静态设置成tp 6.后台地址&#xff1a;域名/zh9025.php 源码下载&#xff1a;https://download.csdn.net/down…...

C#里怎么样使用BinaryReader和BinaryWriter类?

C#里怎么样使用BinaryReader和BinaryWriter类? 二进制读取,有时候就比较有用。 比如在数据序列化到文件里,再从文件里读取出来。因为写入文件的类型有很多种,比如int/bool/long/byte/bytes等等。 又比如在串口通讯,或者网络通讯里,也需要把数据类型序列化到缓冲区,然后…...

k8s1.31版本最新版本集群使用容器镜像仓库Harbor

虚拟机 rocky9.4 linux master node01 node02 已部署k8s集群版本 1.31 方法 一 使用容器部署harbor (1) wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo yum -y install docker-ce systemctl enable docker…...

【大数据学习 | Spark-Core】Spark提交及运行流程

spark的集群运行结构 我们要选择第一种使用方式 命令组成结构 spark-submit [选项] jar包 参数 standalone集群能够使用的选项。 --master MASTER_URL #集群地址 --class class_name #jar包中的类 --executor-memory MEM #executor的内存 --executor-cores NUM # executor的…...

Sickos1.1 详细靶机思路 实操笔记

Sickos1.1 详细靶机思路 实操笔记 免责声明 本博客提供的所有信息仅供学习和研究目的&#xff0c;旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动&#xff0c;您将独自承担…...

Python 获取微博用户信息及作品(完整版)

在当今的社交媒体时代&#xff0c;微博作为一个热门的社交平台&#xff0c;蕴含着海量的用户信息和丰富多样的内容。今天&#xff0c;我将带大家深入了解一段 Python 代码&#xff0c;它能够帮助我们获取微博用户的基本信息以及下载其微博中的相关素材&#xff0c;比如图片等。…...

使用element-plus el-table中使用el-image层级冲突table表格会覆盖预览的图片等问题

在日常开发项目中 使用element-plus 中表格中使用 el-image的点击图片出现图片预览 会出现以下问题 表格一行会覆盖预览的图片 鼠标滑过也会显示表格 el-image 的预览层级和表格的层级冲突导致的。 解决方法&#xff1a;有两种一种是直接使用样式穿透 第二种推荐方法 使用官网推…...