【RabbitMQ】交换机详解看这一篇就够了

🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的专栏《RabbitMQ实战》。🎯🎯

👉点击这里,就可以查看我的主页啦!👇👇

Java方文山的个人主页

🎁如果感觉还不错的话请给我点赞吧!🎁🎁

💖期待你的加入,一起学习,一起进步!💖💖

请添加图片描述

目录

✨前言

什么是交换机

使用交换机的好处

一、交换机(Exchange)的类型

1.直连交换机:Direct Exchange

2.主题交换机:Topic Exchange

3.扇形交换机:Fanout Exchange

4.首部交换机:Headers exchange

5.默认交换机:Default Exchange

6.死信交换机:Dead Letter Exchange

 二、案例演示:交换机的使用

2.1直连交换机(Direct Exchange)

2.2主题交换机(Topic Exchange)

2.3扇形交换机(Fanout Exchange)


✨前言

什么是交换机

在 RabbitMQ 消息队列系统中,交换机(Exchange)是用于接收生产者发送的消息并将其路由到相应的队列的组件。它是消息路由的核心组件之一。使用交换机将生产者的消息分发到队列中可以处理更为复杂的代码或者说可以更精准的发送到队列中。

有了交换机我们的消息不是直接发给队列的而是发送给交换机再通过特殊的条件找到符合条件的队列再由队列发送给消费者,但这中间还有些概念需要我们了解一下分别是路由键和绑定键

  • 路由键(RoutingKey)

生产者将消息发送给交换机的时候会指定RoutingKey指定路由规则

  • 绑定键(BindingKey)

通过绑定键将交换机与队列关联起来,这样RabbitMQ就知道如何正确将消息路由到队列

总结:生产者将消息发给那个Exchanges是路由键决定的,而Exchanges与那个队列绑定是通过邦定键决定的。

使用交换机的好处

刚刚提到使用交换机将消息发送到队列中比直接发送到队列中好,那么具体好在那里我这里举个例

  1. 路由控制:通过交换机,可以根据消息的路由键将消息路由到与之匹配的队列。这样,可以根据消息的属性或标签来定向分发消息,实现精确的消息路由控制。

  2. 消息过滤:交换机可以根据消息的路由键、消息头部属性等信息对消息进行过滤和筛选,将符合特定条件的消息发送到相应的队列。这样可以实现消息的订阅和过滤机制,灵活地处理不同类型的消息。

  3. 广播和多播:通过使用扇形交换机(Fanout Exchange),可以将消息广播到所有与之绑定的队列,实现消息的广播和多播机制,方便实现发布-订阅模式。

  4. 解耦和灵活性:通过将消息发送到交换机而不是直接发送到队列,生产者和消费者之间实现了解耦。生产者只需要将消息发送到指定的交换机,而不需要知道具体的队列。这样,可以灵活地增加、删除或修改队列,而不会对生产者产生影响。

  5. 可扩展性:使用交换机可以实现消息的分发和负载均衡机制。通过将消息发送到多个队列,可以实现横向扩展和并发处理,提高系统的吞吐量和性能。

如果你还不是很理解的话不妨看看这个案例:

假设我们正在构建一个电子商务网站,该网站有一个订单处理系统。在用户提交订单后,需要执行一系列异步任务,例如生成发货单、发送通知邮件、更新库存等。这些任务可能会消耗较长的时间,并且需要并行处理。

在这种情况下,我们可以使用消息队列系统来实现任务的异步处理。具体来说,我们可以创建一个交换机和多个队列,并将每个异步任务绑定到不同的队列上。以下是具体的实现步骤:

  1. 创建一个交换机,例如名为 "order_exchange" 的主题交换机。
  2. 创建多个队列,例如 "shipping_queue"、"notification_queue" 和 "inventory_queue"。
  3. 将 "shipping_queue" 绑定到交换机,并指定路由键为 "shipping"。
  4. 将 "notification_queue" 绑定到交换机,并指定路由键为 "notification"。
  5. 将 "inventory_queue" 绑定到交换机,并指定路由键为 "inventory"。
  6. 当用户提交订单时,订单处理系统将生成一条消息并发送到交换机。
  7. 根据任务类型,将消息的路由键设置为相应的值,例如 "shipping"、"notification" 或 "inventory"。
  8. 消息队列系统将根据消息的路由键将消息路由到相应的队列中。
  9. 每个队列对应一个消费者,负责处理特定的任务。例如,"shipping_queue" 的消费者可以从队列中获取消息,并生成发货单;"notification_queue" 的消费者可以发送通知邮件;"inventory_queue" 的消费者可以更新库存。

