Linux Ubuntu 安装配置RabbitMQ,springboot使用RabbitMQ
rabbit-Ubuntu
一篇文章学会RabbitMQ
在Ubuntu上查看RabbitMQ状态可以通过多种方式进行,包括使用命令行工具和Web管理界面。以下是一些常用的方法:
1-使用systemctl
命令:
sudo systemctl start rabbitmq-server sudo systemctl status rabbitmq-server
-
2-使用
service
命令-
启动服务:同样地,如果RabbitMQ服务尚未启动,可以使用以下命令启动服务
sudo service rabbitmq-server start sudo service rabbitmq-server status
-
查看状态:使用
service
命令查看RabbitMQ服务的状态[^2^][^3^]。如果服务正在运行,你会看到“Active: active (running)”的信息:
-
-
使用
rabbitmqctl
命令-
查看状态:如果你已经安装了RabbitMQ的管理工具(如
rabbitmq-server
),你可以直接使用rabbitmqctl
命令来查看RabbitMQ的状态[^4^]。但请注意,这个命令在某些情况下可能需要在RabbitMQ的安装目录或特定目录下执行:rabbitmqctl status
-
在某些情况下,你可能需要进入RabbitMQ的安装目录(如
/opt/rabbitmq/sbin
或/usr/lib/rabbitmq/bin
)后才能执行rabbitmqctl
命令[^1^]。例如:cd /opt/rabbitmq/sbin ./rabbitmqctl status
-
在Ubuntu上安装RabbitMQ
在Ubuntu上安装RabbitMQ的步骤包括更新系统、安装Erlang环境、添加GPG密钥、安装RabbitMQ、启动服务、启用管理插件以及创建用户等。以下是详细的步骤:
-
更新系统:确保你的Ubuntu系统是最新的,以避免由于软件包过时导致的兼容性问题。打开终端并运行以下命令来更新系统的软件包列表
-
sudo apt update sudo apt upgrade -y
-
安装Erlang环境:RabbitMQ是基于Erlang语言开发的,因此需要先安装Erlang运行环境。执行以下命令来安装Erlang
sudo apt install -y curl gnupg curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/rabbitmq-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/rabbitmq-archive-keyring.gpg] http://dl.bintray.com/rabbitmq-erlang/debian bionic erlang" | sudo tee /etc/apt/sources.list.d/rabbitmq-erlang.list sudo apt update sudo apt install -y erlang
-
添加RabbitMQ的GPG密钥:为了能够从官方源下载RabbitMQ,需要添加其GPG密钥
curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/rabbitmq-archive-keyring.gpg
-
安装RabbitMQ:现在可以安装RabbitMQ服务器了。执行以下命令来添加RabbitMQ的APT源并安装RabbitMQ[^2^][^3^]:
echo "deb [signed-by=/usr/share/keyrings/rabbitmq-archive-keyring.gpg] http://dl.bintray.com/rabbitmq/debian focal main" | sudo tee /etc/apt/sources.list.d/rabbitmq.list sudo apt update sudo apt install -y rabbitmq-server
-
启动RabbitMQ服务:安装完成后,启动RabbitMQ服务并设置开机自启
sudo systemctl start rabbitmq-server sudo systemctl enable rabbitmq-server
-
启用管理插件:RabbitMQ提供了一个Web管理界面,可以通过启用管理插件来访问[^2^][^3^]。
sudo rabbitmq-plugins enable rabbitmq_management
-
创建用户:为了安全起见,建议创建一个新的用户并为其分配权限
sudo rabbitmqctl add_user myuser mypassword sudo rabbitmqctl set_user_tags myuser administrator sudo rabbitmqctl set_permissions -p / myuser ".*" ".*" ".*"
-
访问管理界面:默认情况下,RabbitMQ的管理界面运行在15672端口。打开浏览器,访问http://<your-server-ip>:15672,使用刚刚创建的用户名和密码登录[^2^]。
http://47.97.22.101:15672
重点:服务器上添加rabbitMQ账户
原因 RabbitMQ默认的guest账号密码只能使用localhost登录,服务器上的RabbitMQ只能在电脑浏览器登录,必须新建账户。
1、 创建一个新的用户 sudo rabbitmqctl add_user <username> <password>
2、 设置管理角色 sudo rabbitmqctl set_user_tags <username> administrator 3、 设置用户权限 sudo rabbitmqctl set_permissions -p / <username> ".*" ".*" ".*" 4、 确认权限设置 sudo rabbitmqctl list_user_permissions andy
5、 添加别的虚拟主机(可选) sudo rabbitmqctl add_vhost <vhost_name>
6、 设置权限 sudo rabbitmqctl set_permissions -p <vhost_name> andy ".*" ".*" ".*"
7、 确认用户标签 sudo rabbitmqctl list_user_tags <username>
8、 禁用guest账户(可选) sudo rabbitmqctl delete_user guest
RabbitMQ 命令
http://localhost:15672/
登陆rabbitmq:成功启动服务后,使用以下链接访问主页,则会进入到登陆页面 # 访问rabbitmq主页 http://localhost:15672/
注意:默认是账户名和密码都是:“guest”,表示来宾客户。 由于账号guest具有所有的操作权限,且是默认账号,出于安全因素的考虑,guest用户只能通过localhost登陆使用, 并建议修改guest用户的密码以及新建其他账号管理使用rabbitmq。
服务器上的RabbitMQ,在本地浏览器中无法登录
--------------------------------状态
# 查询rabbitmq的状态 sudo rabbitmqctl status
# 查询rabbitmq的进程 ps -ef | grep rabbitmq ===linux sudo service rabbitmq-server status ==win and mac rabbitmqctl status
# 杀掉rabbitmq进程 ps -ef | grep rabbitmq | grep -v grep | awk '{print $2}' | xargs kill -9
--------------------------------启动
# 启动服务 sudo rabbitmq-server # 后台启动命令 sudo rabbitmq-server -detached
---mac---
brew services start rabbitmq brew services info
--- # 启动插件 sudo rabbitmq-plugins enable rabbitmq_management
--------------------------------关闭
sudo rabbitmqctl stop # 查找rabbitmq在哪 find / -name 'rabbitmq' -type d # rabbitmq内存限制 # 系数计算,假设机器内存32g,以下命令限制的最高内存为,32*0.06=1.92g rabbitmqctl set_vm_memory_high_watermark 0.06 # rabbitmq添加消息日志记录 rabbitmq-plugins enable rabbitmq_tracing
springboot使用RabbitMQ
1-pom.xml
<!--Spring AMQP依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2-application.yml
rabbitmq:host: 47.97.22.101port: 15672virtual-host: /andyusername: andypassword: andypublisher-confirm-type: correlated# 消息发送到交换机确认publisher-returns: true#消息发送到队列确认
# publisher-confirms: true
# Deprecated configuration property 'spring.rabbitmq.publisher-confirms'弃用的配置属性
3-登录客户端
添加对列,一定要提前添加
添加用户 
添加交换机
4-消费者
/*** 4-创建消息消费者* 创建一个消息消费者类,用于接收并处理来自RabbitMQ队列的订单消息:*/
@Slf4j
@Component
public class Consumer
{private static final int MAX_RETRIES = 3; // 最大重试次数private final static String QUEUE_NAME = "orderQueue";@AutowiredIXOrdersService xOrdersService;@AutowiredRabbitTemplate rabbitTemplate;@RabbitListener(queues = QUEUE_NAME)public void handleOrder( XOrders xOrders, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel ) throws IOException{// 将message转为string// String messageStr = new String( xOrders.getBody(), StandardCharsets.UTF_8 );// 生产者手动序列化,消费者使用相同 json 反序列化// Order order = new ObjectMapper().readValue( message.getBody(), Order.class );// Order order = JSONUtil.toBean( messageStr, Order.class );//处理订单逻辑log.info( "--------Consumer-------rabbitMQ received message:" + xOrders );try{// 处理订单创建xOrdersService.insertXOrders( xOrders );channel.basicAck( deliveryTag, false );//消息确认}catch ( Exception e ){// 失败处理(例如重试机制)log.error( "--------Consumer-------rabbitMQ received message error:" + e.getMessage() );// 增加重试次数,并重新发送消息到队列int retryCount = xOrders.getRetryCount() != null ? xOrders.getRetryCount() : 0;if ( retryCount < MAX_RETRIES ){xOrders.setRetryCount( retryCount + 1 );sendToQueue( xOrders );//重新发送消息}else{// 重试次数超过限制,将订单发送到死信队列sendToDeadLetterQueue( xOrders );}channel.basicNack( deliveryTag, false, false ); // 拒绝消息, 将消息重新放回队列}}private void sendToQueue( XOrders orderMessage ){rabbitTemplate.convertAndSend( "order.exchange", "order.routing.key", orderMessage );}private void sendToDeadLetterQueue( XOrders orderMessage ){rabbitTemplate.convertAndSend( "order.dlx.exchange", "order.dlx.routing.key", orderMessage );}// public static void main( String[] args ) throws IOException, TimeoutException// {// // 创建服务器的连接// ConnectionFactory factory = new ConnectionFactory();// factory.setHost( "localhost" );// Connection connection = factory.newConnection();// Channel channel = connection.createChannel();// channel.queueDeclare( QUEUE_NAME, false, false, false, null );// //用于处理消费者接收信息的回调函数// DeliverCallback deliverCallback = new DeliverCallback()// {// @Override// public void handle( String consumerTag, Delivery delivery ) throws IOException// {// String s = new String( delivery.getBody(), StandardCharsets.UTF_8 );// System.out.println( consumerTag + " [x] Received '" + s + "'" );// }// };// /**// * QUEUE_NAME`:表示要消费的队列名。// `true`:表示自动确认消息,当消费者接收到一条消息后就会自动向消息队列发送确认消息,告诉消息队列这条消息已经被消费处理完成。// `deliverCallback`:表示接收到消息后的处理逻辑,将在 `handle` 方法中执行。// `consumerTag -> {}`:表示用于接收消费者标识的回调函数。在上面的 `basicCancel` 方法中同样需要传入该回调函数中的消费者标识才能成功取消消费者的订阅// */// channel.basicConsume( QUEUE_NAME, true, deliverCallback, consumerTag -> {// System.out.println( consumerTag );// } );//// }
}
5-生产者
/**** 3-创建消息生产者* 创建一个消息生产者类,用于发送订单消息到RabbitMQ队列:*/
@Slf4j
@Component
public class Producer
{@Autowiredprivate AmqpTemplate amqpTemplate;@Autowiredprivate RabbitTemplate rabbitTemplate;//对列名private final static String QUEUE_NAME = "orderQueue";public void sendOrder( XOrders xOrders ) throws JsonProcessingException{log.info( "---------------Producer-----------rabbitMQ convertAndSend----- " );//amqpTemplate.convertAndSend(【交换机,有默认值】,【队列名称】 QUEUE_NAME, 【信息】order );// 生产者手动序列化,消费者使用相同 json 反序列化-- 1-实体实现serializable接口,2-序列化String jsonStr = JSONUtil.toJsonStr( xOrders );amqpTemplate.convertAndSend( QUEUE_NAME, jsonStr );// rabbitTemplate.convertAndSend("order.exchange", "order.routing.key", orderMessage);rabbitTemplate.convertAndSend( "order.exchange", "order.routing.key", xOrders );}public static void main( String[] args ) throws IOException, TimeoutException{//创建一个连接工厂ConnectionFactory factory = new ConnectionFactory();//设置连接工厂的参数factory.setHost( "localhost" );// factory.setPort( 5672 );// factory.setUsername( "guest" );// factory.setPassword( "guest" );//创建一个连接Connection connection = factory.newConnection();/**在 RabbitMQ 中,Channel 指的是在连接(Connection)上创建的一个逻辑通道,用来进行发送和接收消息的操作。每一个 Channel 都会拥有独立的 ID,可以根据这个 ID 与 RabbitMQ 服务器进行通信。通过 Channel,应用程序可以进行以下操作:声明队列(Queue)和交换器(Exchange)。将队列绑定到交换器上。发布消息到指定的交换器上。消费指定队列上的消息。在 RabbitMQ 中,每个 Connection 都支持多个 Channel,应用程序可以根据自己的需求创建多个 Channel,从而实现并发和优化网络带宽的利用。但是需要注意,对于一个 Connection 可能存在的并发限制,在应用程序中需要合理控制 Channel 的数量。*///创建一个通道Channel channel = connection.createChannel();channel.queueDeclare( QUEUE_NAME, false, false, false, null );/*** 这段代码是通过 RabbitMQ 的 Java 客户端创建一个名为 `QUEUE_NAME` 的队列。其中,代码参数的含义如下:* `QUEUE_NAME`:队列名,即要创建的队列的名称。* `false`:指定是否为持久化队列。设置为 `false` 表示创建的队列在 RabbitMQ 服务器重启后会被删除。* `false`:指定是否为排他队列。设置为 `false` 表示队列可以被其他连接访问。* `false`:指定队列是否应该自动删除。设置为 `false` 表示当没有任何消费者使用该队列时,该队列不会自动删除。* `null`:指定队列的属性。设置为 `null` 表示不需要为队列设置任何属性。* 当该方法被成功执行后,就可以使用 `channel.basicPublish()` 方法向队列发送消息,并使用 `channel.basicConsume()` 方法从队列中获取消息。该队列的状态信息也可以通过 `com.rabbitmq.client.AMQP.Queue.DeclareOk` 对象来进行监控。**/String message = "Hello World!";channel.basicPublish( "", QUEUE_NAME, null, message.getBytes( StandardCharsets.UTF_8 ) );System.out.println( " [x] Sent '" + message + "'" );}
}
6-配置文件
/***@Description 配置RabbitMQ*@Author Andy Fan*@Date 2024/12/4 13:19*@ClassName RabbitConfig* 补偿机制和重试策略* 1-重试机制(RabbitMQ的死信队列)* 为了保证消息不丢失,并且在处理失败时能够进行重试,可以使用RabbitMQ的死信队列(DLX, Dead Letter Exchange)机制。* 在RabbitMQ中,死信队列用于存储无法成功消费的消息。在消息消费者处理失败时,可以将消息发送到死信队列,待后续处理。**/
@Configuration
public class RabbitConfig
{//声明需要使用的交换机/路由Key/队列的名称public static final String DEFAULT_EXCHANGE = "exchange";public static final String DEFAULT_ROUTE = "route";public static final String DEFAULT_QUEUE = "queue";// 声明队列,@Beanpublic Queue orderQueue(){return QueueBuilder.durable( "order.queue" ).deadLetterExchange( "order.dlx.exchange" ) // 设置死信交换机.deadLetterRoutingKey( "order.dlx.routing.key" ) // 设置死信路由.build();}// 声明交换机,@Beanpublic DirectExchange orderExchange(){return new DirectExchange( "order.exchange" );}@Beanpublic DirectExchange orderDlxExchange(){return new DirectExchange( "order.dlx.exchange" );}//声明路由Key(交换机和队列的关系),需要几个声明几个,这里就一个@Beanpublic Binding binding( Queue orderQueue, DirectExchange orderExchange ){return BindingBuilder.bind( orderQueue ).to( orderExchange ).with( "order.routing.key" );}@Beanpublic Binding dlxBinding( Queue dlxQueue, DirectExchange orderDlxExchange ){return BindingBuilder.bind( dlxQueue ).to( orderDlxExchange ).with( "order.dlx.routing.key" );}@Beanpublic Queue dlxQueue(){return new Queue( "order.dlx.queue" );}
}
7-死信对列
/***@Description*@Author Andy Fan*@Date 2024/12/4 14:00*@ClassName DeadLetterQueueConsumer*/
@Slf4j
@Component
public class DeadLetterQueueConsumer
{@Autowiredprivate IXOrdersService xOrdersService;@RabbitListener(queues = "order.dlx.queue")public void handleDeadLetterQueue( XOrders orderMessage ){// 处理死信队列中的消息,进行补偿操作,例如手动通知用户或采取其他措施// 这里可以手动通知管理员,或者尝试人工干预log.error( "Failed to process order after multiple attempts: " + orderMessage );}}
rabbitmq 怎么保证消息的稳定性?
在使用 RabbitMQ 进行消息队列处理时,消息的稳定性是确保消息不会丢失、重复处理或被错误消费的关键。RabbitMQ 提供了多种机制来保障消息的稳定性,以下是几种常用的策略及其原理:
1. 持久化消息 (Message Persistence)
目的:防止 RabbitMQ 服务重启或宕机时消息丢失。
-
队列持久化:将队列声明为持久化队列,即使 RabbitMQ 重启后队列也能恢复。
-
消息持久化:在发送消息时标记消息为持久化,确保消息写入磁盘。
实现方式:
-
声明队列时设置
durable
为true
,确保队列本身是持久化的。 -
发送消息时设置
MessageProperties.PERSISTENT_TEXT_PLAIN
,确保消息在队列中是持久化存储的。
// 声明持久化队列boolean durable = true;
// 队列持久化 channel.queueDeclare("my_queue", durable, false, false, null);
// 发送持久化消息String message = "Hello World!";
AMQP.BasicProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN; channel.basicPublish("", "my_queue", props, message.getBytes());
注意:持久化会稍微影响性能,因为写入磁盘比写入内存慢。
2. 确认机制 (Publisher Confirms)
目的:确保生产者成功将消息发送到 RabbitMQ,并确认消息被 RabbitMQ 正确接收。
-
RabbitMQ 提供了确认机制,生产者在发送消息后会收到来自 RabbitMQ 的确认(
ack
),确保消息已成功进入队列。 -
如果没有收到确认,生产者可以选择重发消息,避免消息丢失。
实现方式:
-
启用发布确认模式 (
Publisher Confirms
)。 -
监听 RabbitMQ 的确认或失败回调。
// 启用发布确认模式 channel.confirmSelect(); String message = "Hello, RabbitMQ!"; channel.basicPublish("", "my_queue", null, message.getBytes());
// 等待确认if (channel.waitForConfirms()) { System.out.println("Message successfully sent to RabbitMQ"); } else { System.out.println("Message failed to send to RabbitMQ"); }
3. 消息应答 (Consumer Acknowledgments)
目的:确保消息被消费者成功处理后才从队列中移除,防止消息丢失或未被正确消费。
-
默认情况下,消息一旦发送给消费者就从队列中移除。为了确保消息处理成功,RabbitMQ 提供了手动
ack
应答机制。 -
消费者处理完消息后,手动发送
ack
(确认)。如果消费者宕机或处理失败而未发送ack
,消息会重新返回队列并重新分配给其他消费者。
实现方式:
-
设置消费者的
autoAck
参数为false
,启用手动应答。 -
消费者在处理完消息后,调用
channel.basicAck()
手动确认。 -
boolean autoAck = false; // 手动应答模式
channel.basicConsume("my_queue", autoAck, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope,
-
AMQP.BasicProperties properties, bytebody) throws IOException {String message = new String(body, "UTF-8");
System.out.println("Received message: "message);
-
try {// 处理消息// ...(业务逻辑)// 手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
} catch (Exception e) {// 处理失败,可以选择不应答,或者重新回到队列
channel.basicNack(envelope.getDeliveryTag(), false, true);
}
}
});
4. 消息重发与死信队列 (Dead Letter Exchanges - DLX)
目的:处理无法正常消费或被拒绝的消息,确保消息不会意外丢失。
-
当消息无法被正确处理时,可以设置重发机制或将消息转移到死信队列(DLX)中。
-
死信队列是一种特殊的队列,用来存储处理失败、被拒绝或过期的消息,方便后续进行排查和处理。
实现方式:
-
配置队列的
x-dead-letter-exchange
参数,指定消息被拒绝或无法消费时要转发的死信交换机。
代码示例:
// 创建死信队列
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");
channel.queueDeclare("my_queue", true, false, false, args);
// 创建死信交换机和队列
channel.exchangeDeclare("dlx_exchange", "direct");
channel.queueDeclare("dlx_queue", true, false, false, null);
channel.queueBind("dlx_queue", "dlx_exchange", "dlx_routing_key");
5. 限流机制 (Consumer Prefetching)
目的:防止消费者因为无法及时处理消息导致消息堆积、超时或丢失。
-
RabbitMQ 提供了限流机制,消费者可以通过设置
prefetch
值来控制每次最多消费多少条消息,确保消息处理能力不会被超载。 -
这样可以避免消费者因为处理不过来而出现未应答的消息。
实现方式:
-
使用
channel.basicQos()
来设置prefetchCount
,控制每个消费者最多消费多少条未确认的消息。
代码示例:
int prefetchCount = 10; // 每次最多消费10条消息
channel.basicQos(prefetchCount);
// 消费消息
channel.basicConsume("my_queue", false, consumer);
6. 事务机制 (Transactions)
目的:确保消息的发送和处理具备原子性,防止消息在发送或消费过程中丢失或失败。
-
RabbitMQ 支持事务,但事务会影响性能,因此更多情况下会使用发布确认机制(
Publisher Confirms
)替代事务。 -
如果需要,可以在发送消息时开启事务,确保消息的发送和处理一致。
实现方式:
try {
channel.txSelect();
// 开启事务
channel.basicPublish("", "my_queue", null, "Hello Transaction!".getBytes());
channel.txCommit();
// 提交事务
} catch (Exception e) {
channel.txRollback();
// 发生错误时回滚事务
}
7. 集群与高可用模式 (Clustering and High Availability)
目的:确保 RabbitMQ 服务本身的稳定性,防止由于服务宕机导致消息丢失。
-
RabbitMQ 支持集群模式,将多个节点组成集群来提高可用性。
-
可以使用镜像队列,将队列数据复制到集群中的多个节点,确保一个节点宕机后,消息仍然可以被处理。
配置方式:
-
在 RabbitMQ 集群中配置队列策略,将队列设置为镜像模式。
rabbitmqctl set_policy ha-all "^my_queue$" '{"ha-mode":"all"}'
总结
RabbitMQ 通过多种机制来保证消息的稳定性:
-
持久化消息:确保消息存储在磁盘上,防止服务宕机后丢失。
-
发布确认机制:确保生产者能确认消息是否成功发送到队列。
-
消费者手动应答:确保消息被消费者成功处理后才从队列中移除。
-
死信队列:处理失败消息,避免消息丢失。
-
限流机制:防止消费者过载,保证消息被及时处理。
-
集群与高可用:确保 RabbitMQ 服务的稳定性。
这些机制可以结合使用,根据业务需求选择最合适的方式来保证消息的可靠性。
消息队列优缺点
关于消息队列的优点也就是上面列举的,就是在特殊场景下有其对应的好处,解耦、异步、削峰。
缺点有以下几个:
系统可用性降低
系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用,可以点击这里查看。
系统复杂度提高
硬生生加个 MQ 进来,你怎么[保证消息没有重复消费]?怎么[处理消息丢失的情况]?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。
一致性问题
A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。
所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。
RabbitMQ出现后,国内大部分公司都从ActiveMQ切换到了RabbitMQ,基本代替了activeMQ的位置。它的社区还是很活跃的。
它的单机吞吐量也是万级,对于需要支持特别高的并发的情况,它是无法担当重任的。
在高可用上,它使用的是镜像集群模式,可以保证高可用。
在消息可靠性上,它是可以保证数据不丢失的,这也是它的一大优点。
同时它也支持一些消息中间件的高级功能,如:消息重试、死信队列等。
rabbitmq问答
135.rabbitmq 的使用场景有哪些?
-
抢购活动,削峰填谷,防止系统崩塌。
-
延迟信息处理,比如 10 分钟之后给下单未付款的用户发送邮件提醒。
-
解耦系统,对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
136.rabbitmq 有哪些重要的角色?
rabbitmq 中重要的角色有:生产者、消费者和代理:
-
生产者:消息的创建者,负责创建和推送数据到消息服务器;
-
消费者:消息的接收方,用于处理数据和确认消息;
-
代理:就是 rabbitmq 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
137.rabbitmq 有哪些重要的组件?
-
ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。
-
Channel(信道):消息推送使用的通道。
-
Exchange(交换器):用于接受、分配消息。
-
Queue(队列):用于存储生产者的消息。
-
RoutingKey(路由键):用于把生成者的数据分配到交换器上。
-
BindingKey(绑定键):用于把交换器的消息绑定到队列上。
138.rabbitmq 中 vhost 的作用是什么?
vhost:每个 rabbitmq 都能创建很多 vhost,我们称之为虚拟主机,每个虚拟主机其实都是 mini 版的rabbitmq,它拥有自己的队列,交换器和绑定,拥有自己的权限机制。
139.rabbitmq 的消息是怎么发送的?
首先客户端必须连接到 rabbitmq 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 rabbitmq 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。
140.rabbitmq 怎么保证消息的稳定性?
-
提供了事务的功能。
-
通过将 channel 设置为 confirm(确认)模式。
141.rabbitmq 怎么避免消息丢失?
-
把消息持久化磁盘,保证服务器重启消息不丢失。
-
每个集群中至少有一个物理磁盘,保证消息落入磁盘。
142.要保证消息持久化成功的条件有哪些?
-
声明队列必须设置持久化 durable 设置为 true.
-
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
-
消息已经到达持久化交换器。
-
消息已经到达持久化队列。
以上四个条件都满足才能保证消息持久化成功。
143.rabbitmq 持久化有什么缺点?
持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。
144.rabbitmq 有几种广播类型?
-
direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。
-
headers:与 direct 类似,只是性能很差,此类型几乎用不到。
-
fanout:分发模式,把消费分发给所有订阅者。
-
topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到。
145.rabbitmq 怎么实现延迟消息队列?
延迟队列的实现有两种方式:
-
通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能;
-
使用 rabbitmq-delayed-message-exchange 插件实现延迟功能。
146.rabbitmq 集群有什么用?
集群主要有以下两个用途:
-
高可用:某个服务器出现问题,整个 rabbitmq 还可以继续使用;
-
高容量:集群可以承载更多的消息量。
147.rabbitmq 节点的类型有哪些?
-
磁盘节点:消息会存储到磁盘。
-
内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。
148.rabbitmq 集群搭建需要注意哪些问题?
-
各节点之间使用“--link”连接,此属性不能忽略。
-
各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
-
整个集群中必须包含一个磁盘节点。
149.rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
不是,原因有以下两个:
-
存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
-
性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
150.rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
-
不能创建队列
-
不能创建交换器
-
不能创建绑定
-
不能添加用户
-
不能更改权限
-
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。
rabbitmq 对集群节点停止顺序有要求吗?
rabbitmq 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。
------
消息队列“四大天王”:Rabbit、Rocket、Kafka、Pulsar
基于交换机(Exchange)和队列(Queue)的灵活路由
- 交换机(Exchange):消息发布到交换机,通过路由键(Routing Key)决定消息发送到哪个队列。
- 队列(Queue):消息存储的地方,消费者从队列中获取消息。
- 路由键(Routing Key):用于匹配消息和队列的键。
- 绑定(Binding):连接交换机和队列,定义路由规则。
消息传递模式:
- Direct:消息通过精确匹配路由键发送到队列。
- Fanout:消息广播到所有绑定的队列。
- Topic:消息按模式匹配路由键发送到队列。
1.创建连接工厂,给连接工厂配置账号密码等信息
2.通过连接工厂创建了连接
3.通过连接创建一个通道
4. 为通道声明队列(队列名字,对列是否持久化,是否具有排他性,最后一个消费者消费完之后是否自动删除队列,该队列携带什么参数)
5.准备消息内容
6.发送消息给队列
7.关闭通道
8.关闭连接
为什么RabbitMQ是基于通道(channal)处理,而不是连接(connection)?
connection是一个短连接,短连接会经过三次握手四次挥手,这个过程很慢,耗费资源,耗时长,连接开关会造成很大的性能开销
所以connection在 TCP/IP 的基础之上开发一个长连接的信道,即为channal,channal是网络信道,几乎所有的操作都在chanal中进行,一个connection可以开启多个channal,从而大大提高了性能
2.Rabbitmq为什么需要信道,为什么不是TCP直接通信
1、TCP的创建和销毁,开销大,创建要三次握手,销毁要4次分手。
2、如果不用信道,那应用程序就会TCP连接到Rabbit服务器,高峰时每秒成千上万连接就会造成资源的巨大浪费,而且==底层操作系统每秒处理tcp连接数也是有限制的,==必定造成性能瓶颈。
3、信道的原理是一条线程一条信道,多条线程多条信道同用一条TCP连接,一条TCP连接可以容纳无限的信道,即使每秒成千上万的请求也不会成为性能瓶颈。
3.queue队列到底在消费者创建还是生产者创建?
1:一般建议是在rabbitmq操作面板创建。这是一种稳妥的做法。
2∶按照常理来说,确实应该消费者这边创建是最好,消息的消费是在这边。这样你承受一个后果,可能我生产在生产消息可能会丢失消息。
3:在生产者创建队列也是可以,这样稳妥的方法,消息是不会出现丢失。
4:如果你生产者和消费都创建的队列,谁先启动谁先创建,后面启动就覆盖前面的
4.可以存在没有交换机的队列吗?
不可能存在,虽然没有指定交换机,但是一定会存在一个默认的交换机,因为没有交换机队列就不会存在消息
交换机负责消息的接受,队列不会接收消息,是交换机投递消息给队列,而不是队列去接收消息
//6.发送消息给队列
/**
* @Params1 交换机(最好指定一个叫交换机名字,不然就会使用默认的交换机)
* @Params2 队列,路由key
* @Params3 消息的状态控制
* @Params4 消息主题
*/
channel.basicPublish("",queueName, null,message.getBytes());
5.模式
1.简单模式(default)
通过默认交换机投递消息给指定队列
2.发布订阅模式(fanout)
通过交换机与队列进行绑定,交换机会将消息投递到所有与之绑定的队列
类似于一些粉丝关注一个博主之后,这个博主发布视频,所有订阅这个博主的粉丝都会看到这个视频
3.路由模式(direct)
这个模式是发布订阅模式的延伸,本质还是将交换机与队列进行绑定,但是绑定的同时还要加上 Routing Key ,当交换机投递消息时,带上某个路由key,这条消息就会投递到有这个路由key的队列
可以理解为交换机投递信息给队列的时候要根据某个条件投递,交换机只会投递给满足条件的队列
场景:
当一个用户注册某个平台的账号,会把注册通知发送到邮箱,短信与微信,会对用户造成很大的不便,如果指定某个路由key,在发送注册通知的时候在路由key中带上emai,那么这个通知就只会发到带有路由key “email”的队列中
4.主题模式(tocpic)
这个模式是路由模式的延伸
就是在路由模式的基础上,添加模糊条件绑定功能,其中有#,*两种符号来控制条件
符号#:表示0个或一个或多个
符号*:表示一定要有一个并且只有一个
示例:
#.com.#: 满足这个条件的路由key是(com 或者 xxx.com.xxx或者xxx.com.xxx.xxx等等)
*.com.*:满足这个条件的路由key是(xxx.com.xxx)
#.com.*:满足这个条件的路由key是(xxx.xxx.com.x或者com.xxx)
5.headers模式
这个模式也是通过条件来进行发布消息,这个条件是就是参数
交换机先通过参数绑定队列,比如X=1绑定到queue1
在投递消息的时候携带x=1的参数即可将消息投递到queue1
6.Work模式
轮询分发
轮询分发就是指按均分配,消息分发默认就是轮询分发
轮询分发不依赖于消费者的处理速度,即使两个消费者处理速度相差很大,他们处理的消息数量也是一样的
轮询分发下,消费者消费的时候既可以设置为手动,应可以设置为自动应答
公平分发
公平分发就是按劳分配
消息分发取决于消费者的处理速度,如果消费者处理快,那这个消费者就能处理更多的消息,反之处理更少的消息
公平分发下,消费者消费的时候必须设置为手动应答,如果autoAck参数设置为true,那就会变成轮询分发
//设置为手动应答
finalChannel.basicAck(message.getEnvelope().getDeliveryTag(),false);
相关文章:
Linux Ubuntu 安装配置RabbitMQ,springboot使用RabbitMQ
rabbit-Ubuntu 一篇文章学会RabbitMQ 在Ubuntu上查看RabbitMQ状态可以通过多种方式进行,包括使用命令行工具和Web管理界面。以下是一些常用的方法: 1-使用systemctl命令: sudo systemctl start rabbitmq-server sudo systemctl status ra…...
MongoDB数据建模小案例
MongoDB数据建模小案例 朋友圈评论内容管理 需求 社交类的APP需求,一般都会引入“朋友圈”功能,这个产品特性有一个非常重要的功能就是评论体系。 先整理下需求: 这个APP希望点赞和评论信息都要包含头像信息: 点赞列表,点赞用户的昵称,头像;评论列表,评论用户的昵称…...
基于MATLAB野外观测站生态气象数据处理分析实践应用
1.本课程基于MATLAB语言 2.以实践案例为主,提供所有代码 3.原理与操作结合 4.布置作业,答疑与拓展 示意图: 以野外观测站高频时序生态气象数据为例,基于MATLAB开展上机操作: 1.不同生态气象要素文件的数据读写与批处理…...
《三角洲行动》游戏安全组件运行时发生异常1-0-0,是什么原因?以及要如何解决?
《三角洲行动》游戏安全组件异常1-0-0深度探讨 今天你们安全撤离了吗?在《三角洲行动》这款经典射击游戏里,游戏安全组件运行时发生异常1-0-0的原因及解决方案,并借此机会分享一些有关文件丢失、文件损坏和系统报错等问题的通用解决策略。希…...
VAS1260Q奇力LED驱动芯片DCDC降压恒流可替代Diodes8860
VAS1260Q是一款专为车规级LED照明设计的连续模式电感降压驱动器,能够高效地驱动单个或多个串联LED。它集成了高端输出电流检测电路,并通过外部电阻设置标称平均输出电流,具有高可靠性和宽广的应用场景。 核心技术参数 1. 输入电压范围…...
leetcode-88.合并两个有序数组(易理解)
双指针 为了合并两个已排序的数组 nums1 和 nums2,并将结果存储在 nums1 中,我们可以采用双指针的方式从后向前进行合并。这种方法的好处是可以避免在nums1中移动元素,从而提高效率。 算法步骤 初始化三个指针: p1 指向 nums1 的…...
github操作学习笔记
git开源的分布式版本控制系统: 每次修改文件提交后,都会自动创建一个项目版本 查看git版本看有没有安装成功:git --version 把默认编辑器设置成vim:git config --global core.editor "vim" 1、设置昵称和邮箱ÿ…...
基于Hadoop大数据音乐推荐系统的设计与实现
摘 要 各种主流的音乐平台都为用户提供了的大量的音乐,让他们时刻都能沉浸在音乐的海洋之中。然而,过多的音乐往往使用户眼花缭乱,很难发现他们真正所需要的。一套优秀的推荐系统,可以很好地解决这个问题,既能帮助用户…...
【Linux】冯诺依曼体系结构
目录 一、冯诺依曼体系结构二、冯诺依曼体系结构的基本组成三、关于冯诺依曼体系结构的一些问题结尾 一、冯诺依曼体系结构 冯诺依曼体系结构,也称为普林斯顿结构,是现代计算机设计的基础框架。这一体系结构由数学家冯诺依曼在20世纪40年代提出…...
2025计算机毕设选题推荐【小程序方向】【基础功能+创新点设计】
✅博主介绍:CSDN毕设辅导博主、CSDN认证 Java领域优质创作者 ✅技术范围:主要包括Java、Vue、Python、爬虫、小程序、安卓app、大数据、机器学习等设计与开发。 ✅主要内容:免费功能设计、开题报告、任务书、功能实现、代码编写、论文编写和…...
CSS系列(3)-- 定位与文档流详解
前端技术探索系列:CSS 定位与文档流详解 📍 致读者:掌握页面布局的精髓 👋 前端开发者们, 今天我们将深入探讨 CSS 定位与文档流,这是掌握页面布局的关键所在。通过本文,你将全面理解各种定位…...
【书生大模型实战营】Linux 基础知识-L0G1000
前言:书生大模型实战营是上海人工智能实验室开展的大模型系列实践活动,提供免费算力平台,学员通过闯关式任务,可获得免费算力和存储,助力项目实践。本期是第4期,时间从十一月份开始,持续到十二月…...
小程序IOS安全区域优化:safe-area-inset-bottom
ios下边有一个小黑线,位于底部的元素会被黑线阻挡 safe-area-inset-bottom 一 用法及作用: IOS全面屏底部有小黑线,位于底部的元素会被黑线阻挡,可以使用以下样式: .model{padding-bottom: constant(safe-area-ins…...
STM32-C语言基础知识
C语言基础知识 stdint.h简介 给寄存器某个位赋值 给位6赋值为1流程:先清0,再赋值 带参数的宏定义 建议使用do {…}while(0)来构造宏定义 条件编译 条件编译后面必须跟宏语句,如#if _LED_H 指针使用常见的2大问题 1、未初始化 2、越界使…...
深度和法线纹理
屏幕后期处理效果的基本原理就是当游戏画面渲染完毕后通过获取到该画面的信息进行额外的效果处理 之前的边缘检测、高斯模糊、Bloom、运动模糊等效果都是基于获取当前屏幕图像中的像素信息进行后期处理的 如果仅仅根据像素信息来进行一些效果处理,存在以下问题&…...
21天掌握javaweb-->第11天:前端性能优化与响应式设计
前端性能优化与响应式设计 1. 前端性能优化技巧 1.1 代码分割(Code Splitting) 代码分割是一种将代码拆分成多个较小的包,并在需要时按需加载的技术,这有助于减少初始加载时间,并提高应用程序的响应速度。 实现方法…...
java+ssm+mysql校园物品租赁网
项目介绍: 使用javassmmysql开发的校园物品租赁网,系统包含管理员、用户角色,功能如下: 管理员:用户管理;物品管理(物品种类、物品信息、评论信息);订单管理࿱…...
C#常见错误—空对象错误
System.NullReferenceException:未将对象引用设置到对象的实例 在C#编程中,System.NullReferenceException是一个常见的运行时异常,其错误信息“未将对象引用设置到对象的实例”意味着代码试图访问一个未被初始化或已被设置为null的对象的成…...
GPT系列模型简要概述
GPT-1:(0.117B参数量,0.8B words预训练数据) 动机: 在RNN和Transformer之间,选择了后者。 和《All your need is Attention》翻译模型的Encoder-Decoder架构相比,只保留Decoder,因此去掉了Cross…...
关于睡懒觉
我们经常听到一个词:睡懒觉。 我认为,睡懒觉这个词,是错误的。 人,是需要睡眠的,睡不够,就不会醒。睡够了,自然会醒,也不想继续睡。不信你试试,睡够了,你…...
Spring Data JPA 入门
文章目录 前言、Spring Data JPA 是什么?1、背景2、优势3、Spring Data JPA 和 MyBatis-Plus 对比4、Spring Data JPA 与 JPA 的关系是什么? 一、准备1、依赖引入Spring Boot 框架依赖引入:非 Spring Boot 框架依赖引入: 2、定义实…...
操作系统用户界面
实验目的: LINUX操作系统提供了图形用户界面和命令行界面,本实验主要熟悉命令行界面,为后续的实验编程做准备。 二、实验内容: 写出每个命令的功能及格式 1、有关目录和文件操作的命令 1.1cat 功能: 1.一次显示整…...
区块链dapp 开发详解(VUE3.0)
1、安装metamask 插件。 2、使用封装的工具包: wagmi . 3、 wagmi 操作手册地址:connect | Wagmi 4、注意事项: 因为最初是react 版本,所以在VUE版的官方文档有很多地方在 import 用的是 wagmi,需要改为 wagmi/vue 。 连接成功后打印的内容如下&…...
BTC密码学原理
文章目录 比特币的密码学基础哈希函数(Hash Function)非对称加密(Asymmetric Cryptography)数字签名(Digital Signature) 工作量证明(Proof of Work)区块链技术的密码学保障区块链的…...
【NLP 9、实践 ① 五维随机向量交叉熵多分类】
目录 五维向量交叉熵多分类 规律: 实现: 1.设计模型 2.生成数据集 3.模型测试 4.模型训练 5.对训练的模型进行验证 调用模型 你的平静,是你最强的力量 —— 24.12.6 五维向量交叉熵多分类 规律: x是一个五维(索引)向量ÿ…...
Linux -文件系统的备份
本文为Ubuntu Linux操作系统- 第九弹~~ 今天接着上文的内容,讲Linux磁盘存储管理最后一部分内容~ 上期回顾:Linux 图形界面工具管理磁盘分区和文件系统 😎黑犀铠甲合体,流星枪之狂瀑扎帖,碎魔伏暴,灭于狂瀑…...
【Linux基础五】Linux开发工具—上(apt和vim)
【Linux基础五】Linux开发工具—上(apt和vim) 1.apt包管理工具2.Linux中的编辑器vim2.1命令模式2.2插入模式2.3底行模式2.4替换模式2.5视图模式2.6多线程操作2.7配置vim 1.apt包管理工具 在 Linux 系统中,软件包管理工具是用户安装、更新和管…...
力扣--LCR 134.Pow(x,n)
题目 实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000 示例 2: 输入:x 2.10000, n 3 输出:9.2610…...
java抽奖系统(一)2.0
1. 项⽬介绍 1.1 背景 随着数字营销的兴起,企业越来越重视通过在线活动来吸引和留住客⼾。抽奖活动作为⼀种有效的营 销⼿段,能够显著提升⽤⼾参与度和品牌曝光率。于是我们就开发了以抽奖活动作为背景的Spring Boot项⽬,通过这个项⽬提供⼀…...
读取电视剧MP4视频的每一帧,检测出现的每一个人脸并保存
检测效果还不错,就是追踪有点难做 import cv2 import mediapipe as mp import os from collections import defaultdict# pip install msvc-runtime# 初始化OpenCV的MultiTracker # multi_tracker = cv2.MultiTracker_create() # multi_tracker = cv2.legacy.MultiTracker_cre…...
【MySQL 进阶之路】事务并发情况分析
MySQL事务并发控制分析笔记 在数据库系统中,事务并发控制至关重要,能够确保多个事务并发执行时的数据一致性、隔离性和正确性。MySQL通过不同的锁机制控制并发操作,以确保事务的隔离性。以下是对事务A和事务B并发行为的详细分析,…...
基于SSM的线上考试系统的设计与实现(计算机毕业设计)+万字说明文档
系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 IDE环境: Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境: Tomcat 7.x,8.x,9.x版本均可 操作系统…...
redis击穿,穿透,雪崩以及解决方案
目录 击穿 解决方案一 解决方案二 穿透 解决方案 雪崩 解决方案 击穿 指的是单个key在缓存中查不到,去数据库查询,这样如果并发不大或者数据库数据量不大的话是没有什么问题的。 如果数据库数据量大并且是高并发的情况下那么就可能会造成数据库压…...
Flask返回中文Unicode编码(乱码)解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
EasyExcel改名为FastExce做了那些改变呢
回到:github原作者地址:https://github.com/CodePhiliaX/fastexcel 中文 |English | 什么是 FastExcel FastExcel 是由原 EasyExcel 作者创建的新项目。2023 年我已从阿里离职,近期阿里宣布停止更新 EasyExcel,作者他本人决定继…...
数据结构之初始二叉树(1)
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:数据结构(Java版) 目录 树型结构 树的概念 与树的有关概念 树的表示形式 树的应用 二叉树 概念 两种特殊的…...
利用Python爬虫按图搜索淘宝商品(拍立淘)
在当今数字化时代,能够通过图片搜索商品的功能(如淘宝的“拍立淘”)为用户提供了极大的便利。本文将详细介绍如何利用Python爬虫技术实现按图搜索淘宝商品,并提供相应的代码示例。 1. 拍立淘功能简介 “拍立淘”是淘宝提供的一项…...
微信小程序中使用miniprogram-sm-crypto实现SM4加密攻略
在微信小程序开发过程中,数据安全至关重要。本文将为大家介绍如何在微信小程序中使用miniprogram-sm-crypto插件进行SM4加密,确保数据传输的安全性。 一、SM4加密简介 SM4是一种对称加密算法,由国家密码管理局发布,适用于商密领…...
基于注解的方式实现分布式锁的AOP功能
使用场景: 在分布式项目中,涉及到事务,且会操作多张表,那么涉及到并发场景,应当避免同一时间有多个用户并发操作同一份数据,例如:商品秒杀。 解决方案: 使用Around注解实现在方法…...
22. 五子棋小游戏
文章目录 概要整体架构流程技术名词解释技术细节小结 1. 概要 🔊 JackQiao 对 米粒 说:“今天咱们玩个五子棋小游戏,电脑与你轮流在一个 nn 的网格上放置棋子(X 或 O),网格由你输入的正整数n决定࿰…...
import是如何“占领满屏“
import是如何“占领满屏“的? 《拒绝使用模块重导(Re-export)》 模块重导是一种通用的技术。在腾讯、字节、阿里等各大厂的组件库中都有大量使用。 如:字节的arco-design组件库中的组件:github.com/arco-design… …...
传奇996_53——后端ui窗口局部刷新
描述:一个大窗口,点击某个键,弹出小窗口。 小窗口中将msg存进变量中 大窗口中判断一个参数是否为null,如果不为null,说明界面不是第一次打开,而是被刷新了。就加上小窗口的那个变量 有时小窗口中还有其他…...
云原生API网关:2024年的性能与智能化突破
API网关已经成为现代应用世界的流量控制器,并正在成为现代应用架构的重要组成部分。然而,自成立以来,他们的角色发生了巨大变化。它们正在减轻重量,变得更加可编程,并与云原生环境携手合作。 让我们来探索这一激动人心…...
专题二十五_动态规划_两个数组的 dp (含字符串数组)_算法专题详细总结
目录 动态规划_两个数组的 dp (含字符串数组) 1. 最⻓公共⼦序列(medium) 解析: 1. 状态表⽰: 2. 状态转移⽅程: 3. 初始化:编辑 4. 填表顺序:编辑 5. 返回值…...
day09 接口测试(5)——使用postman 完成项目实战
【没有所谓的运气🍬,只有绝对的努力✊】 目录 1、项目简介 2、技术栈 2.1 技术栈 2.2 技术架构图 3、初始化项目环境!!! 3.1 新建用例集 3.2 创建环境变量 3.3 登录模块 3.3.1 从测试用例文档提取数据 3.3.2…...
【Solidity】变量详解:类型、作用域与最佳实践
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 Solidity变量详解:类型、作用域与最佳实践引言1. 变量的类型与声明1…...
知识图谱9:知识图谱的展示
1、知识图谱的展示有很多工具 Neo4j Browser - - - - 浏览器版本 Neo4j Desktop - - - - 桌面版本 graphX - - - - 可以集成到Neo4j Desktop Neo4j 提供的 Neo4j Bloom 是用户友好的可视化工具,适合非技术用户直观地浏览图数据。Cypher 是其核心查询语言&#x…...
scss文件内引入其他scss文件报错
1、今天在编译一些老项目的时候,老是提示下面信息 2、而且有很多Sass import rules are deprecated and will be removed in Dart Sass 3.0.0.警告 3、用npm view sass versions看,其中sass的最新版本是1.82.0 4、经过测试"sass": "1.75…...
使用 ASP.NET Core HttpLoggingMiddleware 记录 http 请求/响应
我们发布了一个应用程序,该应用程序运行在一个相当隐蔽的 WAF 后面。他们向我们保证,他们的产品不会以任何方式干扰我们的应用程序。这是错误的。他们删除了我们几乎所有的“自定义”标头。为了“证明”这一点,我构建了一个中间件,…...
沃丰科技智能客服在跨境电商独立站中的核心角色
随着全球化进程的加速和互联网技术的不断发展,跨境电商行业蓬勃兴起,为消费者提供了更广阔、更便捷的购物选择。在这样一个竞争激烈的市场环境中,优质的客户服务成为了企业脱颖而出的关键。沃丰科技智能客服凭借其先进的技术和人性化的设计理…...