RabbitMQ基础
MQ 的基本概念
MQ全称Message Queue
实在消息的传输过程中保存的消息的容器,多用于分布式系统之间进行通信。
优势:应用解耦:减少程序的耦合程度,进而提高系统的维护性
异步提速:提高系统的性能
削峰填谷:减少高峰时期的并发请求的压力,提高系统的稳定性
劣势:系统可用性降低
系统复杂度提高
使用场景
- 需要提高程序的扩展性的时候
- 需要提高程序性能的时候
- 性能 :MQ
- 查询数据:Redis
- 高并发的时候
常见的MQ产品
- RabbitMQ
- ActiveMQ
- RocketMQ
- Kafka
RabbitMQ简介
什么是RabbitMQ?
- 定义:是一款基于erlang语言实现的MQ消息中间件产品,基于AMQP协议实现
组成部分:
- server
- 服务器broker
- Virtual Host:用于隔离不同业务的消息,类似数据库的概念
- exchange:消息交换机- 路由消息
- queue:消息存储队列 - 存储消息
- binding:绑定exchange和queue之间的联系,routingKey(过滤消息的条件)
- Virtual Host:用于隔离不同业务的消息,类似数据库的概念
- 服务器broker
- client
- 生产者producer
- 消费者consumer
- server
注意关系
- 一个Broker可以有多个Virtual Host
- exchange与queue之间多对多关系
总结
AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议
的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中
间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。类比HTTP。
就是生产者发布消息到交换机,然后交换机再通过路由的方式决定路由到哪个消息队列然后消费者从队列中监听拿走队列的消息进行消费RabbitMQ基础架构
Broker:接收和分发消息的应用,RabbitMQ Server就是 Message Broker
Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网
络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多
个vhost,每个用户在自己的 vhost 创建 exchange/queue 等Connection:publisher/consumer 和 broker 之间的 TCP 连接
Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection
的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线
程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和
message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection
极大减少了操作系统建立 TCP connection 的开销Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)
Queue:消息最终被送到这里等待 consumer 取走
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存
到 exchange 中的查询表中,用于 message 的分发依据
RabbitMQ提供了六种工作模式
简单模式
- work queues
- Publish/Subscribe发布与订阅模式
- Routing路由模式
- Topics主题模式
- RPC远程调用模式
JMS
- JMS 即 Java 消息服务(JavaMessage Service)应用程序接口,是一个 Java 平台中关于面向消息中间件
的API - JMS 是 JavaEE 规范中的一种,类比JDBC
- 很多消息中间件都实现了JMS规范,例如:ActiveMQ。RabbitMQ 官方没有提供 JMS 的实现包,但是开
源社区有
第一种工作模式(简单模式)
package com.itheima;import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** @author 陈朋* @date 2021/1/21 0021 19:26*/
public class ProducerHello {public static void main(String[] args) throws IOException, TimeoutException {//创建连接工厂ConnectionFactory factory = new ConnectionFactory();//设置参数factory.setHost("159.75.102.92");factory.setPort(5672);factory.setVirtualHost("itcast");factory.setUsername("itheima");factory.setPassword("itheima");//创建连接Connection connection = factory.newConnection();//创建ChannelChannel channel = connection.createChannel();/* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)参数:1. queue:队列名称2. durable:是否持久化,当mq重启之后,还在3. exclusive:* 是否独占。只能有一个消费者监听这队列* 当Connection关闭时,是否删除队列*4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉5. arguments:参数。*/channel.queueDeclare("helloWorld", true, false, false, null);/* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)参数:1. exchange:交换机名称。简单模式下交换机会使用默认的 ""2. routingKey:路由名称3. props:配置信息4. body:发送消息数据*/String body = "hello rabbitmq";channel.basicPublish("", "helloWorld", null, body.getBytes());
// //释放资源
// channel.close();
// connection.close();}}
package com.itheima;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** @author 陈朋* @date 2021/1/21 0021 19:49*/public class ConsumerHelloWorld {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();//ip 默认值 localhostfactory.setHost("159.75.102.92");//端口 默认值 5672factory.setPort(5672);//虚拟机 默认值/factory.setVirtualHost("itcast");//用户名 默认 guestfactory.setUsername("itheima");//密码 默认值 guestfactory.setPassword("itheima");Connection connection = factory.newConnection();Channel channel = connection.createChannel();//接受消息Consumer consumer = new DefaultConsumer(channel) {/***回调方法,当收到消息后,会自动执行该方法* @param consumerTag 消息队列的标识* @param envelope 获取一些信息 交换机,路由key...* @param properties 配置信息* @param body 数据* @throws IOException IO异常*/@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("consumerTag:" + consumerTag);System.out.println("Exchange:" + envelope.getExchange());System.out.println("RoutingKey:" + envelope.getRoutingKey());System.out.println("properties:" + properties);System.out.println("body:" + new String(body));}};/*basicConsume(String queue, boolean autoAck, Consumer callback)参数:1. queue:队列名称2. autoAck:是否自动确认3. callback:回调对象*/channel.basicConsume("helloWorld", true, consumer);}
}
Work queues 工作队列模式
- Work Queues:与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息
- 应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
- Work Queues 与入门程序的简单模式的代码几乎是一样的。可以完全复制,并多复制一个消费者进行多
个消费者同时对消费消息的测试。 - 在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争的关系
- Work Queues 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。例如:短信服务部署多个,
只需要有一个节点成功发送即可。
Work Queues 与入门程序的简单模式的代码几乎是一样的。可以完全复制,并多复制一个消费者进行多
个消费者同时对消费消息的测试。
Pub/Sub 订阅模式
在订阅模型中,多了一个 Exchange 角色,而且过程略有变化:
- P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- C:消费者,消息的接收者,会一直等待消息到来
- Queue:消息队列,接收消息、缓存消息
- Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合
路由规则的队列,那么消息会丢失!
package com.itheima;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;
/*** @author 陈朋* @date 2021/1/21 0021 19:59*/
public class ProducerPubSub {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("159.75.102.92");factory.setPort(5672);factory.setVirtualHost("itcast");factory.setUsername("itheima");factory.setPassword("itheima");Connection connection = factory.newConnection();Channel channel = connection.createChannel();/* exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)参数:1. exchange:交换机名称2. type:交换机类型DIRECT("direct"),:定向FANOUT("fanout"),:扇形(广播),发送消息到每一个与之绑定队列。TOPIC("topic"),通配符的方式HEADERS("headers");参数匹配3. durable:是否持久化4. autoDelete:自动删除5. internal:内部使用。 一般false6. arguments:参数 */String exchangeName = "test_fanout";//创建交换机channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);//创建队列String queue1Name = "test_fanout_queue1";String queue2Name = "test_fanout_queue2";channel.queueDeclare(queue1Name, true, false, false, null);channel.queueDeclare(queue2Name, true, false, false, null);//绑定队列和交换机/* queueBind(String queue, String exchange, String routingKey)参数:1. queue:队列名称2. exchange:交换机名称3. routingKey:路由键,绑定规则如果交换机的类型为fanout ,routingKey设置为"" */channel.queueBind(queue1Name,exchangeName,"");channel.queueBind(queue2Name,exchangeName,"");String body = "日志信息:张三调用了findAll方法...日志级别:info...";//发送消息channel.basicPublish(exchangeName,"",null,body.getBytes());//释放资源channel.close();connection.close();}
}
class Consumer_PubSub1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory = new ConnectionFactory();//2. 设置参数factory.setHost("159.75.102.92");factory.setPort(5672);factory.setVirtualHost("itcast");factory.setUsername("itheima");factory.setPassword("itheima");//3. 创建连接 ConnectionConnection connection = factory.newConnection();//4. 创建ChannelChannel channel = connection.createChannel();//5. 创建队列QueueString queue1Name = "test_fanout_queue1";String queue2Name = "test_fanout_queue2";// 接收消息Consumer consumer = new DefaultConsumer(channel){/*回调方法,当收到消息后,会自动执行该方法1. consumerTag:标识2. envelope:获取一些信息,交换机,路由key...3. properties:配置信息4. body:数据*/@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("body:"+new String(body));System.out.println("将日志信息打印到控制台.....");}};channel.basicConsume(queue1Name,true,consumer);}
}
class Consumer_PubSub2 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory = new ConnectionFactory();//2. 设置参数factory.setHost("159.75.102.92");factory.setPort(5672);factory.setVirtualHost("itcast");factory.setUsername("itheima");factory.setPassword("itheima");//3. 创建连接 ConnectionConnection connection = factory.newConnection();//4. 创建ChannelChannel channel = connection.createChannel();//5. 创建队列QueueString queue1Name = "test_fanout_queue1";String queue2Name = "test_fanout_queue2";// 接收消息Consumer consumer = new DefaultConsumer(channel){/*回调方法,当收到消息后,会自动执行该方法1. consumerTag:标识2. envelope:获取一些信息,交换机,路由key...3. properties:配置信息4. body:数据*/@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println("body:"+new String(body));System.out.println("将日志信息保存数据库.....");}};channel.basicConsume(queue2Name,true,consumer);}
}
Routing 路由模式
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)
- 消息的发送方在向 Exchange 发送消息时,也必须指定消息的 RoutingKey
- Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的Routingkey 与消息的 Routing key 完全一致,才会接收到消息
- P:生产者,向 Exchange 发送消息,发送消息时,会指定一个routing key
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与 routing key 完全匹配的队列
- C1:消费者,其所在队列指定了需要 routing key 为 error 的消息
- C2:消费者,其所在队列指定了需要 routing key 为 info、error、warning 的消息
//创建交换机channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, false, null);//创建队列String queue1Name = "test_direct_queue1";String queue2Name = "test_direct_queue2";channel.queueDeclare(queue1Name, true, false, false, null);channel.queueDeclare(queue2Name, true, false, false, null);//绑定队列和交换机/* queueBind(String queue, String exchange, String routingKey)参数:1. queue:队列名称2. exchange:交换机名称3. routingKey:路由键,绑定规则如果交换机的类型为fanout ,routingKey设置为"" *///队列1绑定errorchannel.queueBind(queue1Name,exchangeName,"error");//队列1绑定error、info、warningchannel.queueBind(queue2Name,exchangeName,"info");channel.queueBind(queue2Name,exchangeName,"error");channel.queueBind(queue2Name,exchangeName,"warning");String body = "日志信息:张三调用了findAll方法...日志级别:info...";//发送消息channel.basicPublish(exchangeName,"info",null,body.getBytes());
Routing 模式要求队列在绑定交换机时要指定 routing key,消息会转发到符合 routing key 的队列。
Topics 通配符模式
- Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符!
- Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
- 通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc
或者 item.insert,item.* 只能匹配 item.insert
- 红色 Queue:绑定的是 usa.# ,因此凡是以 usa. 开头的 routing key 都会被匹配到
- 黄色 Queue:绑定的是 #.news ,因此凡是以 .news 结尾的 routing key 都会被匹配
//把所有error级别的日志存储到数据库,所有order系统的日志存储到数据库channel.queueBind(queue1Name,exchangeName,"#.error");channel.queueBind(queue1Name,exchangeName,"order.*");channel.queueBind(queue2Name,exchangeName,"*.*");String body = "日志信息:张三调用了findAll方法...日志级别:info...";//发送消息channel.basicPublish(exchangeName,"order.info",null,body.getBytes());
Topic 主题模式可以实现 Pub/Sub 发布与订阅模式和 Routing 路由模式的功能,只是 Topic 在配置routing key 的时候可以使用通配符,显得更加灵活。
工作模式总结
- 简单模式 HelloWorld:一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)。
- 工作队列模式 Work Queue:一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)。
- 发布订阅模式 Publish/subscribe
- 需要设置类型为 fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消
息发送到绑定的队列。 - 路由模式 Routing:需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。
- 通配符模式 Topic:需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。
工作模式 | 交换机 | 消息队列 | 消费者数量 |
---|---|---|---|
simple | " " | 自定义队列名称 | 1 |
work | " " | 自定义队列名称 | N |
pub/sub | BuiltinExchangeType.FANOUT | 自定义队列名称 | N |
routing | BuiltinExchangeType.DIRECT | 自定义队列名称 | N |
topic | BuiltinExchangeType.TOPIC | 自定义队列名称 | N |
Spring整合RabbitMQ
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:rabbit="http://www.springframework.org/schema/rabbit"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/rabbithttp://www.springframework.org/schema/rabbit/spring-rabbit.xsd"><!--加载配置文件--><context:property-placeholder location="classpath:rabbitmq.properties"/><!-- 定义rabbitmq connectionFactory --><rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"port="${rabbitmq.port}"username="${rabbitmq.username}"password="${rabbitmq.password}"virtual-host="${rabbitmq.virtual-host}"/><!--定义管理交换机、队列--><rabbit:admin connection-factory="connectionFactory"/><!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机默认交换机类型为direct,名字为:"",路由键为队列的名称--><!--id:bean的名称name:queue的名称auto-declare:自动创建auto-delete:自动删除。 最后一个消费者和该队列断开连接后,自动删除队列exclusive:是否独占durable:是否持久化--><rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/><!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --><!--定义广播交换机中的持久化队列,不存在则自动创建--><rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/><!--定义广播交换机中的持久化队列,不存在则自动创建--><rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/><!--定义广播类型交换机;并绑定上述两个队列--><rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true"><rabbit:bindings><rabbit:binding queue="spring_fanout_queue_1" /><rabbit:binding queue="spring_fanout_queue_2"/></rabbit:bindings></rabbit:fanout-exchange><!--<rabbit:direct-exchange name="aa" ><rabbit:bindings><!–direct 类型的交换机绑定队列 key :路由key queue:队列名称–><rabbit:binding queue="spring_queue" key="xxx"></rabbit:binding></rabbit:bindings></rabbit:direct-exchange>--><!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --><!--定义广播交换机中的持久化队列,不存在则自动创建--><rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/><!--定义广播交换机中的持久化队列,不存在则自动创建--><rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/><!--定义广播交换机中的持久化队列,不存在则自动创建--><rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/><rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true"><rabbit:bindings><rabbit:binding pattern="heima.*" queue="spring_topic_queue_star"/><rabbit:binding pattern="heima.#" queue="spring_topic_queue_well"/><rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/></rabbit:bindings></rabbit:topic-exchange><!--定义rabbitTemplate对象操作可以在代码中方便发送消息--><rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
RabbitMQ基础相关推荐
- java B2B2C Springcloud电子商务平台源码-RabbitMQ基础概念...
RabbitMQ是一个由erlang开发的AMQP的开源实现. 需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码 一零三八七七四六二六 AMQP,即Adva ...
- RabbitMQ基础知识详解
RabbitMQ基础知识详解 2017年08月28日 20:42:57 dreamchasering 阅读数:41890 标签: RabbitMQ 什么是MQ? MQ全称为Message Queue, ...
- Rabbitmq 基础
Rabbitmq基础 1 MQ 基本概念 1.1 MQ概述 MQ全称Message Queue,是在消息的传输过程中保存消息的容器,多用于分布式系统之间进行通信. 分布式系统通信两种方式:直接远程调用 ...
- Ruby使用RabbitMQ(基础)
Ruby使用RabbitMQ(基础) RabbitMQ documentation rabbitmq-tutorials rabbitmq-configure bunny 前提 最近刚刚接触到mq, ...
- rabbitmq基础1——消息中间件概念、Rabbitmq的发展起源和基本组件的作用流程
文章目录 一.消息中间件 1.1 概念 1.2 作用 1.2.1 消息队列持久化 1.2.2 消息队列分发策略 1.2.3 消息队列的高可用和高可靠 1.2.3.1 一主多从共享集群 1.2.3.2 ...
- rabbitmq基础2——rabbitmq二进制安装和docker安装、基础命令
文章目录 一.RabbitMQ安装 1.1 二进制安装 1.2 rabbitmqctl工具 1.3 docker安装 二.rabbitmq基础命令 2.1 多租户与权限类 2.1.1 创建虚拟主机 2 ...
- (2)RabbitMQ基础概念及工作流程详解
上一节中我们对MQ做了一个概要介绍,这一节开始我们选取RabbitMQ开始进行学习,本节将会RabbitMQ做个简单介绍,并且会对其常见的基础概念做个讲解,最后会简单介绍一下RabbitMQ的工作流程 ...
- RabbitMQ基础知识介绍、RabbitMQ的安装
RabbitMQ基础知识介绍 官方解释:MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过 读写出入队列的消息 ...
- 转 RabbitMQ 基础概念及 Spring 的配置和使用 推荐好文 举例讲解
从不知道到了解-RabbitMQ 基础概念及 Spring 的配置和使用 转: sumile.cn » 从不知道到了解-RabbitMQ 基础概念及 Spring 的配置和使用 序言 你在系统中是 ...
- RabbitMQ基础概念详细介绍
转至:http://www.ostest.cn/archives/497 引言 你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼. ...
最新文章
- python目录大纲
- Redis宕机了怎么办?
- C#和.NET Framework的关系
- linux下生成源程序控制流图,Linux下控制(统计)文件的生成的C代码实现
- 稀疏数组(数据结构)
- 美赛论文格式基本要求
- java 爬虫 sessionid_java爬虫实战之模拟登陆
- 首次公开专利并且专利数量最多公司(2022.07.25-2022.7.31)
- STM32芯片命名规则及含义
- unbuntu20.04下载opencv4.5.2
- 人脸识别之FaceNet
- pyton笔记-part1
- 【音视频基础】(六):CIE颜色空间二之CIE-XYZ及Y的含义
- Predis\ClientException: Command ‘ZPOPMAX‘ is not a registered Redis command
- 周星馳 電影經典對白
- 壁纸wallpaper透视壁纸王者荣耀透视壁纸mpkg
- 【HFSS】有限大阵列FA-DDM仿真
- 【办公】火影金刚4K笔记本老毛桃重装Win10
- 如何使用SPSS按行显示和按列显示的摘要报告
- 【服务器问题】浪潮inspur服务器个别硬盘亮红灯且发出1长的滴滴声响