通过以上步骤,我们实现了任务的解耦和灵活性。生产者只需要将消息发送到交换机,而不需要关心具体的任务队列。每个任务都有独立的队列和消费者,可以并行处理,提高系统的吞吐量和性能。同时,我们可以根据需要增加或删除队列,扩展系统的处理能力

 

一、交换机(Exchange)的类型

了解完交换机的概念,我们来认识一下交换机的类型

1.直连交换机:Direct Exchange

直连交换机是最简单的交换机类型,它将消息的路由键与绑定键进行精确匹配,当我们的路由键和绑定键一致的时候,将消息发送到与之完全匹配的队列。

像上图所描述的我们的路由键是orange就会进入对应的绑定键的orange队列中

 

 如果是两个相同的绑定键则都会进入,同时推送到Q1和Q2队列中

 注意:直连交换机只能根据绑定键进行路由,无法实现更复杂的路由逻辑。这意味着在需要进行高级路由或消息过滤的情况下,直连交换机可能无法满足需求。如果我们需要一个消息发送到多个队列中需要在交换机上绑定多个路由键,也是非常的麻烦


2.主题交换机:Topic Exchange

主题交换机基于模式匹配的方式将消息路由到队列。它使用通配符来进行匹配,支持通配符符号 "*" 和 "#"。其中 "*" 表示匹配一个单词,"#" 表示匹配一个或多个单词。

为了方便大家理解我写几个案例:

  •  RoutingKey:aa.orange.bb ====>Q1
  •  RoutingKey:aa.orange.rabbit====>Q1,Q2
  •  RoutingKey:aa.bb.rabbit====>Q2
  •  RoutingKey:lazy.aa====>Q2
  •  RoutingKey:lazy.aa.rabbit====>Q2
  •  RoutingKey:lazy.orange.aa====>Q1,Q2
  •  RoutingKey:lazy.orange.rabbit====>Q1,Q2

主题交换机就像是升级版的直连交换机一样,通过绑定键进行访问,但是不同的地方是主题交换机有两个特殊字符一个是*号另一个是#,通过这两个符合可以设定不同的条件,只有符合条件才会发送到对应的队列中。

知识拓展:

  1. 当一个队列的绑定键是#,它将会接收所有的消息,而不再考虑所接收消息的路由键
  2. 当一个队列的绑定键没有用到#和*时,它就像直连交换机一样工作


3.扇形交换机:Fanout Exchange

扇形交换机将消息广播到所有与之绑定的队列。无论消息的路由键是什么,扇形交换机都会将消息发送到所有绑定的队列中。这种类型的交换机常用于实现发布-订阅模式,将消息广播给多个消费者。


4.首部交换机:Headers exchange

首部交换机和扇形交换机一样都不要路由键,首交换机根据消息Headers的属性进行匹配和路由。在消息发送时,可以指定一组键值对作为消息的头部属性,交换机会根据这些属性进行匹配。首部交换机提供了更灵活的匹配方式,但相对复杂度较高,通常使用较少。

注意:Hash结构中要求携带一个键“x-match”,这个键的Value可以是any或者all,这代表消息携带的Hash是仅匹配一个(any)还是需要全部匹配(all)

  • all:在发布消息时携带的所有Entry必须和绑定在队列上的所有Entry完全匹配
  • any:只要发布消息时携带的有一对键值对headers满足队列定义的多个参数arguments的其中一个就能匹配上,注意这里是键值对的完全匹配,只要匹配到键,值却是不一样的


