中间件知识点-消息中间件(Rabbitmq)一

消息中间件介绍

MQ的作用(优点)主要有以下三个方面:
a.异步
b.解耦
c.削峰

MQ的作用(缺点)主要有以下三个方面:
a.系统可用性降低
b.系统复杂度提高
c.存在消息一致性问题需要解决
备注:
引入MQ后系统的复杂度会大大提高。
以前服务之间可以进行同步的服务调用,引入MQ后,会变为异步调用,数据的链路就会变得更复杂。并且还会带来其他一些问
题。比如:如何保证消费不会丢失?不会被重复调用?怎么保证消息的顺序性等问题。(消息一致性问题)
A系统处理完业务,通过MQ发送消息给B、C系统进行后续的业务处理。如果B系统处理成功,C系统处理失败怎么办?这就需要考虑如何保证消息数据处理的一致性。

几大MQ产品特点比较(简单介绍)
rabbitmq可靠性:即不会丢数据,每个节点都存着全量数据(即数据一致性,rabbitmq仲裁队列quorum基于raft协议保证数据一致性,保证多半节点确认后才将消息写到队列中)。

kafka高可用:即使用了分区副本,把数据分散,保证高可用,不会因为一台机器宕机就无法使用。
kafka吞吐量大:使用了分区后可以在多台机器都存储数据,能存储的数据比较多
kafka性能非常好:使用了直接内存,零拷贝。

rocketmq借鉴了kafka、rabbitmq。

rabbitmq适合企业内部使用。一般企业使用rabbitmq就够了,一般企业5个节点就已经很多了。

在这里插入图片描述上面说的RabbitMq吞吐量比较低、消息积累会影响性能这些,其实在RabbitMq设计了Stream队列之后,这些都已经解决了。所以说RabbitMq的缺点只是对其过去版本的总结,现有的版本已经提升了很多了。因为加入了Stream队列这些,所以Stream队列借助了Kafka、RocketMq的思想。

另外上面说的RabbitMq吞吐量比较低、消息积累会影响性能这些是因为Classic和Quorum队列在收到消息时,如果是持久化消息,则会将消息储存在内存中,同时也会写入磁盘,所以消息量大的时候会内存会不断变小,吞吐量和性能就会变差。而RocketMQ或者kafka是会存在本地的,需要的时候再读取到内存,没那么耗性能。
1.kafka、rocketmq吞吐量高,是因为会将数据分散存储。那他会把收到的消息数据存到内存?
不会,他们是记录到日志中的。
但是在操作系统层面,当应用程序写入一个文件时,文件内容并不会直接写入到硬件当中,而是会先写入到操作系统中的一个缓存PageCache中。但可以认为会把收到的消息数据存到内存。
2.Stream队列不会将消息存在内存?不会。
3.Stream类似Kafka、RocketMq,以append-only log方式将消息记录到日志文件,然后消费的时候也是通过offset方式进行消费。
4.Quorum、Classic队列没有offset,不能快速找到数据,才需要将全部数据存放到内存。

这3个主要中间件一直在更新,一直在发展,越来越像。

activemq现在很少使用了,主要是用在很老的项目。

rocketmq适合业务比较多(topic比较多)的场景。

rabbitmq里面的routingkey和rocketmq里面的tag的区别?
routingkey在用在从交换机转换到哪个队列的,用在服务端。而tag用于消息过滤、消息路由、业务分类。

mq一般使用推模式。

rocketmq和kafka在互联网公司用的比较多,rabbitmq在中小公司用的比较多。
kafka一般用在大数据场景。

Rabbitmq

Rabbitmq单机搭建
略。

