RabbitMQ之快速入门、上手
前言
学习一样新技术、新框架,最重要的是学习其思想、原理。即原理性思维。
如果是因为工作原因,需要快速上手RabbitMQ,本篇或许适合你。
核心概念
- Connection:publisher/consumer 和 broker 之间的 TCP 连接
- Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
- Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
- Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
- VHost: 虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离

安装
- 修改host,添加下面数据
192.168.204.179 rabbit
- 拉取镜像
docker pull rabbitmq:3.8.2-management
- 启动容器
docker run -d --restart=always \--hostname rabbit \--name=rabbitmq \-p 5671:5617 -p 5672:5672 -p4369:4369 -p 15671:15671 -p 15672:15672 -p 25672:25672 \rabbitmq:3.8.2-management
5671:开启管理插件时,管理界面接口
5671、5672:AMQP
4369:守护进程
25672:
用户、Virtual Host管理
用户角色
访问管理界面:192.168.204.179:15672
默认账号密码:guest
角色列表:
- 超级管理员(administrator)可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。
- 监控者(monitoring)可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
- 策略制定者(policymaker)可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
- 普通管理者(management)仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
- 其他无法登陆管理控制台,通常就是普通的生产者和消费者。

Virtual Hosts配置
每个虚拟主机就相当于一个独立的MQ服务器,虚拟主机之间相互隔离。
- 创建虚拟主机,主机名称一般以/开头:

- 权限设置:

添加队列

添加交换机
- 添加交换机

- 绑定Queue

整合SpringBoot
<!--amqp协议的起步依赖坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><!--rabbit测试依赖坐标--><dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit-test</artifactId><scope>test</scope></dependency><!--SpringBoot测试依赖坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
# RabbitMQ 服务host地址
spring.rabbitmq.host=rabbit
# 端口
spring.rabbitmq.port=5672
# 虚拟主机地址
spring.rabbitmq.virtual-host=/mytest
# rabbit服务的用户名
spring.rabbitmq.username=test
# rabbit服务的密码
spring.rabbitmq.password=123456
五种工作模式
1.简单模式

- 先添加一个Queue

- 生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() {/*** 参数1:消息队列名称* 参数2:消息内容*/rabbitTemplate.convertAndSend("/myqueue", "hello 小兔子!");}}
- 消费者
@Component
@RabbitListener(queues = "/myqueue")
public class SimpleListener {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====接收消息====>"+msg);}
}
2.工作队列模式

相比于简单模式,这个模式下会存在多个消费者。
- 在普通模式基础上,增加订阅者即可:
@Component
@RabbitListener(queues = "/myqueue")
public class SimpleListener2 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====222接收消息====>"+msg);}
}
- 测试:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() {/*** 参数1:消息队列名称* 参数2:消息内容*/for (int i = 0; i < 10000; i++) {rabbitTemplate.convertAndSend("/myqueue", "hello 小兔子!" + i);}}}
交换机类型

消息将发给交换机,交换机的类型决定了它会怎么处理这个消息:
- Direct(直连交换机):使用消息的 Routing Key 与队列的 Binding Key 进行精确匹配,只有消息的 Routing Key 与队列的 Binding Key 完全相同时,消息才会被路由到该队列。非广播。
- Fanout(扇形交换机):忽略消息的 Routing Key,直接将消息发送到所有与交换机绑定的队列。广播消息。
- Topic(主题交换机):使用通配符匹配的方式将消息路由到多个队列。通配符由字符 “#” 和 “” 组成,其中 “#” 表示匹配零个或多个单词,“” 表示匹配一个单词。支持灵活的消息路由。
- Headers(头交换机):使用消息的 Headers 属性来匹配队列的 Binding Headers,从而确定消息的路由。较少使用,一般情况下使用 Direct、Fanout 或 Topic 类型的交换机就能满足大部分场景。
符号 “#” 匹配一个或多个词,符号""匹配不多不少一个词。因此“audit.#” 能够匹配到“audit.irs.corporate”,但是“audit.” 只会匹配到 “audit.irs”。
总结就是交换机负责消息转发,不进行数据存储,如果没有找到绑定的队列或匹配的队列,消息将会丢失。
3.广播模式
将同一个消息广播到订阅的多个消费者手中。
创建队列和交换机