5.默认交换机:Default Exchange

默认交换机是一个预定义的无名交换机,它会自动将消息发送到与之路由键名称相同的队列中。当生产者没有显式地指定交换机时,消息会被发送到默认交换机中。

注意:默认交换机有一个特殊属性是默认交换机会自动将新建队列绑定到自己身上,并且绑定的路由键名称与队列名称一致。 也就是说当创建一个新的队列时,如果未显式地指定要绑定的交换机,那么该队列将自动绑定到默认交换机上。


6.死信交换机:Dead Letter Exchange

死信交换机用于处理无法被消费者正确处理的消息。当消息在队列中变成死信(例如超过重试次数或队列已满),它将被发送到死信交换机,并根据死信交换机的绑定规则路由到指定的死信队列中进行进一步处理。

通常是以下三种情况:

  • 消息被拒绝,并且设置 requeue 参数为 false
  • 消息过期(默认情況下 Rabbit 中的消息不过期,但是可以设置队列的过期时间和消息的过期时间以达到消息过期的效果)
  • 队列达到最大长度(一般当设置了最大队列长度或大小并达到最大值时)

满足以上的任意一种就会变成死信消息被我们的死信交换机接收到并发送给队列

案例讲解 

生产者生产一条1分钟后超时的订单消息到正常交换机exchange-a中,消息匹配到队列queue-a,但一分钟后仍未消费。 消息会被投递到死信交换机dlxy-exchange中,并发送到死信队列中, 死信队列dlx-queue的消费者拿到消息后,根据消息去查询订单的状态,如果仍然是未支付状态,将订单状态更新为超时状态。

 

 二、案例演示:交换机的使用

编写代码之前先了解一下我们交换机的参数属性

 Name交换机名称

Type:交换机类型,direct、 topic、fanout、 headers

Durability:是杏需要持久化,如果持久性,则RabbitMQ重启后,交换机还存在

Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该ExchangeInternal:

Internal:当前Exchange是否用于RabbitMQ内部使用,默认为FalseArguments:

Arguments:扩展参数,用于扩展AMQP协议定制化使用

 

2.1直连交换机(Direct Exchange)

🧑‍🍳生产者

package org.example.produce.config;import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {/*** 定义队列* @return*/@Beanpublic Queue directQueue(){return new Queue("direct-queue");}/*** 自定义直连交换机* @return*/@Beanpublic DirectExchange directExchange(){return new DirectExchange("direct-exchange",true,false);}/*** 将队列与交换机进行绑定,并设置路由键* @return*/@Beanpublic Binding binding(){return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct_routing_key");}}
  • directQueue() 方法:创建一个名为 "direct-queue" 的队列,并将其返回。
  • directExchange() 方法:创建一个名为 "direct-exchange" 的直连交换机,并将其返回。这个交换机是持久化的(durable = true),不自动删除(autoDelete = false)。
  • binding() 方法:将 directQueue() 方法返回的队列与 directExchange() 方法返回的交换机进行绑定,并设置路由键为 "direct_routing_key"。这样,当消息的路由键与 "direct_routing_key" 完全匹配时,消息将会被路由到这个队列中。

🧑‍💼消费者

package org.example.produce.controller;import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"direct-queue"})
public class DirectReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json){System.out.println(json);}
}

👇👇Controller

package org.example.produce.controller;import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class Sender {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("/sendData")public String sendData() {Map<String,Object> data=new HashMap<>();data.put("msg","hello 我是直连交换机");rabbitTemplate.convertAndSend("direct-exchange","direct_routing_key", data);return "😎";}
}

🌷🌷效果展示:

 

 

2.2主题交换机(Topic Exchange)

🧑‍🍳生产者

