RabbitMQ 四种交换机(Direct、Topic、Fanout、Headers)详解
本文是博主在梳理 RabbitMQ 知识的过程中,将所遇到和可能会遇到的基础知识记录下来,用作梳理 RabbitMQ 的整体架构和功能的线索文章,通过查找对应的知识能够快速的了解对应的知识而解决相应的问题。
文章目录
- 一、直连交换机(Direct Exchange)
- 1、核心原理
- 2、Java 代码示例
- 二、主题交换机(Topic Exchange)
- 1、核心原理
- 2、Java代码示例
- 三、扇出交换机(Fanout Exchange)
- 1、核心原理
- 2、Java代码示例
- 四、头交换机(Headers Exchange)
- 1、核心原理
- 2、Java代码示例
- 五、四种交换机对比
RabbitMQ 是一个开源的消息中间件(Message Broker),基于 AMQP
(高级消息队列协议,Advanced Message Queuing Protocol)实现,旨在为分布式系统提供可靠的异步通信解决方案。
在开发中引入 RabbitMQ 中间件一般用于不同项目之间的消息传递,利用其基本特性可以应对各种常见问题。
- 异步通信桥梁:允许不同应用程序通过
消息
进行间接通信,无需实时同步响应,提升系统解耦性和可扩展性。 - 削峰填谷:通过消息队列暂存峰值流量,缓解后端服务压力,适用于流量波动较大的场景(如电商促销、日志处理)。
- 可靠消息传递:支持消息持久化、确认机制(ACK)、重试策略等,确保消息不丢失或重复处理。
- 多语言支持:提供 Java、Python、C#、Go、JavaScript 等主流语言的客户端库,方便跨平台集成。
- 高可用性与集群:支持节点集群部署(如镜像队列),实现故障转移和负载均衡,保障服务稳定性。
- 插件生态:通过插件扩展功能,例如管理界面(RabbitMQ Management Plugin)、STOMP/MQTT 协议支持、消息追踪等。
- 轻量与高效:基于 Erlang 语言开发,天生支持高并发和低延迟,适合分布式系统环境。与 Kafka 等吞吐量优先的中间件相比,RabbitMQ 在处理超大规模数据时性能稍弱,更适合中等规模、注重可靠性的业务。
本文主要讲解 RabbitMQ 支持的四种类型交换机:
首先,RabbitMQ是一个消息代理,负责接收和转发消息。在RabbitMQ中,生产者发送消息到交换机,交换机根据类型和绑定规则将消息路由到队列。消费者从队列中获取消息进行处理。
这里的关键在于交换机的类型,不同的交换机类型决定了不同的消息路由方式。
一、直连交换机(Direct Exchange)
1、核心原理
直连交换机是 RabbitMQ 中最简单的消息路由机制,基于精确的路由键匹配规则。
直连交换机根据消息的路由键(Routing Key
)和队列绑定时指定的绑定键(Binding Key
)进行精确匹配。如果两者完全匹配,消息就会被路由到对应的队列。这种精确匹配的方式适用于需要精确控制消息路由的场景,比如根据不同的任务类型分发到不同的队列。
- 生产者发送消息时指定一个路由键(
Routing Key
)。 - 队列通过绑定键(
Binding Key
)与交换机绑定。 - 仅当路由键与绑定键完全匹配时,消息才会被路由到对应队列。
假设有一个直连交换机 direct_logs
,队列A绑定路由键 info
,队列B绑定路由键 error
。当生产者发送路由键为 info
的消息时,只有队列A会收到;发送 error
则只有队列B收到。如果发送 warning
且没有队列绑定该键,消息会被丢弃。
在RabbitMQ中,当声明一个队列时,如果没有指定交换机,会使用默认的直连交换机,队列名称作为路由键。
使用直连交换机要注意的一些特性:
- 精确匹配:路由键与绑定键需严格一致(如
order.create
→order.create
)。 - 多队列绑定:多个队列可绑定到同一路由键,消息会广播到所有匹配队列。
- 区分大小写:
Error
与error
视为不同键。 - 默认交换机:RabbitMQ 内置无名直连交换机,队列默认绑定到该交换机(路由键=队列名)。
常用的一些场景:
- 任务分类:例如按订单类型(
payment
、shipping
)分发到不同队列。 - 日志级别:将
info
、error
日志路由到不同的处理服务。
2、Java 代码示例
1、代码依赖
<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.14.2</version>
</dependency>
2、生产者(发送消息)
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;public class DirectExchangeProducer {private static final String EXCHANGE_NAME = "direct_logs";private static final String[] ROUTING_KEYS = {"info", "error", "warning"};private static final String QUEUE_NAME = "my-mq-queue";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {// 1. 创建连接工厂ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);// 2. 建立连接并创建通道try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 3. 声明直连交换机channel.exchangeDeclare(EXCHANGE_NAME, "direct");// 4. 发送消息到不同路由键for (String routingKey : ROUTING_KEYS) {String message = "Log message of level: " + routingKey;channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");}}}
}
3、消费者(接收消息)
import com.rabbitmq.client.*;public class DirectExchangeConsumer {private static final String EXCHANGE_NAME = "direct_logs";private static final String QUEUE_NAME = "info_queue";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 1. 声明直连交换机channel.exchangeDeclare(EXCHANGE_NAME, "direct");// 2. 声明队列并绑定路由键channel.queueDeclare(QUEUE_NAME, false, false, false, null);channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info"); // 绑定键为 "info"// 3. 定义消息处理回调DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};// 4. 监听队列消费消息channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});}
}
生产者发送消息:
- 发送路由键为
info
→ 投递到绑定键为info
的队列。 - 发送路由键为
error
→ 投递到绑定键为error
的队列。 - 发送路由键为
warning
→ 若无队列绑定该键,消息被丢弃。
消费者绑定队列:
- 队列
info_queue
绑定到direct_logs
交换机,绑定键为info
。 - 仅接收路由键为
info
的消息。
4、注意事项
- 若无队列匹配路由键,消息会被直接丢弃。
- 多个消费者监听同一队列时,消息会在消费者间轮询分发(竞争消费模式)。
- 多队列绑定同一键:例如同时绑定
queue1
和queue2
到error
键,消息会广播到两个队列。
- 多队列绑定同一键:例如同时绑定
- 直连交换机因匹配规则简单,路由性能极高,适合高吞吐场景。
- 结合其他交换机,例如先用
Topic Exchange
分类消息,再用Direct Exchange
精确分发。
直连交换机通过精确的路由键匹配机制,为明确目标的消息分发提供了高效、直接的解决方案。
二、主题交换机(Topic Exchange)
1、核心原理
主题交换机是RabbitMQ中基于通配符模式匹配的灵活路由机制,与直连交换机不同的是,它可以支持模糊匹配机制。
- 生产者发送消息时指定一个路由键(
Routing Key
),格式为点分隔的单词(order.payment.success
)。 - 队列通过绑定键(
Binding Key
)与交换机绑定,绑定键支持通配符(*
和#
)。 - 路由规则:根据绑定键的通配符模式匹配路由键,匹配成功则消息路由到对应队列。
通配符规则:
*
(星号):- 匹配一个单词(任意字符组合)。
- 示例:
order.*.success
→ 匹配order.payment.success
,但不匹配order.payment.email.success
。
#
(井号):- 匹配零个或多个单词(任意长度)。
- 示例:
order.#
→ 匹配order.payment
、order.payment.success
、order
等。
- 绑定键格式:
- 必须为点分隔的单词(如
user.notification.email
)。 - 通配符必须位于单词位置(如
*.error
是合法的,*error
非法)。
- 必须为点分隔的单词(如
常用的一些场景:
- 多维度消息分类:例如按业务类型(
order
、payment
)和操作结果(success
、error
)组合路由。 - 动态订阅:允许消费者根据自身需求绑定特定模式的路由键(如监听所有错误日志
*.error
)。 - 层次化路由:支持树形结构的路由设计(如
region.us.east.notification
)。
2、Java代码示例
1、代码依赖(Maven)
<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.14.2</version>
</dependency>
2、生产者(发送消息)
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;public class TopicExchangeProducer {private static final String EXCHANGE_NAME = "topic_logs";private static final String[] ROUTING_KEYS = {"order.payment.success","order.shipping.error","user.notification.email","system.alert.high"};private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明主题交换机channel.exchangeDeclare(EXCHANGE_NAME, "topic");// 发送不同路由键的消息for (String routingKey : ROUTING_KEYS) {String message = "Message with routing key: " + routingKey;channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");}}}
}
3、消费者(接收消息)
import com.rabbitmq.client.*;public class TopicExchangeConsumer {private static final String EXCHANGE_NAME = "topic_logs";private static final String QUEUE_NAME = "order_errors";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 声明主题交换机channel.exchangeDeclare(EXCHANGE_NAME, "topic");// 声明队列并绑定路由键模式channel.queueDeclare(QUEUE_NAME, false, false, false, null);channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "order.*.error");// 定义消息处理回调DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};// 监听队列消费消息channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});}
}
绑定键与路由键匹配:
- 队列A绑定
order.*.error
→ 匹配order.shipping.error
,不匹配order.payment.success
。 - 队列B绑定
#.error
→ 匹配system.alert.error
、order.shipping.error
等。 - 队列C绑定
system.#
→ 匹配system.alert.high
、system.monitoring.cpu
等。
消息分发:
- 路由键
order.shipping.error
→ 同时匹配队列A和队列C。 - 路由键
user.notification.email
→ 仅匹配绑定user.*.email
或user.#
或#.email
的队列。
4、注意事项
- 通配符位置限制:
*
和#
必须位于绑定键的单词位置,如*.error
合法,error.*
或err*
非法。
- 通配符匹配相比直连交换机的精确匹配略有性能损耗,但在大多数场景下可忽略。
- 若消息无匹配队列,默认会被丢弃。
- 可以组合使用交换机,例如先用
Fanout Exchange
广播消息,再用Topic Exchange
精细化过滤。
主题交换机通过通配符模式匹配,提供了高度灵活的路由能力,适用于需要多维度分类、动态订阅或层次化消息分发的场景。
三、扇出交换机(Fanout Exchange)
1、核心原理
扇出交换机是RabbitMQ中最简单的广播型消息路由机制。
- 生产者发送消息到扇出交换机时,路由键(Routing Key)会被完全忽略。
- 所有绑定到该交换机的队列都会收到消息的副本,无论队列的绑定键是什么。
- 本质是“发布/订阅”模式:消息会被广播到所有关联的队列。
常见的特性:
- 无路由键匹配:路由键在发送时无效(可随意填写或为空)。
- 广播机制:消息会被复制并分发给所有绑定队列。
- 动态绑定:队列可在任何时刻绑定到交换机,实时生效。
- 性能高效:无需复杂匹配逻辑,适合高频消息分发。
常用于的场景:
- 实时通知系统:例如向所有在线用户广播系统公告。
- 日志聚合:将日志消息广播到多个处理服务(存储、分析、报警)。
- 事件驱动架构:多个服务需要同时响应同一事件(如订单创建事件)。
- 缓存同步:多个缓存节点需要同时接收数据更新消息。
2、Java代码示例
1、添加依赖(Maven)
<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.14.2</version>
</dependency>
2、生产者(发送广播消息)
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;public class FanoutExchangeProducer {private static final String EXCHANGE_NAME = "fanout_notifications";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明扇出交换机(如果不存在则创建)channel.exchangeDeclare(EXCHANGE_NAME, "fanout");// 发送消息(路由键可任意填写,实际会被忽略)String message = "Broadcast message to all queues!";channel.basicPublish(EXCHANGE_NAME, "any_routing_key", // 此处路由键无效null, message.getBytes("UTF-8"));System.out.println(" [x] Sent: '" + message + "'");}}
}
3、消费者(接收广播消息)
import com.rabbitmq.client.*;public class FanoutExchangeConsumer {private static final String EXCHANGE_NAME = "fanout_notifications";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 声明扇出交换机channel.exchangeDeclare(EXCHANGE_NAME, "fanout");// 创建临时队列(非持久化、独占、自动删除)String queueName = channel.queueDeclare().getQueue();// 将队列绑定到扇出交换机(绑定键无效,可填写空字符串)channel.queueBind(queueName, EXCHANGE_NAME, "");System.out.println(" [*] Waiting for messages. Queue: " + queueName);// 定义消息处理回调DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [x] Received: '" + message + "'");};// 监听队列消费消息channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});}
}
启动两个消费者:
- 消费者A创建队列
amq.gen-123
并绑定到fanout_notifications
。 - 消费者B创建队列
amq.gen-456
并绑定到同一交换机。
生产者发送消息:
- 消息发送到
fanout_notifications
交换机。 - 交换机会将消息复制并发送到
amq.gen-123
和amq.gen-456
两个队列。
消费者接收结果:
- 消费者A和消费者B会同时收到相同的消息副本。
4、注意事项:
-
示例中使用临时队列(
queueDeclare()
无参调用),实际生产环境需根据需求设置队列属性:// 持久化队列(服务器重启后保留)boolean durable = true;channel.queueDeclare("persistent_queue", durable, false, false, null);
-
示例中自动确认消息(
autoAck=true
),实际场景建议手动确认以保证可靠性:channel.basicConsume(queueName, false, deliverCallback, consumerTag -> {}); // 在消息处理完成后手动确认 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
-
广播机制会导致消息被复制多份,需注意网络带宽压力(高并发场景需评估)和消费者处理能力(避免广播风暴)。
5、扩展使用:
- 组合其他交换机:先用
Fanout Exchange
广播消息,再用Direct Exchange
精细化处理特定任务。- 例如:订单创建事件广播 → 库存服务扣减库存,日志服务记录日志,通知服务发送短信。
- 动态订阅系统:新服务上线时自动绑定到扇出交换机,立即接收所有消息。服务下线时解绑队列,停止接收消息。
- 多个缓存节点绑定到同一扇出交换机,接收数据更新消息以保持一致性。
扇出交换机通过简单粗暴的广播机制,为需要 一对多
消息分发的场景提供了高效解决方案。
四、头交换机(Headers Exchange)
1、核心原理
头交换机是RabbitMQ中基于消息头(Headers)键值对匹配的路由机制。
- 路由键(Routing Key)完全被忽略:消息的路由不依赖路由键,而是基于消息头的属性。
- 绑定规则:队列通过声明一组键值对条件与交换机绑定,消息头需满足这些条件才能路由到队列。
- 匹配模式:支持
x-match
参数指定匹配规则:all
:消息头需包含所有声明的键值对(逻辑与)。any
:消息头只需包含任意一个声明的键值对(逻辑或)。
核心特性:
- 不依赖路由键:路由键(如
basicPublish
中的routingKey
)在头交换机中无意义。 - 灵活匹配规则:支持复杂逻辑(
all
或any
)匹配消息头的多个属性。 - 键值对匹配:匹配基于消息头的键值对,值可以是字符串或数值类型。
- 适用特殊场景:常用于需要多条件组合路由的场景,例如根据消息的元数据(如版本、环境)分发。
适用场景:
- 多条件路由:需要同时满足多个消息属性(如
type=order
且priority=high
)。 - 协议转换:消息头可携带协议元数据(如
format=json
、version=2
),路由到对应处理器。 - 环境隔离:根据
env=prod
或env=test
将消息路由到不同环境队列。 - A/B测试:通过消息头标记用户分组(如
group=A
),路由到不同实验队列。
2、Java代码示例
1、添加依赖(Maven)
<dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.14.2</version>
</dependency>
2、生产者(发送消息)
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.util.HashMap;
import java.util.Map;public class HeadersExchangeProducer {private static final String EXCHANGE_NAME = "headers_logs";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明头交换机channel.exchangeDeclare(EXCHANGE_NAME, "headers");// 定义消息头Map<String, Object> headers = new HashMap<>();headers.put("type", "order");headers.put("priority", 1);headers.put("format", "json");// 设置消息属性(包含Headers)AMQP.BasicProperties props = new AMQP.BasicProperties.Builder().headers(headers).build();// 发送消息(路由键被忽略,可填任意值)String message = "Order message with headers!";channel.basicPublish(EXCHANGE_NAME, "", // 路由键无意义props, message.getBytes("UTF-8"));System.out.println(" [x] Sent: '" + message + "'");}}
}
3、消费者(接收消息)
import com.rabbitmq.client.*;
import java.util.HashMap;
import java.util.Map;public class HeadersExchangeConsumer {private static final String EXCHANGE_NAME = "headers_logs";private static final String QUEUE_NAME = "order_high_priority";private static final String HOST = "10.106.182.54";private static final String USERNAME = "root";private static final String PASSWORD = "123456";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost(HOST);factory.setUsername(USERNAME);factory.setPassword(PASSWORD);Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 声明头交换机channel.exchangeDeclare(EXCHANGE_NAME, "headers");// 声明队列channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 定义绑定条件(需同时满足 type=order 且 priority=1)Map<String, Object> bindingArgs = new HashMap<>();bindingArgs.put("type", "order");bindingArgs.put("priority", 1);bindingArgs.put("x-match", "all"); // 匹配模式:all 或 any// 绑定队列到交换机channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "", bindingArgs);System.out.println(" [*] Waiting for messages...");// 定义消息处理回调DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");Map<String, Object> headers = delivery.getProperties().getHeaders();System.out.println(" [x] Received: '" + message + "'");System.out.println(" Headers: " + headers);};// 监听队列消费消息channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});}
}
队列绑定条件:
- 队列A绑定条件:
type=order
且priority=1
(x-match=all
)。 - 队列B绑定条件:
format=json
或format=xml
(x-match=any
)。
消息头匹配:
- 消息头
{type=order, priority=1, format=json}
→ 同时匹配队列A和队列B(若队列B的绑定条件为x-match=any
)。 - 消息头
{type=payment, format=json}
→ 仅匹配队列B(若绑定条件为x-match=any
)。
4、注意事项
- 键值对类型限制:消息头的值可以是字符串或数值类型,其他类型(如对象)需序列化处理。
- 头交换机的匹配逻辑较复杂(需遍历所有绑定条件),性能略低于直连或扇出交换机,但在合理设计下仍可高效运行。
- 避免过度复杂的绑定条件(如大量
x-match=all
的组合),可能增加匹配开销。 - 绑定队列时
routingKey
参数被忽略,但RabbitMQ API
要求该参数存在(示例中传递空字符串)。
5、扩展应用
- 动态路由配置:根据消息头中的
version
和region
动态路由到不同服务版本或地域的队列。 例如:version=2
且region=us-east
→ 路由到 2版的us-east
。 - 灰度发布:通过消息头标记用户ID的哈希值(如
user_hash=30
),将部分用户请求路由到新功能队列。 - 协议适配:消息头携带
content-type=avro
,路由到Avro
格式解析服务;携带content-type=protobuf
,路由到Protobuf
解析服务。
头交换机通过灵活的消息头键值对匹配机制,为需要复杂多条件路由的场景提供了强大的支持。
五、四种交换机对比
交换机类型 | 路由规则 | 适用场景 |
---|---|---|
Direct | 精确匹配路由键 | 明确指定目标队列的场景 |
Fanout | 广播到所有绑定队列(忽略路由键) | 发布/订阅模式(如通知广播) |
Topic | 通配符匹配(* 或 # ) | 灵活的多条件路由(如分类消息) |
Headers | 基于消息头键值对匹配 | 复杂路由逻辑(不依赖路由键) |
相关文章:
RabbitMQ 四种交换机(Direct、Topic、Fanout、Headers)详解
本文是博主在梳理 RabbitMQ 知识的过程中,将所遇到和可能会遇到的基础知识记录下来,用作梳理 RabbitMQ 的整体架构和功能的线索文章,通过查找对应的知识能够快速的了解对应的知识而解决相应的问题。 文章目录 一、直连交换机(Dire…...
聚合分销系统开发:短剧小说外卖网盘电商cpscpa系统
聚合分销系统是一种整合了多种分销项目和功能的综合性平台,其核心在于通过CPS(按销售付费)和CPA(按行为付费)两种模式,为推广者提供多样化的赚钱机会。以下是聚合分销系统的主要项目和功能: 一…...
【Flume 】Windows安装步骤、配置环境
🛠 Flume 是什么? Apache Flume 是一个高效、可靠、可扩展的数据收集系统,通常用于收集日志、流数据,比如收集数据到 HDFS、Kafka 等。 虽然 Flume 本身是为 Linux 服务器设计的,但 在 Windows 本地也是能跑起来的&a…...
【信息系统项目管理师】高分论文:论质量管理和进度管理(智慧旅游平台建设项目)
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文1、规划质量管理2、质量保证3、质量控制论文 2019年3月,我作为项目经理,参加了某市智慧旅游平台建设项目,负责项目的全面管理, 该项目以打造一流的国内外生态旅游城市为目标,旨在大数据云平台建设的基…...
一致性哈希详解:优雅地扩展分布式系统
引言 对于哈希算法,相信大家一定不会陌生。它经常被用在负载均衡、分库分表等场景中。例如,在进行分库分表时,我们可能初步根据业务分析,确定 128 张表足以满足当前的数据量需求。此时,当需要插入或查询一条记录时&am…...
pytest 技术总结
目录 一 pytest的安装: 二 pytest有三种启动方式: 三 用例规则: 四 配置框架: 一 pytest的安装: pip install pytest # 安装 pip install pytest -U # 升级到最新版 二 pytest有三种启动方式: 1…...
数据库MySQL学习——day5(总结与复习实践)
文章目录 1、复习总结1.1. 数据库基础1.2. 表操作1.3. 数据操作1.4. 更新与删除 2、实践任务:创建学生管理系统数据库2.1. 数据库设计2.2. 创建表的SQL语句2.3. 插入示例数据2.4. 查询与数据操作示例 3、调试与练习4、 今日小结 1、复习总结 1.1. 数据库基础 数据…...
unity bug
发现一个奇怪的bug,就是某些unity版本打包apk时候不允许StreamingAssets里面有中文文件或者中文路径。比如下图这面这俩都是不行的。 解决方案:中文改为英文即可。 一般报错信息如下: > Configure project :launcher WARNING:The option s…...
苹果计划2026年底前实现美版iPhone“印度造”,以减轻关税及地缘政治风险
基于 6 个来源 据多家媒体报道,苹果公司计划在2026年底前,实现在印度组装销往美国的大部分或全部iPhone手机,以减轻关税和地缘政治紧张局势带来的风险。这一目标意味着苹果需将印度的iPhone产量增加一倍以上,凸显其供应链多元化战…...
新增Webhook通知功能,文档目录树展示性能优化,zyplayer-doc 2.5.1 发布啦!
zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具,支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件,支持基于知识库的AI问答,专为私有化部署而设计,最大程度上保证企业或…...
【量化交易笔记】17.多因子的线性回归模型策略
前言 上一篇介绍了 因子的评价和分析方法,让我知道如何判断该因子的作用,以及对最终结果的影响,其最大的问题,他只能评价和分析单因子,而对多个因子,不能直接加以评价。我们自然会想到,如果是多…...
五年经验Java开发如何破局创业
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息文章目录 五年经验Java开发如何破局创业一、创业方向筛选与优劣势分析**方向1:技术教育/在线课程开发****方向2:企业级技术服务外包****方向3:技…...
定制一款国密浏览器(11):SM2算法的椭圆曲线参数定义
在国密算法中,SM2 算法是最复杂的,不仅是算法本身比较复杂,其应用场景也复杂。不管 SM2 算法本身有多复杂,作为开发者,我们需要知道的是 SM2 算法是建立在椭圆曲线算法(ECC)之上。关于 SM2 算法和椭圆曲线算法之间的关系,参考我之前的一篇文章: 解读国密非对称加密算…...
RAG技术与应用---0426
大语言模型>3.10 课程中会用到python 工具箱: faiss,modelscope,langchain,langchain_community,PyPDF2 1)大模型应用开发的三种模式 提示词没多少工作量,微调又花费时间费用,RAG是很多公司招聘用来对LLM进行应用…...
STM32的开发环境介绍
目录 STM32软件环境 Keil软件在线安装 其他软件环境安装 STM32开发的几种方式 STM32寄存器版本和库函数版本 标准外设库的作用: STM32软件环境 STM32 的集成开发环境(IDE):编辑编译软件 常见的环境: (1)KEIL&a…...
【生成式AI】从原理到实践的创造性革命
目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现(文生图) 三、性能对比测试方法论量化数据对比结果分析 四、最佳实践推荐方…...
Win下Pycharm运行/调试配置脚本形参执行替换Linux下终端执行,进行调试需要注意的
Linux下终端执行 python demo/image_demo.py demo/demo.jpg rtmdet_tiny_8xb32-300e_coco.py --weights rtmdet_tiny_8xb32-300e_coco_20220902_112414-78e30dcc.pth --device cpuWin下Pycharm运行/调试配置脚本形参执行 主要改红色两处 如果工作目录正确,脚本形参…...
Pytorch(无CPU搭建)+Jupyter
2024年最新最简洁深度学习环境配置:AnacondaPyTorch(CPU、GPU)VScodePycahrm_哔哩哔哩_bilibili 跟 PyCharm說再見, [VSCode] PythonJupyter 超牛逼的功能 ! 5分鐘大幅提升編碼效率~ 數據分析、AI大神必備_哔哩哔哩_bilibili...
类的高级特性与语法细节
static 静态关键字 Java中的static关键字用于修饰类的成员(属性或方法),表示“静态”的含义,即属于类本身,而非某个对象。静态成员在内存中只有一份,在类加载时初始化,生命周期贯穿程序运行始终…...
基于 RAG 的 Text2SQL 全过程的 Python 实现详解,结合 LangChain 框架实现自然语言到 SQL 的转换
什么是RAG 一、核心流程:三阶段协同 RAG的核心流程分为检索(Retrieval)、增强(Augmentation)、生成(Generation)三个阶段,形成“检索→知识整合→生成”的闭环。 1. 检索ÿ…...
使用 OpenCV 进行视觉图片调整的几种常见方法
以下是使用 OpenCV 进行视觉图片调整的几种常见方法: 调整图片大小 指定目标尺寸:使用cv2.resize()函数,通过设定目标图像的宽度和高度来调整图片大小。例如,将图片调整为 200x200 像素: import cv2 image cv2.imre…...
【特殊场景应对9】视频简历的适用场景与风险分析
写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…...
Dify 1.3.0 为 LLM 节点引入了结构化输出支持
Dify 1.3.0 为 LLM 节点引入了结构化输出支持 0. 引言1. 使用方法 0. 引言 Dify 1.3.0 开始,在 LLM 节点支持结构化输出:Dify 已经为 LLM 节点引入了结构化输出支持。这意味着您的语言模型现在可以返回整齐组织且易于处理的数据。后端实现由 Nov1c444 在…...
【Linux网络】HTTP协议全解析 - 从请求响应到方法与Header
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
JSP实现用户登录注册系统(三天内自动登录)
JSP实现用户登录注册系统 引言 在Web开发中,用户认证是最基础且核心的功能之一。本文基于JSP技术,实现了一个包含注册、登录、自动登录(3天内)、退出等功能的用户系统,并在过程中解决了Cookie字符错误、错误信息回显…...
大数据模型现状分析
大数据模型现状分析 一、引言 在当今数字化时代,数据以前所未有的速度增长,大数据已成为推动各行业发展的核心动力。大数据模型作为挖掘数据价值的关键工具,正受到广泛关注与深入研究。通过对海量、多样且高速产生的数据进行处理和分析&…...
代码随想录算法训练营第二十八天
LeetCode题目: 509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯2444. 统计定界子数组的数目(每日一题) 其他: 今日总结 往期打卡 动态规划解题步骤: 确定递推公式确定遍历顺序记忆化搜索(确定dp数组以及下标的含义与初始化值)递推优化与空间优化 509. 斐波那契数 跳转: 5…...
HTML与安全性:XSS、防御与最佳实践
HTML 与安全性:XSS、防御与最佳实践 前言 现代 Web 应用程序无处不在,而 HTML 作为其基础结构,承载着巨大的安全责任。跨站脚本攻击(XSS)仍然是 OWASP Top 10 安全威胁之一,对用户数据和网站完整性构成严…...
三维重建(二十)——思路整理与第一步的进行
文章目录 一、整体思路二、细分三、之前存在问题四、任务安排五、第一步——找到内参并选定一种5.1 train的RTK5.2 test的RTK5.3 各选择一个5.3.1 train-185.3.2 test-193一、整体思路 这部分主要是宏观的讲一下整体框架。 从gshell里面提取核心参数,放入py3d,渲染出图片,…...
判断 ONNX 模型是否支持 GPU
🔍 判断 ONNX 模型是否支持 GPU 的几个关键点: ✅ 1. 检查模型支持的 Execution Provider 可以通过下面的代码打印出来当前模型使用了什么设备: 需要安装好:onnxruntime-gpu import onnxruntime as ort session ort.InferenceSe…...
CANFD技术在实时运动控制系统中的应用:协议解析、性能测试与未来发展趋势
摘要: 本文深入探讨了CANFD技术在实时运动控制系统中的应用。通过对传统CAN协议与CANFD协议的对比分析,详细阐述了CANFD在提升数据传输效率、增强系统实时性与稳定性方面的优势。文章结合具体测试案例,对CANFD总线的性能指标进行了全面评估&a…...
Java基础 4.26
1.访问修饰符细节 package com.logic.modifier;public class A {public int n1 100;protected int n2 200;int n3 300;private int n4 400;public void m1() {//在同一个类中 可以访问public protected 默认 private 修饰属性和方法System.out.println(n1 " " …...
山东大学离散数学第九章习题解析
参考教材:离散数学教程,徐秋亮 / 栾俊峰 / 卢雷 / 王慧 / 赵合计 编著,山东大学计算机科学与技术学院 注:该解析为个人所写,涵盖了 2022-2023-2 学期赵合计老师所布置的所有课本习题;由于学识、认识及经验…...
5G融合消息PaaS项目深度解析 - Java架构师面试实战
5G融合消息PaaS项目深度解析 - Java架构师面试实战 场景:互联网大厂Java求职者面试,面试官针对5G融合消息PaaS项目进行提问。 第一轮提问 面试官:马架构,请简要介绍5G融合消息PaaS平台的核心功能和应用场景。 马架构ÿ…...
React-Redux
1、安装 npm i redux react-redux reduxjs/toolkit 2、基础使用方式(无 Toolkit) (1)核心Api createStore:创建数据仓库;store.dispatch():用于派发action,执行修改动作…...
Linux基础篇、第4章_03系统磁盘高级管理LVM 逻辑卷管理器
题目:系统磁盘高级管理LVM 逻辑卷管理器 版本号: 1.0,0 作者: 老王要学习 日期: 2025.04.26 适用环境: Centos7 文档说明 本文档聚焦于 Centos7 系统下的磁盘高级管理,围绕 LVM 逻辑卷管理器展开。详细介绍了物理卷、卷组和逻辑卷的创建、管理与删除操…...
代码随想录算法训练营Day36
力扣1049.最后一块石头的重量Ⅱ【medium】 力扣474.一和零【meidum】 一、力扣1049.最后一块石头的重量Ⅱ【medium】 题目链接:力扣1049.最后一块石头的重量Ⅱ 视频链接:代码随想录 1、思路 把这个问题转换成尽可能将 stones 分成两个等分子集…...
iperf网络性能测试
iperf 是一个网络性能测试工具,用于测量网络带宽、延迟、抖动等性能指标。它支持 TCP 和 UDP 协议,可以在客户端和服务器模式下运行,广泛用于网络性能评估和故障排查。 主要功能 带宽测试:测量网络的最大可用带宽。延迟测试&…...
基于 Nginx 的 WebSocket 反向代理实践
一、HTTP 协议升级机制回顾 Upgrade/Connection 报头 客户端发起 WebSocket 握手时,会在普通 HTTP 请求中加入Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: <随机值> Sec-WebSocket-Version: 13服务端若接受协议切换,会以 101 Swit…...
C++ 同步原语
同步原语(Synchronization Primitives)是操作系统和编程语言提供的基本工具,用于在多线程或并发环境中协调线程(或进程)之间的执行顺序,管理共享资源的访问,以避免数据竞争(data rac…...
mmap详解
mmap详解 mmap基础概念mmap内存映射原理mmap相关函数调用mmap的使用细节mmap和常规文件操作的区别 mmap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一…...
基于大模型底座重构司法信息系统
前置篇章:法律智能体所需的基础知识 构建一个高效的法律智能体,特别是在基于RAG(Retrieval-Augmented Generation)架构的背景下,需要融合多种学科和领域的知识。以下是对法律智能体开发和应用所需核心基础知识的简要介…...
如何判断你的PyTorch是GPU版还是CPU版?
如何判断你的PyTorch是GPU版还是CPU版? PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIA CUDA)上运行。对于深度学习开发者来说,正确识别PyTorch版本至关重要,因为GPU版本可以带来10-100倍的性能提升。本文将全面…...
Leetcode刷题记录19——无重复字符的最长子串
题源:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述: 思路一: 通过两个指针,第一个指针指向字串的开头,第二…...
SpringBoot程序的创建以及特点,配置文件,LogBack记录日志,配置过滤器、拦截器、全局异常
一、创建一个SpringBoot程序 在之前写过一篇如何创建SpringBoot程序,两种方式,方法1:通过maven创建SpringBoot项目 方法2:使用Spring Initialzr创建一个SpringBoot项目(缺点:当创建项目时网络中断&#x…...
Ubuntu20.04 Ollama 配置相关
Ubuntu20.04 Ollama 配置相关 Ubuntu20.04 Ollama 配置相关ollama修改配置文件常用命令修改端口局域网访问 Ubuntu20.04 Ollama 配置相关 ollama修改配置文件常用命令 sudo gedit /etc/systemd/system/ollama.service systemctl daemon-reload systemctl restart ollama sys…...
python调用ffmpeg对截取视频片段,可批量处理
本文完全免费,非VIP文章,如果您发现需VIP可看全文,请邮箱联系我:openwebsitefoxmail.com 文章目录 python调用ffmpeg对截取视频片段,可批量处理用到的ffmpeg命令python调用bash指令的方法python处理代码准备函数python…...
【WLAN】华为无线AC双机热备负载分担—双链路热备份
配套实验拓扑可以下载学习交流:【WLAN】华为无线AC双机负载分担—双链路热备份 双链路备份的传统配置方式是在主、备AC上为AP指定对方AC的IP地址,并分别配置优先级,通过比较优先级的方式来确定主、备AC。为简化配置逻辑,新配置方式…...
学习笔记——《Java面向对象程序设计》-内部类、匿名类、异常类
参考教材: Java面向对象程序设计(第3版)微课视频版 清华大学出版社 1、内部类 类中可以有两种重要的成员:成员变量和方法。实际上Java还允许类可以有一种成员:内部类。 内部类可以使用其外嵌类中的成员变量&#x…...
BS架构与CS架构的对比分析:了解两种架构的不同特点与应用
目录 前言1. BS架构概述1.1 什么是BS架构?1.2 BS架构的主要特点 2. CS架构概述2.1 什么是CS架构?2.2 CS架构的主要特点 3. BS架构与CS架构的对比3.1 用户体验3.2 安全性3.3 适用场景 4. 结语 前言 在现代软件开发中,架构设计决定了应用的性能…...