生产者
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQSPTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() {/*** 参数1:消息队列名称* 参数2:消息内容*/for (int i = 0; i < 10000; i++) {rabbitTemplate.convertAndSend("/fanout_exchange", null, "hello 小兔子!" + i);}}}
消费者
@Component
@RabbitListener(queues = "/fanout_queue1")
public class SimpleListener_Fanout1 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====222接收消息====>"+msg);}
}@Component
@RabbitListener(queues = "/fanout_queue2")
public class SimpleListener_Fanout2 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====3333接收消息====>"+msg);}
}
4.路由模式
在将队列绑定到交换机的时候,需要指定路由key;发送消息的时候也要指明路由key。

配置交换机

生产者
/*** 路由**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQRoutingTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() {/*** 参数1:消息队列名称* 参数2:消息内容*/for (int i = 0; i < 10000; i++) {if (i % 2 == 0) {rabbitTemplate.convertAndSend("/routing_exchange", "info", "hello 小兔子!" + i);} else {rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + i);}}}}
消费者
@Component
@RabbitListener(queues = "/routing_err1")
public class Routing_Err1 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====routing_err1接收消息====>"+msg);}
}@Component
@RabbitListener(queues = "/routing_err2")
public class Routing_Err2 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====routing_err2接收消息====>"+msg);}
}@Component
@RabbitListener(queues = "/routing_info1")
public class Routing_info1 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====routing_info1接收消息====>"+msg);}
}
5.主题模式(通配符)
#:匹配零个或多个。
*:匹配一个。

配置