package org.example.produce.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {//  定义路由规则public static String A_KEY = "*.orange.*" ;public static String B_KEY = "*.* rabbit";public static String C_KEY = "lazy.#";/*** 定义队列* @return*/@Beanpublic Queue Topicqueue(){return new Queue("topic-queue",true);}/*** 定义主题交换机* @return*/@Beanpublic TopicExchange topicExchange(){return new TopicExchange("topic-exchange",true,false);}/*** 将队列与交换机进行绑定,并设置路由键* @return*/@Beanpublic Binding bindingA(){return BindingBuilder.bind(Topicqueue()).to(topicExchange()).with(A_KEY);}}
  • 路由规则的定义:
    A_KEY 定义了一个路由键为 ".orange." 的规则,其中 "*" 表示匹配任意一个单词,这个规则可以匹配以 "orange" 为中间单词的路由键。
    B_KEY 定义了一个路由键为 ", rabbit" 的规则,其中 "," 表示按照单词划分,它可以匹配两个单词的路由键,且第二个单词为 "rabbit"。
    C_KEY 定义了一个路由键为 "lazy.#" 的规则,其中 "#" 表示匹配任意数量的单词,这个规则可以匹配以 "lazy" 开头并且后面可以是任意数量的单词的路由键。
  • Topicqueue() 方法: 创建了一个名为 "topic-queue" 的队列,并将其返回。此队列是持久化的。
  • topicExchange() 方法: 创建了一个名为 "topic-exchange" 的主题交换机,并将其返回。该交换机是持久化的,不自动删除。
  • binding() 方法: 将 Topicqueue() 方法返回的队列与 topicExchange() 方法返回的主题交换机进行绑定,并使用路由键 A_KEY 进行绑定。这样,当消息的路由键与 A_KEY 匹配时,消息将会被路由到这个队列中。

🧑‍💼消费者

package org.example.produce.controller;import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"topic-queue"})
public class TopicReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json){System.out.println(json);}}

👇👇Controller

package org.example.produce.controller;import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class Sender {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("/sendTopic")public String sendTopic() {Map<String,Object> data=new HashMap<>();data.put("msg","你好!! 我是主题交换机");rabbitTemplate.convertAndSend("topic-exchange","aa.orange.bb", data);return "😎";}
}

🌷🌷效果展示:

 

 

2.3扇形交换机(Fanout Exchange)

🧑‍🍳生产者

package org.example.produce.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {@Beanpublic Queue queueX(){return new Queue("queue-x");}@Beanpublic Queue queueY(){return new Queue("queue-y");}@Beanpublic Queue queueZ(){return new Queue("queue-z");}/*** 定义扇形交换机,与路由键无关* @return*/@Beanpublic FanoutExchange fanoutExchange(){return new FanoutExchange("fanout-exchange",true,false);}@Beanpublic Binding bindingX(){return BindingBuilder.bind(queueX()).to(fanoutExchange());}@Beanpublic Binding bindingY(){return BindingBuilder.bind(queueY()).to(fanoutExchange());}@Beanpublic Binding bindingZ(){return BindingBuilder.bind(queueZ()).to(fanoutExchange());}}

队列的定义:
queueX() 方法创建了一个名为 "queue-x" 的队列。
queueY() 方法创建了一个名为 "queue-y" 的队列。
queueZ() 方法创建了一个名为 "queue-z" 的队列。

  • fanoutExchange() 方法: 创建了一个名为 "fanout-exchange" 的扇形交换机,并将其返回。该交换机是持久化的,不自动删除。扇形交换机会忽略消息的路由键,而是将消息广播到绑定到它上面的所有队列中。
  • bindingX()、bindingY()、bindingZ() 方法: 分别将 queueX()、queueY()、queueZ() 返回的队列与 fanoutExchange() 返回的扇形交换机进行绑定。这样,当消息发送到扇形交换机时,它会被广播到所有绑定的队列中。

🧑‍💼消费者

package org.example.produce.controller;import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
public class FanoutReceiver {@RabbitListener(queues = {"queue-x"})@RabbitHandlerpublic void handlerY(Map<String,Object> json){System.out.println("已接受到队列queue-x传递过来的消息:"+json);}@RabbitListener(queues = {"queue-y"})@RabbitHandlerpublic void handlerX(Map<String,Object> json){System.out.println("已接受到队列queue-y传递过来的消息:"+json);}@RabbitListener(queues = {"queue-z"})@RabbitHandlerpublic void handlerZ(Map<String,Object> json){System.out.println("已接受到队列queue-z传递过来的消息:"+json);}
}