Rabbitmq集群(多节点的集群有两种方式模式)
a.默认的普通集群模式
不支持高可用:master宕机需要手动重启。
消息也不可靠:消息只存在一个节点中。
集群的各个节点之间只会有相同的元数据(比如队列、交换机。)。另外消息不会进行冗余,只存在一个节点中。消费时,如果消费的不是存有数据的节点, RabbitMQ会临时在节点之间进行数据传输,将消息从存有数据的节点传输到消费的节点。这种集群模式的消息可靠性不是很高。并且,这种集群模式也不支持高可用,即当某一个节点服务挂了后,需要手动重启服务,才能保证这一部分消息能正常消费。
所以这种集群模式只适合一些对消息安全性不是很高的场景。而在使用这种模式时,消费者应该尽量的连接上每一个节点,减少消息在集群中的传输。
b.镜像模式(会主动同步数据,而不是被动)
高可用:会自动选举新master节点。
消息可靠:消息会存在各节点。
但吞吐量不高:消息存在各节点,这样会消耗大量带宽。
这种模式是在普通集群模式基础上的一种增强方案,这也就是RabbitMQ的官方HA高可用方案。需要在搭建了普通集群之后再补充搭建。其本质区别在于,这种模式会在镜像节点中间主动进行消息同步,而不是在客户端拉取消息时临时同步。
并且在集群内部有一个算法会选举产生master和slave,当一个master挂了后,也会自动选出一个来。从而给整个集群提供高可用能力。
这种模式的消息可靠性更高,因为每个节点上都存着全量的消息。而他的弊端也是明显的,集群内部的网络带宽会被这种同步通讯大量的消耗(每个节点都存在全量数据,需要进行大量数据同步,不像kafka将数据分散存储),进而降低整个集群的性能。这种模式下,队列数量最好不要过多。

Exchanges:消息发送到RabbitMQ中后,会首先进入一个交换机,然后由交换机负责将数据转发到不同的队列中。即交换机的作用是可以对发送到RabbitMq Server的消息进行转发,转发到不同的队列中。
RabbitMQ中有多种不同类型的交换机来支持不同的路由策略。从Web管理界面就能看到,在每个虚拟主机中,RabbitMQ都会默认创建几个不同类型的交换机来。
交换机多用来与生产者打交道。生产者发送的消息通过Exchange交换机分配到各个不同的Queue队列上,而对于消息消费者来说,通常只需要关注自己感兴趣的队列就可以了。
在这里插入图片描述
队列消息删除机制:包括手动确认、自动确认、定时删除(TTL)等方式。
可以通过给队列设置TTL属性,来定时删除消息。
// 声明一个队列
String queueName = “testQueue”;
Map<String, Object> arguments = new HashMap<>();
// 设置消息的TTL为30000毫秒(30秒)
arguments.put(“x-message-ttl”, 30000);
channel.queueDeclare(queueName, true, false, false, arguments);

stream队列中的消息被消费了,是不会被删除的,而是通过类似kafka offset去定位从哪里去消费(只有Stream队列类型才会有offset概念,Classic经典队列没有),因为还需要发到其它消费组去推送消息。当过了一段时间后会统一删除队列的消息。

rabbitmq和kafka一样,同一个消费组的只有一个消费者才会消费消费消息。不同消费者组会重复消费一个消息。

rabbitmq没有类似kafka topic的概念,只有queue、exchanges的概念,只是在exchanges有topic的概念。而kafka、rocketmq有topic、queue的概念。
exchanges topic的概念?(Topic Exchange 类型支持通配符匹配路由键,然后将消息发送到符合特定规则的Queue 中)
RabbitMQ 和 Kafka 在消息队列架构上有一些不同之处。在 RabbitMQ 中,消息传递主要围绕着 Exchange 和 Queue 来进行,而 Exchange 中的类型包括 Direct、Fanout、Topic 和 Headers。
a.Exchange:Exchange 接收来自生产者的消息,并根据特定的规则将消息路由到一个或多个与之绑定的 Queue 中。Exchange 的类型决定了消息的路由方式。
b.Queue:Queue 是消息的容器,消费者从中接收消息。消息在进入 Queue 之前通过 Exchange 进行路由。
在 RabbitMQ 中,并没有像 Kafka 中的 Topic 概念那样直接映射到 Exchange。相对应的是,在 RabbitMQ 中的 Exchange 类型中,Topic Exchange 类型支持通配符匹配路由键,类似于 Kafka 中的 Topic,但并不是一一对应的概念。
因此,在 RabbitMQ 中,你可以使用 Topic Exchange 来实现类似于 Kafka Topic 的功能,通过设置特定的 Routing Key 和 Binding Key,将消息发送到符合特定规则的 Queue 中,实现灵活的消息路由和订阅功能。