生产者
/*** topc**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class MQTopicTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void contextLoads() {/*** 参数1:消息队列名称* 参数2:消息内容*/for (int i = 0; i < 10000; i++) {if (i % 2 == 0) {rabbitTemplate.convertAndSend("/topic_exchange", "test.info", "hello 小兔子!" + i);} else {rabbitTemplate.convertAndSend("/topic_exchange", "test.err", "hello 小黑子!" + i);}rabbitTemplate.convertAndSend("/topic_exchange", "my.test.warn", "hello 小白子!" + i);}}}
消费者
@Component
@RabbitListener(queues = "/topic_info")
public class Topic1 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====topic_info接收消息====>"+msg);}
}@Component
@RabbitListener(queues = "/topic_err")
public class Topic2 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====topic_err接收消息====>"+msg);}
}@Component
@RabbitListener(queues = "/topic_warn")
public class Topic3 {@RabbitHandlerpublic void simpleHandler(String msg){System.out.println("=====topic_warn接收消息====>"+msg);}
}
工作模式总结
- 简单模式:一个生产者和一个消费者,无需交换机。
- 工作队列模式:一个生产者,多个消费者(竞争消息),无需交换机。
- 发布订阅模式:fanout类型的交换机。消费广播到每个绑定的queue中。
- 路由模式:direct类型的交换机。消息发送到路由key精确匹配的队列中。
- 通配符模式:topic类型的交换机。消息发送到通配符匹配的路由key的队列中。
高级特性
生产者确认
rabbitmq提供了两种方式来保证投递的可靠性:
- confirm 确认模式:消息发到交换机,不管是否成功,都回调confirmCallback。
- return 退回模式:投递失败会回调returnCallback。
配置
spring:rabbitmq:password: 123456username: testvirtualHost: /mytestport: 5672host: rabbitpublisherReturns: truepublisherConfirmType: SIMPLE
案例
@Slf4j
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback{@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {if (ack) {log.info("消息发送到exchange成功");} else {log.info("消息发送到exchange失败");}}
}@Slf4j
public class RabbitReturnCallback implements RabbitTemplate.ReturnsCallback {@Overridepublic void returnedMessage(ReturnedMessage returnedMessage) {log.info("消息发送失败:{}", returnedMessage.getMessage());}
}@Configuration
public class RabbitCallBackConfig {@Resourceprivate RabbitTemplate rabbitTemplate;@PostConstructpublic void initRabbitTemplate(){rabbitTemplate.setConfirmCallback(new RabbitConfirmCallback());rabbitTemplate.setReturnsCallback(new RabbitReturnCallback());}}@RestController
public class TestController {@Resourceprivate RabbitTemplate rabbitTemplate;@GetMapping("confirmCallBack")public String confirmCallBack() {for (int i = 0; i < 10000; i++) {rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + i);}return "ok";}@GetMapping("returnCallBack")public String returnCallBack() {for (int i = 0; i < 10000; i++) {// 不存在的routingkeyrabbitTemplate.convertAndSend("/routing_exchange", "err2", "hello 小黑子!" + i);}return "ok";}}
消费者确认
消费者消息确认有三种类型:
- 无确认:none。收到生产者的消息之后,直接ACK。
- 手动确认:manual。消费者需要显式的告诉RabbitMQ消息已经确认。手动确认更安全。
- 自动确认:auto。客户端收到消息之后,mq自动ACK。
为什么手动确认更安全?
消费者处理消息失败时,可以重新处理消息。其它优势:1.消费者可以根据处理能力控制消费速率;2.批量确认多个信息。
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;import java.util.concurrent.atomic.AtomicInteger;/*** 自定义监听器,监听到消息之后,立即执行onMessage方法*/
@Component
public class CustomAckConsumerListener implements ChannelAwareMessageListener {private static final AtomicInteger errCount = new AtomicInteger();/*** 监听到消息之后执行的方法** @param message 消息内容* @param channel 消息所在频道*/@Overridepublic void onMessage(Message message, Channel channel) throws Exception {//获取消息内容byte[] messageBody = message.getBody();String msg = new String(messageBody, "UTF-8");System.out.println("接收到消息,执行具体业务逻辑{} 消息内容:" + msg);//获取投递标签MessageProperties messageProperties =message.getMessageProperties();long deliveryTag = messageProperties.getDeliveryTag();// 模拟业务错误if(errCount.getAndIncrement() % 2 == 0) {System.out.println("业务报错,重回队列");channel.basicNack(deliveryTag, false, true);return;}// 签收消息,前提条件,必须在监听器的配置中,开启手动签收模式// 参数1:消息投递标签// 数2:是否批量签收:true一次性签收所有,false,只签收当前消息channel.basicAck(deliveryTag, false);System.out.println("手动签收完成:{}");}
}
import cn.lsj.consumer.listener.CustomAckConsumerListener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ListenerConfiguration {/*** 注入消息监听器适配器** @param customAckConsumerListener 自定义监听器对象*/@Beanpublic MessageListenerAdapter messageListenerAdapter(CustomAckConsumerListener customAckConsumerListener) {//创建自定义监听器适配器对象return new MessageListenerAdapter(customAckConsumerListener);}/*** 注入消息监听器容器** @param connectionFactory 连接工厂* @param messageListenerAdapter 自定义的消息监听器适配器*/@Beanpublic SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {//简单的消息监听器容器对象SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();//绑定消息队列container.setQueueNames("/routing_err3");//设置连接工厂对象container.setConnectionFactory(connectionFactory);//设置消息监听器适配器container.setMessageListener(messageListenerAdapter);//设置手动确认消息:NONE(不确认消息),MANUAL(手动确认消息),AUTO(自 动确认消息)container.setAcknowledgeMode(AcknowledgeMode.MANUAL);return container;}}
消费端限流
应用场景:
- 宕机恢复之后处理大量的积压消息导致业务系统再次崩溃。
- 短时间大量请求来到,业务系统不支持同时处理那么多的消息。
要求:必须为手动确认消息。
package cn.lsj.consumer.config;import cn.lsj.consumer.listener.CustomAckConsumerListener;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ListenerConfiguration {/*** 注入消息监听器适配器** @param customAckConsumerListener 自定义监听器对象*/@Beanpublic MessageListenerAdapter messageListenerAdapter(CustomAckConsumerListener customAckConsumerListener) {//创建自定义监听器适配器对象return new MessageListenerAdapter(customAckConsumerListener);}/*** 注入消息监听器容器** @param connectionFactory 连接工厂* @param messageListenerAdapter 自定义的消息监听器适配器*/@Beanpublic SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {//简单的消息监听器容器对象SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();//绑定消息队列container.setQueueNames("/routing_err3");//设置连接工厂对象container.setConnectionFactory(connectionFactory);//设置消息监听器适配器container.setMessageListener(messageListenerAdapter);//设置手动确认消息:NONE(不确认消息),MANUAL(手动确认消息),AUTO(自 动确认消息)container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//设置消费端限流,每次拉取消息多少条,默认是250条container.setPrefetchCount(1);return container;}}
TTL(消息存活时间)
消息过期时间到未被消费则被自动清楚。
可以针对消息设置,也可以针对队列设置。
消息过期时间
rabbitTemplate.convertAndSend("/routing_exchange", "err", "hello 小黑子!" + count.getAndIncrement(), m -> {// 10秒m.getMessageProperties().setExpiration(String.valueOf(10000L));return m;
});
队列过期时间
参数message-TTL ,单位毫秒,在创建队列的时候添加。

注意点
- 消息过期的优先级高于队列;
- 即使消息已经过期,也要等到前面的消费被消费或删除才进一步处理,所以要注意消息堆积的情况。
死信队列
是什么
当消息过期未消费;当消费者拒接消息且不放回源队列;当队列队列达到最大限度时。
以上三个场景,导致了Dead message(死消息)的产生。

如何设置死信队列

延迟队列
是什么
消息到达队列之后,不会马上被消费,而是等待一段时间之后才会被消费。
应用场景
- 定时任务;
- 订单超时;
- 定时通知;
- 消息重试\错误重试:消息放入延迟队列,过一段时间重试;
如何实现?
死信队列配合过期队列实现延迟队列。
如下图,我们不设置过期队列的消费者,让消息过期之后进入死信队列,达到延迟效果。

相关文章:
RabbitMQ之快速入门、上手
前言 学习一样新技术、新框架,最重要的是学习其思想、原理。即原理性思维。 如果是因为工作原因,需要快速上手RabbitMQ,本篇或许适合你。 核心概念 Connection:publisher/consumer 和 broker 之间的 TCP 连接Channel…...
GBASE南大通用-GBase 8s数据库日志模式及切换
一、 GBase 8s数据库共有以下 4 种日志模式:无日志模式、缓冲日志模式、无缓冲日志模式、ANSI 模式。详细介绍如下: 1、无日志模式(Non logging): 采用无日志模式时,所有 DML 操作都不会被记录到日志中&…...
侵入式和非侵入式微服务框架的比较
微服务框架可以分为侵入式和非侵入式两种。侵入式框架需要对现有代码进行改造,而非侵入式框架则无需改造现有代码。 侵入式框架 侵入式框架将微服务治理功能嵌入到应用程序中,需要修改应用程序的代码。这种框架的优点是可以提供更强大的功能࿰…...
Go语言程序设计-第5章--函数
Go语言程序设计-第5章–函数 5.1 函数声明 每个函数声明都包含一个名字、一个形参列表、一个可选的返回列表以及函数体: func name(parameter-list) (result-list) {body }func add(x int, y int) int { return x y} func sub(x, y int) (z int) {z x - y; return} func f…...
数据被锁?被.mkp 勒索病毒攻击后的拯救行动
导言: 网络安全面临着越来越多的挑战,而.mallox勒索病毒则成为数字威胁中的一股强大势力。它的威胁不仅体现在其高度复杂的加密算法上,还表现在对受感染系统的深度渗透和数据的极大破坏上。以下是.mallox勒索病毒的主要威胁:如不…...
Fine-Tuning Language Models from Human Preferences
Abstract 奖励学习(reward learning)可以将强化学习(RL)应用到由人类判断定义奖励的任务中,通过询问人类问题来构建奖励模型。奖励学习的大部分工作使用了模拟环境,但是关于价值的复杂信息经常是以自然语言的形式表达的。我们相信语言奖励学习是使强化学习在现实世界任务…...
提升数据库性能的关键指南-Oracle AWR报告
文章目录 一、了解AWR报告:数据库性能的仪表盘二、生成AWR报告三、解读AWR报告的关键部分1.报告开头的系统基础信息2.ADDM发现3.负载概览(Load Profile)4.参数文件5.顶级前台等待事件6.SQL 统计信息-顶级SQL7.SGA Advisory AND PAG Advisory 一、了解AWR报告&#x…...
云计算IaaS、PaaS和SaaS之
提供的服务来比较如下两图 示例图 示例图...
解锁大数据世界的钥匙——Hadoop HDFS安装与使用指南
目录 1、前言 2、Hadoop HDFS简介 3、Hadoop HDFS安装与配置 4、Hadoop HDFS使用 5、结语 1、前言 大数据存储与处理是当今数据科学领域中最重要的任务之一。随着互联网的迅速发展和数据量的爆炸性增长,传统的数据存储和处理方式已经无法满足日益增长的需求。…...
写在2023岁末:敏锐地审视量子计算的当下
本周,《IEEE Spectrum》刊登了一篇出色的文章,对量子计算(QC)的近期前景进行了深入探讨。 文章的目的并不是要给量子计算的前景泼冷水,而是要说明量子计算的前景还很遥远,并提醒读者量子计算的用例可能很窄…...
C/C++学习笔记十三 C++中的重载运算符
1、什么是运算符重载? 运算符重载是 C 中的一项功能,使运算符(例如 、- 等)能够处理用户定义的数据类型。这种机制称为编译时多态性,并提供了为不同数据类型定制运算符行为的优点。 例如,我们可以重载“”运…...
Java 实现自动获取法定节假日
一、背景 在实现业务需求的过程中,遇到了需要计算 x 个工作日后的日期需求。由于工作日是每年国务院发布的,调休和休假都没有规律,所以无法使用算法进行计算。 一般的实现方案是自己维护一个工作日和调休的表,或者去爬取国务院发…...
湘潭大学-2023年下学期-c语言-作业0x0a-综合1
A 求最小公倍数 #include<stdio.h>int gcd(int a,int b) {return b>0?gcd(b,a%b):a; }int main() {int a,b;while(~scanf("%d%d",&a,&b)){if(a0&&b0) break;printf("%d\n",a*b/gcd(a,b));}return 0; }记住最大公约数的函数&…...
网络协议-BIO实战和NIO编程
网络通信编程基本常识 在开发过程中,如果类的名字有 Server 或者 ServerSocket 的,表示这个类是给服务端容纳网络 服务用的,如果类的名字只包含 Socket 的,那么表示这是负责具体的网络读写的。 ServerSocket 并不负责具体的网络读…...
Word 将页面方向更改为横向或纵向
文章目录 更改整个文档的方向更改部分页面的方向方法1:方法2: 参考链接 更改整个文档的方向 选择“布局”>“方向”,选择“纵向”或“横向”。 更改部分页面的方向 需要达到下图结果: 方法1: 选:中你要在横向页面…...
关键字:abstract关键字
在 Java 中,abstract是一个关键字,用于修饰类和方法。当一个类被声明为抽象类时,它不能被实例化,只能被其他类继承。同时,抽象类可以包含抽象方法,抽象方法没有方法体,只包含方法的签名…...
从PDF中提取图片
由于工作需要,要从pdf文件中提取出图片保存到本地,项目中就引用到了Apache PDFBox库。 1 什么是Apache PDFBox? Apache PDFBox库,一个用于处理PDF文档的开源Java工具。它允许用户创建全新的PDF文件,操作现有的PDF文档࿰…...
推荐:一个不错的介绍Apache Doris的PPT
原来Apache Doris居然是百度开源出来的,不错。部分节选:完整下载地址网盘: 链接: https://pan.baidu.com/s/18WR70R_f72GxCjh0lykStQ 提取码: umd3 复制这段内容后打开百度网盘手机App,操作更方便哦 --来自百度网盘超级会员v7的分…...
【Python_PySide2学习笔记(二十二)】进度对话框QProgressDialog类的基本用法
进度对话框QProgressDialog类的基本用法 进度对话框QProgressDialog类的基本用法前言一、QProgressDialog 的常用方法1、创建进度对话框2、进度对话框设置窗口标题3、进度对话框隐藏"最大化"、"最小化"、"关闭"4、进度对话框设置是否自动关闭5、…...
使用rust读取usb设备ACR122U的nfc卡片id
rust及其高效和安全著称,而且支持跨平台,所以就想使用这个rust开发一个桌面端程序,来读取nfc设备的nfc卡片的id信息,下面就做一个最简单的入门教程吧,也是我写的第三个rust应用。 当你电脑上安装好了rust环境之后&…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