👇👇Controller

package org.example.produce.controller;import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class Sender {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("/sendFanout")public String sendFanout() {Map<String,Object> data=new HashMap<>();data.put("msg","我是扇形交换机,广播通知");rabbitTemplate.convertAndSend("fanout-exchange",null, data);return "😎";}
}

🌷🌷效果展示:

 

请添加图片描述

到这里我的分享就结束了,欢迎到评论区探讨交流!!

💖如果觉得有用的话还请点个赞吧 💖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/640648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每个人都可以是架构师,每个人都需要培养架构思维

您好&#xff0c; 如果喜欢我的文章或者想上岸大厂&#xff0c;可以关注公众号「量子前端」&#xff0c;将不定期关注推送前端好文、分享就业资料秘籍&#xff0c;也希望有机会一对一帮助你实现梦想 什么是架构 “架构”&#xff0c;即架设、构建。完成对于平台的合理架设&am…

VMware安装Linux-Redhat7.9 详细步骤

目录 一、安装准备二、安装步骤 一、安装准备 Redhat 7.9 镜像下载 VMware安装步骤可查看文章&#xff1a;https://blog.csdn.net/a2279338659/article/details/126346345 可去官网下载&#xff0c;或者加群下载镜像资源。 二、安装步骤 创建新的虚拟机&#xff1a; 我这边…

语音模块学习——LSYT201B模组(深圳雷龙科技)

目录 引子 处理器 外设 音频 蓝牙 模组展示 引子 关注我的老粉们应该知道我之前用过语音模块做东西&#xff0c;那个比较贵要50多。 今天这个淘宝20元左右比那个便宜&#xff0c;之前那个内核是51的&#xff0c;一个8位机。 后面我做东西的时候语音模块可能会换成这个&…

【计算机网络】Socket的TCP_NODELAY选项与Nagle算法

TCP_NODELAY是一个套接字选项&#xff0c;用于控制TCP套接字的延迟行为。当TCP_NODELAY选项被启用时&#xff0c;即设置为true&#xff0c;就会禁用Nagle算法&#xff0c;从而实现TCP套接字的无延迟传输。这意味着每次发送数据时都会立即发送&#xff0c;不会等待缓冲区的填充或…

代码随想录算法训练营DAY24|回溯1

算法训练DAY24|回溯1 第77题. 组合 力扣题目链接 给定两个整数 n 和 k&#xff0c;返回 1 ... n 中所有可能的 k 个数的组合。 示例: 输入: n 4, k 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 上面我们说了要解决 n为100&#xff0c;k为50的情况&#xff0…

vscode连不上虚拟机,一直密码错误

最近在做毕设&#xff0c;但是vscode使用连接不上虚拟机&#xff0c;我以为是网络配置的问题&#xff0c;一顿查阅没找到原因。 后来查了一下ssh的日志&#xff0c;发现ssh有消息&#xff0c;但是也提示密码错误。 没找到密码配置格式什么的&#xff0c;经查看sshd配置文件发现…

工业相机与镜头参数及选型

文章目录 1、相机成像系统模型1.1 视场1.2 成像简化模型 2、工业相机参数2.1 分辨率2.2 靶面尺寸2.3 像元尺寸2.4 帧率/行频2.5 像素深度2.6 动态范围2.7 信噪比2.8 曝光时间2.9 相机接口 3、工业镜头参数3.1 焦距3.2 光圈3.3 景深3.4 镜头分辨率3.5 工作距离&#xff08;Worki…

微信小程序入门,学习全局配置与页面配置

目录 一、微信小程序 二、微信小程序的全局配置 三、微信小程序的页面配置 四、全局配置与页面配置的区别 一、微信小程序 微信小程序是一种基于微信平台的应用程序&#xff0c;它可以在微信内部直接运行&#xff0c;无需下载安装。微信小程序具有以下特点和优势&#xff…