Direct Exchange:将消息路由到绑定键(Binding Key)与消息的路由键(Routing Key)完全匹配的队列中。
Fanout Exchange:将消息广播到所有绑定的队列中。
Topic Exchange:根据消息的路由键和绑定键的模式进行匹配,将消息路由到一个或多个符合匹配规则的队列中。
Headers Exchange:使用消息的头部信息(headers)而不是路由键来决定消息的路由方式。

rabbitmq Exchanges四种类型:Direct、Fanout、Topic 和 Headers的java代码使用案例:
1.Direct Exchange

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;public class DirectExchangeExample {private static final String EXCHANGE_NAME = "direct_exchange";private static final String QUEUE_NAME = "direct_queue";private static final String ROUTING_KEY = "direct_routing_key";public static void main(String[] args) throws Exception {// 创建连接和通道ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明一个 Direct Exchangechannel.exchangeDeclare(EXCHANGE_NAME, "direct");// 声明一个队列channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 将队列绑定到 Exchange,并指定 Routing Keychannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);// 发送消息到 Exchange,并指定 Routing KeyString message = "Hello, Direct Exchange!";channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, message.getBytes("UTF-8"));System.out.println("Sent message: '" + message + "'");}}
}

解释:
在这个例子中,我们创建了一个 Direct Exchange 名称为 direct_exchange。
创建了一个队列 direct_queue,并将它绑定到 direct_exchange,绑定时使用了路由键 direct_routing_key。
发送了一条消息到 Exchange,并指定了与队列绑定时相同的路由键 direct_routing_key。

2 Fanout Exchange

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;public class FanoutExchangeExample {private static final String EXCHANGE_NAME = "fanout_exchange";private static final String QUEUE_NAME1 = "fanout_queue1";private static final String QUEUE_NAME2 = "fanout_queue2";public static void main(String[] args) throws Exception {// 创建连接和通道ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明一个 Fanout Exchangechannel.exchangeDeclare(EXCHANGE_NAME, "fanout");// 声明两个队列channel.queueDeclare(QUEUE_NAME1, false, false, false, null);channel.queueDeclare(QUEUE_NAME2, false, false, false, null);// 将队列1绑定到 Exchangechannel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "");// 将队列2绑定到 Exchangechannel.queueBind(QUEUE_NAME2, EXCHANGE_NAME, "");// 发送消息到 ExchangeString message = "Hello, Fanout Exchange!";channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));System.out.println("Sent message: '" + message + "'");}}
}

解释:
在这个例子中,我们创建了一个 Fanout Exchange 名称为 fanout_exchange。
创建了两个队列 fanout_queue1 和 fanout_queue2,并将它们都绑定到 fanout_exchange。
发送了一条消息到 Exchange,消息会被广播到所有与 Exchange 绑定的队列上。

3.Topic Exchange

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;public class TopicExchangeExample {private static final String EXCHANGE_NAME = "topic_exchange";private static final String QUEUE_NAME1 = "topic_queue1";private static final String QUEUE_NAME2 = "topic_queue2";private static final String ROUTING_KEY1 = "topic.key1";private static final String ROUTING_KEY2 = "topic.key2";public static void main(String[] args) throws Exception {// 创建连接和通道ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明一个 Topic Exchangechannel.exchangeDeclare(EXCHANGE_NAME, "topic");// 声明两个队列channel.queueDeclare(QUEUE_NAME1, false, false, false, null);channel.queueDeclare(QUEUE_NAME2, false, false, false, null);// 将队列1绑定到 Exchange,使用通配符路由键 "*.key1"channel.queueBind(QUEUE_NAME1, EXCHANGE_NAME, "*.key1");// 将队列2绑定到 Exchange,使用通配符路由键 "topic.*"channel.queueBind(QUEUE_NAME2, EXCHANGE_NAME, "topic.*");// 发送消息到 Exchange,使用路由键 "topic.key1"String message1 = "Hello, Topic Exchange - Key1!";channel.basicPublish(EXCHANGE_NAME, "topic.key1", null, message1.getBytes("UTF-8"));System.out.println("Sent message 1: '" + message1 + "'");// 发送消息到 Exchange,使用路由键 "topic.key2"String message2 = "Hello, Topic Exchange - Key2!";channel.basicPublish(EXCHANGE_NAME, "topic.key2", null, message2.getBytes("UTF-8"));System.out.println("Sent message 2: '" + message2 + "'");}}
}

解释:
在这个例子中,我们创建了一个 Topic Exchange 名称为 topic_exchange。
创建了两个队列 topic_queue1 和 topic_queue2,并分别使用不同的通配符路由键将它们绑定到 topic_exchange。
发送了两条消息到 Exchange,分别使用了符合队列绑定模式的路由键。

4.Headers Exchange

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
import java.util.HashMap;
import java.util.Map;public class HeadersExchangeExample {private static final String EXCHANGE_NAME = "headers_exchange";private static final String QUEUE_NAME = "headers_queue";private static final Map<String, Object> HEADERS = new HashMap<>();public static void main(String[] args) throws Exception {// 创建连接和通道ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {// 声明一个 Headers Exchangechannel.exchangeDeclare(EXCHANGE_NAME, "headers");// 声明一个队列channel.queueDeclare(QUEUE_NAME, false, false, false, null);// 定义 headers 属性匹配规则HEADERS.put("header1", "value1");// 将队列绑定到 Exchange,指定 headers 匹配规则channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "", HEADERS);// 发送消息到 Exchange,并设置匹配的 headers 属性Map<String, Object> messageHeaders = new HashMap<>();messageHeaders.put("header1", "value1");String message = "Hello, Headers Exchange!";channel.basicPublish(EXCHANGE_NAME, "", new BasicProperties.Builder().headers(messageHeaders).build(), message.getBytes("UTF-8"));System.out.println("Sent message: '" + message + "'");}}
}

解释:
在这个例子中,我们创建了一个 Headers Exchange 名称为 headers_exchange。
创建了一个队列 headers_queue,并使用 headers 属性 header1=value1 将它绑定到 headers_exchange。
发送了一条消息到 Exchange,并设置了符合队列绑定 headers 属性的消息头。

rabbitmq实现顺序消费需要在单队列才能实现。

在一个worker(节点)中创建了VirtualHost、Queues后,集群中的其它节点也会有这个VirtualHosts、Queues。即集群中所有的节
点数据都是一样的,是会进行同步的。
消费者消费消息有推模式和拉模式

队列
Classic 经典队列(最为常用)
单机环境中(不是集群),拥有比较高的消息可靠性。因为可以持久化数据。
多节点下采用主从复制,可能会丢失数据。

Quorum 仲裁队列(针对Classic优化方式一)
(单机下通过持久化保证消息可靠性。集群下通过Raft协议保证分布式消息可靠性)
a.仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。官方文档中表示,未来会使用Quorum仲裁队列代替传统Classic队列。
b.基于Raft一致性协议实现的一种新型的分布式消息队列,他实现了持久化,多备份的FIFO队列,主要就是针对RabbitMQ的镜像模式设计的。
c.仲裁队列的消息都是持久化的,同时队列的所有消息一直会保存在内存中。经典队列也一样,都会保存在内存中。
d.适合场景
Quorum队列更适合:需要队列长期存在,并且对容错、数据安全方面的要求比较高。对低延迟要求不高,且不需要临时消息的场景。例如 电商系统的订单,引入MQ后,处理速度可以慢一点,但是订单不能丢失。
e.不适合场景
响应的Quorum队列不适合使用的场景:
1、一些临时使用的队列:比如transient临时队列,exclusive独占队列,或者经常会修改和删除的队列。(即Quorum不支持临时队列和独占队列)
2、对消息低延迟要求高: 一致性算法会影响消息的延迟。
3、对数据安全性要求不高:Quorum队列需要消费者手动通知或者生产者手动确认。
4、队列消息积压严重 : 如果队列中的消息很大,或者积压的消息很多,就不要使用Quorum队列(因为Classic和Quorum队列在收到消息时,如果是持久化消息,则会将消息储存在内存中,同时也会写入磁盘,所以消息量大的时候会内存会不断变小,吞吐量和性能就会变差)。Quorum队列当前会将所有消息始终保存在内存中,直到达到内存使用极限。
所以,对于消息特别多且消息积压严重,我们一般不使用activemq/rabbitmq的经典队列和仲裁队列。

rabbimq中Quorum队列和Classic经典队列的区别总结:
1.可靠性方面:单节点时,经典队列和仲裁队列可靠性都很高。多节点集群下,经典队列仅采用主从复制,即一个主节点和多个从节点,主节点负责处理所有的读写操作,从节点则复制主节点的数据,数据可能会不一致。而仲裁队列采用了基于Raft一致性协议实现数据一致性,需要又集群中多半节点同意确认后,才会写入到队列中,可靠性更高。
单机下通过持久化保证消息可靠性。集群下通过Raft协议保证分布式消息可靠性
2.持久化:经典队列可以设置是否持久化,而仲裁队列都是持久化。同时队列的所有消息一直会保存在内存中。经典队列也一样,都会保存在内存中。所以对于消息特别多且消息积压严重,我们一般不使用activemq/rabbitmq的经典队列和仲裁队列。
3.仲裁队列大部分功能都是在经典队列基础上做减法,比如仲裁队列相比经典队列少了是否持久化、是否独占这些属性配置。
4.仲裁队列有毒消息机制,而经典队列没有。

Stream队列(针对Classic优化方式二,推荐)
Stream队列不会把所有的消息都放在内存中,需要的时候才从磁盘上去取(通过offset快速找到,而不像Quorum、Classic队列没有offset,不能快速找到数据,才需要将全部数据存放到内存)

RabbitMQ编程模型
(可以使用下面三种方式来使用RabbitMq)
从原生API、SpringBoot集成(使用最多)、SpringCloudStream集成。

RabbitMQ高级使用场景
1.Header路由
2.分组消费模式
3.死信队列
4.消费优先级与流量控制
5.远程数据分发插件
6.懒队列(懒加载) Lazy Queue
RabbitMQ从3.6.0版本开始,就引入了懒队列的概念。懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。
懒队列的设计目标是为了支持非常长的队列(数百万级别)。队列可能会因为一些原因变得非常长-也就是数据堆积。
懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的(即尽早将消息持久化,尽早多节点同步)。
7.消息分片存储插件
如何提高吞吐量?
上面的懒队列其实就是针对这个问题的一种解决方案。但是很显然,懒队列的方式属于治标不治本。真正要提升RabbitMQ单队列的吞吐量,还是要从数据也就是消息入手,只有将数据真正的分开存储才行。RabbitMQ提供的Sharding插件,就是一个可选的方案。他会真正将一个队列中的消息分散存储到不同的节点上,并提供多个节点的负载均衡策略实现对等的读与写功能。

RabbitMQ使用中的常见问题
一、如何使用RabbitMQ保证消息不丢失?
二、如何保证消息幂等
三、如何保证消息的顺序?
四、关于RabbitMQ的数据堆积问题
五、RabbitMQ的备份与恢复
六、RabbitMQ的性能监控
七、搭建HAProxy,实现高可用集群(在集群基础上增加负载均衡的能力,将客户端的请求能够尽量均匀的分配到集群中各个节点上)

总结:
基于MQ的事件驱动机制,给庞大的互联网应用带来了不一样的方向。MQ的异步、解耦、削峰三大功能特点在很多业务场景下都能带来极大的性能提升,在日常工作过程中,应该尝试总结这些设计的思想。虽然MQ的功能,说起来比较简单,但是随着MQ的应用逐渐深化,所需要解决的问题也更深入。对各种细化问题的挖掘程度,很大程度上决定了开发团队能不能真正Hold得住MQ产品。通常面向互联网的应用场景,更加注重MQ的吞吐量,需要将消息尽快的保存下来,再供后端慢慢消费。而针对企业内部的应用场景,更加注重MQ的数据安全性,在复杂多变的业务场景下,每一个消息都需要有更加严格的安全保障。而在当今互联网,Kafka(吞吐量大)是第一个场景(即面向互联网的应用场景)的不二代表,但是他会丢失消息的特性,让kafka的使用场景比较局限。RabbitMQ作为一个老牌产品,是第二个场景(即对企业内部的应用场景)最有力的代表。当然,随着互联网应用不段成熟,也不断有其他更全能的产品冒出来,比如阿里的RocketMQ(所以rocketmq是rabbitmq和kafka之后推出的)以及雅虎的Pulsar。但是不管未来MQ领域会是什么样子,RabbitMQ依然是目前企业级最为经典也最为重要的一个产品。他的功能最为全面,周边生态也非常成熟,并且RabbitMQ有庞大的Spring社区支持,本身也在吸收其他产品的各种优点,持续进化,所以未来RabbitMQ的重要性也会更加凸显。

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

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

相关文章

【软件基础知识】什么是 API,详细解读

想象一下,你正在使用智能手机上的天气应用。你打开应用,瞬间就能看到实时天气、未来预报,甚至是空气质量指数。但你有没有想过,这些数据是如何神奇地出现在你的屏幕上的?答案就在三个字母中:API。 API,全称Application Programming Interface(应用程序编程接口),是现代软件世…

计算机网络 --- 初识协议

序言 上一篇文章中 &#xff08;&#x1f449;点击查看&#xff09;&#xff0c;我们简单的了解了怎么寻找目标计算机&#xff0c;需要通过交换机&#xff0c;路由器等设备跨越多个网络来不断的转发我们需要传输的数据&#xff0c;直至到达目标计算机。  那我们设备之间数据是…

重回极简:华为如何走向全面智能化?

“人类发现地球只是宇宙一员的时候&#xff0c;也是我们距离群星最遥远的时候。” 这个来自天文领域的喟叹&#xff0c;今天同样出现在行业与企业的智能化之路上。在这个时代坐标上&#xff0c;AI大模型技术极速成熟&#xff0c;AIGC和AI Agent等应用受到了各个行业的巨大期待。…

昇腾大模型推理解决方案MindIE部署

MindIE大模型推理套件 MindIE&#xff08;Mind Inference Engine&#xff0c;昇腾推理引擎&#xff09;是华为公司针对AI全场景推出的整体解决方案&#xff0c;包含丰富的推理加速套件。通过开放各层次AI能力&#xff0c;支撑客户多样化的AI业务需求&#xff0c;使能百模千态&a…

存储 NFS

目录 1.存储的应用场景 2.存储分类 3.NFS服务组成 4.环境说明 ​编辑 5.服务端部署 6.NFS服务端的配置 7.NFS服务端本地进行测试 1.存储的应用场景 存储一般用于上传网站数据&#xff08;内容&#xff09;&#xff0c;一般用于在网站集群中。使用存储的话用户上传的…

成型的程序

加一个提示信息 加上python 常用的包 整个程序打包完 250M 安装 960MB matplot numpy pandas scapy pysearial 常用的包 (pyvisa)… … 啥都有 Python 解释器组件构建 要比 lua 容易的多 &#xff08;C/Rust 的组件库)

JavaSE--集合总览02:单列集合Collection的体系之一:List

Collection体系的特点 分为 list 和set集合&#xff0c;这篇文章主要讲述List&#xff0c;下篇讲述Set。 简单认识单列集合collection集合的特点 : list集合的特点&#xff1a; 有序 可重复 有索引 set集合的特点&#xff1a;无序 不重复 无索引 其中LinkedHashSet有序 TreeS…

微服务架构陷阱与挑战

微服务架构6大陷阱 现在微服务的基础设施还是越来越完善了&#xff0c;现在基础设施缺乏的问题逐渐被解决了。 拆分粒度太细&#xff0c;服务关系复杂 拆分降低了服务的内部复杂度&#xff0c;但是提升了系统的外部复杂度&#xff0c;服务越多&#xff0c;服务和服务之间的连接…

from tqdm.auto import tqdm用法详细介绍

tqdm 是一个 Python 库&#xff0c;用于在长时间运行的任务中显示进度条。tqdm.auto 是 tqdm 的一个版本&#xff0c;能够自动适配输出环境&#xff08;如 Jupyter Notebook、命令行等&#xff09;&#xff0c;以确保进度条在各种环境下显示正确。下面是 tqdm.auto 的详细用法介…

每天五分钟计算机视觉:将人脸识别问题转换为二分类问题

本文重点 在前面的课程中,我们学习了两种人脸识别的网络模型,这两种人脸识别网络不能算是基于距离或者Triplet loss等等完成的神经网络参数的学习。我们比较熟悉的是分类任务,那么人脸识别是否可以转变为分类任务呢? 本节课程我们将介绍一种全新的方法来学习神经网络的参…

用友U8二次开发工具KK-FULL-*****-EFWeb使用方法

1、安装: 下一步&#xff0c;下一步即可。弹出黑框不要关闭&#xff0c;让其自动执行并关闭。 2、服务配置&#xff1a; 输入服务器IP地址&#xff0c;选择U8数据源&#xff0c;输入U8用户名及账号&#xff0c;U8登录日期勾选系统日期。测试参数有效性&#xff0c;提示测试通过…

利用 FastAPI 和 Jinja2 模板引擎快速构建 Web 应用

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;它基于标准 Python 类型提示。FastAPI 支持异步编程&#xff0c;使得开发高性能的 Web 应用变得简单快捷。在本文中&#xff0c;我们将探讨如何使用 FastAPI 结合 Jinj…

VS code 查看 ${workspaceFolder} 目录指代路径

VS code 查看 ${workspaceFolder} 目录指代路径 引言正文 引言 在 VS code 创建与运行 task.json 文件 一文中我们已经介绍了如何创建属于自己的 .json 文件。在 VS code 中&#xff0c;有时候我们需要添加一些文件路径供我们导入自定义包使用&#xff0c;此时&#xff0c;我们…

OpenCV系列教程二:基本图像增强(数值运算)、滤波器(去噪、边缘检测)

文章目录 一、基本图像增强&#xff08;数值运算&#xff09;1.1 加法 &#xff08;cv2.add&#xff09;1.1.1 图像与标量相加&#xff08;调节亮度&#xff09;1.1.2 图像与图像相加&#xff08;两个图像shape要相同&#xff09;1.1.3 图像的加权加法&#xff08;渐变切换&…

基于SpringBoot+Vue+MySQL的网上租赁系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当前共享经济蓬勃发展的背景下&#xff0c;网上租赁系统作为连接租赁双方的重要平台&#xff0c;正逐步改变着人们的消费观念和生活方式。通过构建一个基于SpringBoot、Vue.js与MySQL的网上租赁系统&#xff0c;我们旨在为用户…

python线程(python threading模块、python多线程)(守护线程与非守护线程)

文章目录 Python多线程入门1. Python多线程概述2. threading模块基础- Thread 类: 这是一个代表线程的类。可以通过创建Thread类的实例来新建一个线程。- Lock 类: 在多线程环境中&#xff0c;为了防止数据错乱&#xff0c;通常需要用到锁机制。Lock类提供了基本的锁功能&#…

本地搭建我的世界服务器(JAVA)简单记录

网上参考教程挺多的&#xff0c;踩了不少坑&#xff0c;简单记录一下&#xff0c;我做的是一个私人服务器&#xff0c;就是和朋友3、4个人玩。 笨蛋 MC 开服教程 先放一个比较系统和完整的教程&#xff0c;萌新可用&#xff0c;这个教程很详细&#xff0c;我只是记录一下自己的…

【C++】list容器的基本使用

一、list是什么 list的底层结构是带头双向循环链表。 相较于 vector 的连续线性空间&#xff0c;list 就显得复杂很多&#xff0c;它是由一个个结点构成&#xff0c;每个结点申请的空间并不是连续的&#xff0c;它的好处是每次插入或删除一个数据&#xff0c;就配置或释放一个…

禁忌搜索算法(TS算法)求解实例---旅行商问题 (TSP)

目录 一、采用TS求解 TSP二、 旅行商问题2.1 实际例子&#xff1a;求解 6 个城市的 TSP2.2 **求解该问题的代码**2.3 代码运行过程截屏2.4 代码运行结果截屏&#xff08;后续和其他算法进行对比&#xff09; 三、 如何修改代码&#xff1f;3.1 减少城市坐标&#xff0c;如下&am…

游戏如何对抗定制挂

近年来&#xff0c;游戏安全对抗强度相比以往更加激烈&#xff0c;具体表现在“定制挂”趋势显著。在近期收集的近万款外挂样本中&#xff0c;定制挂约占比78%&#xff0c;常见的内存修改器、变速器等通用作弊手段占比正在下降。 所谓定制挂&#xff0c;是指针对某款游戏单独开…