Spring Boot自动配置原理

1.SpringBootApplication注解 springboot是基于spring的新型的轻量级框架&#xff0c;最厉害的地方当属**自动配置。**那我们就可以根据启动流程和相关原理来看看&#xff0c;如何实现传奇的自动配置 SpringBootApplication//标注在某个类上&#xff0c;表示这个类是SpringBo…

<蓝桥杯软件赛>零基础备赛20周--第15周--快速幂+素数

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…

【设计模式】张一鸣笔记:责任链接模式怎么用?

我将通过一个贴近现实的故事——请假审批流程&#xff0c;带你了解和掌握责任链模式。 什么是责任链模式&#xff1f; 责任链模式是一种行为设计模式&#xff0c;它让你可以避免将请求的发送者与接收者耦合在一起&#xff0c;让多个对象都有处理请求的机会将这个对象连成一条…

同样是IT行业,测试和开发薪资真就差这么大吗?

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

前端学习笔记 7:小兔鲜

前端学习笔记 7&#xff1a;小兔鲜 准备工作 创建项目 创建项目&#xff1a; npm init vuelatest相关选项如下&#xff1a; 在src目录下添加以下目录&#xff1a; 别名路径联想 默认情况下在 VSCode 中输入import xxx from ...时不会启用路径联想功能&#xff0c;要启用需…

使用宝塔面板安装wiki.js详细教程

因为在安装过程中遇到了一些问题&#xff0c;花费了很长时间在解决问题上。根据这篇教程可以少踩很多坑。点赞加关注吧。 准备运行环境 Nodejs 在宝塔面板的软件商店中找到nodejs版本管理器并安装。 点击设置&#xff0c;选择一个稳定版安装。 PostgreSQL 官方推荐的数据库是…

用户洞察:精准解读用户的真实需求!

洞察用户需求的过程和谈恋爱一样。你不能简简单单地问客户&#xff0c;你想要什么&#xff1f;你有什么痛点&#xff1f;这样的问法是无法得到任何有价值的信息。这就好比谈恋爱的场景&#xff0c;如果你问对方想吃什么&#xff0c;大概率会得到“随便”“都行”这类的答案&…

力扣62. 不同路径

动态规划 思路&#xff1a; 定义 dp[r][c] 为到达坐标 (r, c) 的路径数&#xff1a; 它只能有同一行左边相邻方格向右到达或者同一列上方相邻方格向下到达&#xff1b;状态转移方程&#xff1a; dp[r][c] dp[r][c - 1] dp[r - 1][c]初始状态 dp[0][0] 1第一行的路径数是 1第…

2526. 随机数生成器(BSGS,推导)

题目路径&#xff1a; https://www.acwing.com/problem/content/2528/ 思路&#xff1a;

HNU-数据挖掘-实验1-实验平台及环境安装

数据挖掘课程实验实验1 实验平台及环境安装 计科210X 甘晴void 202108010XXX 文章目录 数据挖掘课程实验<br>实验1 实验平台及环境安装实验背景实验目标实验步骤1.安装虚拟机和Linux平台&#xff0c;熟悉Ubuntu环境。2.在Linux平台上搭建Python平台&#xff0c;并安装…

esp32-idf eclipse 分区表(partition table / NVS)的读写demo

前言&#xff1a; 分区表&#xff08;Partition Table&#xff09;和 NVS&#xff08;Non-Volatile Storage&#xff09;是 ESP-IDF 中用于存储数据的两种不同机制。 分区表&#xff08;Partition Table&#xff09;&#xff1a; 分区表定义了将 Flash 存储器划分为不同逻辑分…

RT-DETR 模型改进 | AKConv:具有任意采样形状和任意参数数量的卷积核

基于卷积操作的神经网络在深度学习领域取得了显著的成果,但标准卷积操作存在两个固有缺陷。一方面,卷积操作受限于局部窗口,无法捕捉其他位置的信息,而其采样形状是固定的。另一方面,卷积核的大小固定为kk,呈固定的正方形形状,而参数数量往往随大小呈平方增长。显然,不…