RabbitMQ篇

news/2024/4/24 13:06:30/文章来源:https://blog.csdn.net/viperd/article/details/136543886

1.初始MQ

1.1. 同步和异步通讯

微服务间通讯有同步和异步两种方式:

同步通讯:就像打电话,需要实时响应。

异步通讯:就像发邮件,不需要马上回复。

两种方式各有优劣,打电话可以立即得到响应,但是你却不能跟多个人同时通话。发送邮件可以同时与多个人收发邮件,但是往往响应会有延迟。

1.1.1.同步通讯

我们之前学习的Feign调用就属于同步方式,虽然调用可以实时得到结果,但存在下面的问题:

总结:

同步调用的优点:

  • 时效性较强,可以立即得到结果

同步调用的问题:

  • 耦合度高

  • 性能和吞吐能力下降

  • 有额外的资源消耗

  • 有级联失败问题

1.1.2.异步通讯

异步调用则可以避免上述问题:

我们以购买商品为例,用户支付后需要调用订单服务完成订单状态修改,调用物流服务,从仓库分配响应的库存并准备发货。

在事件模式中,支付服务是事件发布者(publisher),在支付完成后只需要发布一个支付成功的事件(event),事件中带上订单id。

订单服务和物流服务是事件订阅者(Consumer),订阅支付成功的事件,监听到事件后完成自己业务即可。

为了解除事件发布者与订阅者之间的耦合,两者并不是直接通信,而是有一个中间人(Broker)。发布者发布事件到Broker,不关心谁来订阅事件。订阅者从Broker订阅事件,不关心谁发来的消息。

Broker 是一个像数据总线一样的东西,所有的服务要接收数据和发送数据都发到这个总线上,这个总线就像协议一样,让服务间的通讯变得标准和可控。

好处:

  • 吞吐量提升:无需等待订阅者处理完成,响应更快速

  • 故障隔离:服务没有直接调用,不存在级联失败问题

  • 调用间没有阻塞,不会造成无效的资源占用

  • 耦合度极低,每个服务都可以灵活插拔,可替换

  • 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件

缺点:

  • 架构复杂了,业务没有明显的流程线,不好管理

  • 需要依赖于Broker的可靠、安全、性能

好在现在开源软件或云平台上 Broker 的软件是非常成熟的,比较常见的一种就是我们今天要学习的MQ技术。

1.2.技术对比:

MQ,中文是消息队列(MessageQueue),字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。

比较常见的MQ实现:

  • ActiveMQ

  • RabbitMQ

  • RocketMQ

  • Kafka

几种常见MQ的对比:

RabbitMQActiveMQRocketMQKafka
公司/社区RabbitApache阿里Apache
开发语言ErlangJavaJavaScala&Java
协议支持AMQP,XMPP,SMTP,STOMPOpenWire,STOMP,REST,XMPP,AMQP自定义协议自定义协议
可用性一般
单机吞吐量一般非常高
消息延迟微秒级毫秒级毫秒级毫秒以内
消息可靠性一般一般

追求可用性:Kafka、 RocketMQ 、RabbitMQ

追求可靠性:RabbitMQ、RocketMQ

追求吞吐能力:RocketMQ、Kafka

追求消息低延迟:RabbitMQ、Kafka

2.快速入门

2.1.安装RabbitMQ

安装RabbitMQ:

我们在Centos7虚拟机中使用Docker来安装。

方式一:在线拉取:

docker pull rabbitmq:3-management

方式二:从本地加载

已经提供了的镜像包:

上传到虚拟机中后,使用命令加载镜像即可:

docker load -i mq.tar

执行下面的命令来运行MQ容器:

docker run \-e RABBITMQ_DEFAULT_USER=itcast \-e RABBITMQ_DEFAULT_PASS=123321 \--name mq \--hostname mq1 \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq:3-management

MQ的基本结构:

RabbitMQ中的一些角色:

  • publisher:生产者

  • consumer:消费者

  • exchange个:交换机,负责消息路由

  • queue:队列,存储消息

  • virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔离

2.2.RabbitMQ消息模型

RabbitMQ官方提供了5个不同的Demo示例,对应了不同的消息模型:

2.3.导入Demo工程

资料提供了一个Demo工程,mq-demo:

导入后可以看到结构如下:

包括三部分:

  • mq-demo:父工程,管理项目依赖

  • publisher:消息的发送者

  • consumer:消息的消费者

2.4.入门案例

简单队列模式的模型图:

官方的HelloWorld是基于最基础的消息队列模型来实现的,只包括三个角色:

  • publisher:消息发布者,将消息发送到队列queue

  • queue:消息队列,负责接受并缓存消息

  • consumer:订阅队列,处理队列中的消息

2.5.总结

基本消息队列的消息发送流程:

  1. 建立connection

  2. 创建channel

  3. 利用channel声明队列

  4. 利用channel向队列发送消息

基本消息队列的消息接收流程:

  1. 建立connection

  2. 创建channel

  3. 利用channel声明队列

  4. 定义consumer的消费行为handleDelivery()

  5. 利用channel将消费者与队列绑定

3.SpringAMQP

SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。

SpringAMQP提供了三个功能:

  • 自动声明队列、交换机及其绑定关系

  • 基于注解的监听器模式,异步接收消息

  • 封装了RabbitTemplate工具,用于发送消息

3.1.Basic Queue 简单队列模型

在父工程mq-demo中引入依赖

<!--AMQP依赖,包含RabbitMQ-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

3.1.1.消息发送

首先配置MQ地址,在publisher服务的application.yml中添加配置:

 
spring:rabbitmq:host: 192.168.150.101 # 主机名port: 5672 # 端口virtual-host: / # 虚拟主机username: itcast # 用户名password: 123321 # 密码

然后在publisher服务中编写测试类SpringAmqpTest,并利用RabbitTemplate实现消息发送:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void testSimpleQueue() {// 队列名称String queueName = "simple.queue";// 消息String message = "hello, spring amqp!";// 发送消息rabbitTemplate.convertAndSend(queueName, message);}
}

3.1.2.消息接收

首先配置MQ地址,在consumer服务的application.yml中添加配置:

spring:rabbitmq:host: 192.168.150.101 # 主机名port: 5672 # 端口virtual-host: / # 虚拟主机username: itcast # 用户名password: 123321 # 密码

然后在consumer服务的cn.itcast.mq.listener包中新建一个类SpringRabbitListener,代码如下:

@Component
public class SpringRabbitListener {@RabbitListener(queues = "simple.queue")public void listenSimpleQueueMessage(String msg) throws InterruptedException {System.out.println("spring 消费者接收到消息:【" + msg + "】");}
}

3.1.3.测试

启动consumer服务,然后在publisher服务中运行测试代码,发送MQ消息:

3.2. WorkQueue

Work queues,也被称为(Task queues),任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息

当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度(供 >> 求)。长此以往,消息就会堆积越来越多,无法及时处理。

此时就可以使用work 模型,多个消费者共同处理消息处理,速度就能大大提高了。

3.2.1. 消息发送

我们循环发送,模拟大量消息堆积现象。

在publisher服务中的SpringAmqpTest类中添加一个测试方法:

/*** workQueue* 向队列中不停发送消息,模拟消息堆积。*/
@Test
public void testWorkQueue() throws InterruptedException {// 队列名称String queueName = "simple.queue";// 消息String message = "hello, message_";for (int i = 0; i < 50; i++) {// 发送消息rabbitTemplate.convertAndSend(queueName, message + i);Thread.sleep(20);}
}

3.2.2. 消息接收

要模拟多个消费者绑定同一个队列,我们在consumer服务的SpringRabbitListener中添加2个新的方法:

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());Thread.sleep(20);
}@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());Thread.sleep(200);
}

注意到这两个消费者sleep了0.2和0.02秒,模拟任务耗时。

3.2.3.测试

启动ConsumerApplication后,在执行publisher服务中刚刚编写的发送测试方法testWorkQueue。

可以看到消费者1很快完成了自己的25条消息。消费者2却在缓慢的处理自己的25条消息。

也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。这样显然是有问题的。

3.2.4.能者多劳

在spring中有一个简单的配置,可以解决这个问题,将消费者获取消息的模式改为:每次只取一条,处理完之后才能继续取。我们修改consumer服务的application.yml文件,添加配置:

spring:rabbitmq:listener:simple:prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

3.2.5.总结

Work模型的使用:

  • 多个消费者绑定到一个队列,同一条消息只会被一个消费者处理

  • 通过设置prefetch来控制消费者预取的消息数量

3.3.发布/订阅

发布订阅模型如图:

可以看到,在订阅模型中,多了一个exchange角色,而且过程略有变化:

  • Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)

  • Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有以下3种类型:

    • Fanout:广播,将消息交给所有绑定到交换机的队列

    • Direct:定向,把消息交给符合指定routing key 的队列

    • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列

  • Consumer:消费者,与以前一样,订阅队列,没有变化

  • Queue:消息队列也与以前一样,接收消息、缓存消息。

Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!

3.4.Fanout

Fanout,英文翻译是扇出,我觉得在MQ中叫广播更合适。

在广播模式下,消息发送流程是这样的:

  • 1) 可以有多个队列

  • 2) 每个队列都要绑定到Exchange(交换机)

  • 3) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定

  • 4) 交换机把消息发送给绑定过的所有队列

  • 5) 订阅队列的消费者都能拿到消息

我们的计划是这样的:

  • 创建一个交换机 itcast.fanout,类型是Fanout

  • 创建两个队列fanout.queue1和fanout.queue2,绑定到交换机itcast.fanout

3.4.1.声明队列和交换机

Spring提供了一个接口Exchange,来表示所有不同类型的交换机:

在consumer中创建一个类,声明队列和交换机:

@Configuration
public class FanoutConfig {/*** 声明交换机* @return Fanout类型交换机*/@Beanpublic FanoutExchange fanoutExchange(){return new FanoutExchange("itcast.fanout");}/*** 第1个队列*/@Beanpublic Queue fanoutQueue1(){return new Queue("fanout.queue1");}/*** 绑定队列和交换机*/@Beanpublic Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);}/*** 第2个队列*/@Beanpublic Queue fanoutQueue2(){return new Queue("fanout.queue2");}/*** 绑定队列和交换机*/@Beanpublic Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);}
}

3.4.2.消息发送

在publisher服务的SpringAmqpTest类中添加测试方法:

@Test
public void testFanoutExchange() {// 队列名称String exchangeName = "itcast.fanout";// 消息String message = "hello, everyone!";rabbitTemplate.convertAndSend(exchangeName, "", message);
}

3.4.3.消息接收

在publisher服务的SpringAmqpTest类中添加测试方法:

@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {System.out.println("消费者1接收到Fanout消息:【" + msg + "】");
}@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {System.out.println("消费者2接收到Fanout消息:【" + msg + "】");
}

测试结果:

3.4.4.总结

交换机的作用是什么?

  • 接收publisher发送的消息

  • 将消息按照规则路由到与之绑定的队列

  • 不能缓存消息,路由失败,消息丢失

  • FanoutExchange的会将消息路由到每个绑定的队列

声明队列、交换机、绑定关系的Bean是什么?

  • Queue

  • FanoutExchange

  • Binding

3.5.Direct

在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。

在Direct模型下:

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)

  • 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey

  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息

案例需求如下

  1. 利用@RabbitListener声明Exchange、Queue、RoutingKey

  2. 在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2

  3. 在publisher中编写测试方法,向itcast. direct发送消息

3.5.1.基于注解声明队列和交换机

在consumer的SpringRabbitListenerTest中,基于注解声明交换机和队列,并且加上BindingKey:

//基于注解声明队列和交换机@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue1"),exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),key = {"red","blue"}))public void listenDirectQueue1(String msg){System.out.println("消费者1接收到Direct消息:【" + msg + "】");}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue2"),exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),key = {"red","black"}))public void listenDirectQueue2(String msg){System.out.println("消费者2接收到Direct消息:【" + msg + "】");}

重启服务。

3.5.2. 消息发送

在publisher中的SpringAmqpTest中创建测试方法:

@Testpublic void testDirectExchange() {// 队列名称String exchangeName = "itcast.direct";// 消息String message = "hello, red!";rabbitTemplate.convertAndSend(exchangeName, "red", message);}

启动测试方法后,查看结果:

将publisher的测试方法里面的RoutingKey改为:black 再次测试:

 @Testpublic void testDirectExchange() {// 队列名称String exchangeName = "itcast.direct";// 消息String message = "hello, black!";rabbitTemplate.convertAndSend(exchangeName, "black", message);}

 

很明显看出,发送者的  RoutingKey  和 交换机与队列 之间的  BindingKey  有相同值时,交换机才会给队列发送消息。

3.5.3.总结

描述下Direct交换机与Fanout交换机的差异?

  • Fanout交换机将消息路由给每一个与之绑定的队列

  • Direct交换机根据RoutingKey判断路由给哪个队列

  • 如果多个队列具有相同的RoutingKey,则与Fanout功能类似

基于@RabbitListener注解声明队列和交换机有哪些常见注解?

  • @Queue

  • @Exchange

3.6.Topic

3.6.1.说明

Topic类型的ExchangeDirect类型的相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!

Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

通配符规则:

#:匹配一个或多个词

*:匹配不多不少恰好1个词

举例:

item.#:能够匹配item.spu.insert 或者 item.spu

item.*:只能匹配item.spu

图示:

解释:

  • Queue1:绑定的是china.# ,因此凡是以 china.开头的routing key 都会被匹配到。包括china.news和china.weather

  • Queue2:绑定的是#.news ,因此凡是以 .news结尾的 routing key 都会被匹配。包括china.news和japan.news

案例需求:

实现思路如下:

  1. 并利用@RabbitListener声明Exchange、Queue、RoutingKey

  2. 在consumer服务中,编写两个消费者方法,分别监听topic.queue1和topic.queue2

  3. 在publisher中编写测试方法,向itcast. topic发送消息

3.6.2. 消息发送

在publisher的SpringAmqpTest中编写一个测试方法,其中RoutingKey为 china.news :

@Testpublic void testTopicExchange(){//队列名称String exchangeName = "itcast.topic";//消息String message = "喜报!孙悟空大战哥斯拉,胜!";rabbitTemplate.convertAndSend(exchangeName,"china.news",message);}

3.6.3. 消息接收

在consumer的SpringRabbitListener中添加方法,其中queue1绑定的RoutingKey为 china.#

而queue2绑定的RoutingKey为 #.news 也就是说,不出意外的话,queue1能匹配到所有以china.开头的RoutingKey,而queue2能匹配到所有以 .news 结尾的RoutingKey:

/*** TopicExchange*/@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "itcast.queue1"),exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),key = "china.#"))public void listenTopicQueue1(String msg){System.out.println("消费者1接收到Topic消息:【" + msg + "】");}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "itcast.queue2"),exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),key = "#.news"))public void listenTopicQueue2(String msg){System.out.println("消费者2接收到Topic消息:【" + msg + "】");}

重启服务,测试:

说明两个队列都收到了发送方的消息

再测试一个RoutingKey为 japan.news的消息:

将publisher中的测试方法的RoutingKey改成 japan.news:

 @Testpublic void testTopicExchange(){//队列名称String exchangeName = "itcast.topic";//消息String message = "某岛国核废水排海,没有一点道德.";rabbitTemplate.convertAndSend(exchangeName,"japan.news",message);}

重启测试方法,查看结果:

说明只有消费者2收到,也就是queue2匹配到了。(queue2是匹配以 .news结尾的所有RoutingKey)

成功!

3.6.4.总结

描述下Direct交换机与Topic交换机的差异?

  • Topic交换机接收的消息RoutingKey必须是多个单词,以 **.** 分割

  • Topic交换机与队列绑定时的bindingKey可以指定通配符

  • #:代表0个或多个词

  • *:代表1个词

3.7.消息转换器

之前说过,Spring会把你发送的消息序列化为字节发送给MQ,接收消息的时候,还会把字节反序列化为Java对象。

只不过,默认情况下Spring采用的序列化方式是JDK序列化。众所周知,JDK序列化存在下列问题:

  • 数据体积过大

  • 有安全漏洞

  • 可读性差

我们来测试一下。

3.7.1.测试默认转换器

我们修改消息发送的代码,发送一个Map对象:

 @Testpublic void testSendObjectQueue(){Map<String, Object> msg = new HashMap<>();msg.put("name","jack");msg.put("age",21);rabbitTemplate.convertAndSend("object.queue",msg);}

先停止consumer服务。

发送消息后查看控制台:

3.7.2.配置JSON转换器

显然,JDK序列化方式并不合适。我们希望消息体的体积更小、可读性更高,因此可以使用JSON方式来做序列化和反序列化。

在publisher服务中引入依赖:

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.10</version>
</dependency>

配置消息转换器。

在启动类中添加一个Bean即可:

@Bean
public MessageConverter jsonMessageConverter(){return new Jackson2JsonMessageConverter();
}

重新启动,并去控制台查看:

同样,消息接受时,也需要配置Json转换器

在pom文件引入依赖:

<!--jackson--><dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.10</version></dependency>

并且在启动类加上:

@Bean
public MessageConverter jsonMessageConverter(){return new Jackson2JsonMessageConverter();
}

然后在配置类中基于Bean的方式声明一个ObjectQueue:

 @Beanpublic Queue ObjectQueue(){return new Queue("object.queue");}

然后到consumer的SpringRabbitListenerTest中编写:

/*** 消息转换器,接受Object类型的数据*/@RabbitListener(queues = "object.queue")public void listenObjectQueue(Map<String, Object> msg){System.out.println("接受到object.queue消息:" + msg);}

重启consumer服务,查看结果:

说明配置JSON转换器成功!

以上就是RabbitMQ入门的全部内容了,感谢各位看客老爷观看!

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

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

相关文章

Neo4j 新手教程 环境安装 基础增删改查 python链接 常用操作 纯新手向

Neo4j安装教程&#x1f680; 目前在学习知识图谱的相关内容&#xff0c;在图数据库中最有名的就是Neo4j,为了降低入门难度&#xff0c;不被网上很多华丽呼哨的Cypher命令吓退&#xff0c;故分享出该文档&#xff0c;为自己手动总结&#xff0c;包括安装环境&#xff0c;增删改查…

程序员失业,被迫开启 PlanB——成为自由职业/独立开发者的第 0 天

程序员失业&#xff0c;被迫开启 PlanB——成为自由职业/独立开发者的第 0 天 今天在逛V2EX的时候看到的一个帖子&#xff0c;程序员中年被裁&#xff0c;被迫开启独立开发这条路。 原贴如下&#xff1a; lastday, 失业啦 公司年前通知我合同到期不续签&#xff0c;今天是我…

python爬虫(3)

上一次的代码结果如下&#xff1a; 当然会有一点点不一样是正常的表现&#xff0c;因为这个图本身使用随机数rand函数做的&#xff0c;用其他两种随机函数出来的结果也不会完全相同。 继上节这次带来的是数组的重塑和转置 1、一维数组的重塑 在NumPy模块中的reshape()函数可…

redis缓存(穿透, 雪崩, 击穿, 数据不一致, 数据并发竞争 ), 分布式锁(watch乐观锁, setnx, redission)

redis的watch缓存机制 WATCH 机制原理&#xff1a; WATCH 机制&#xff1a;使用 WATCH 监视一个或多个 key , 跟踪 key 的 value 修改情况&#xff0c;如果有key 的 value 值在事务 EXEC 执行之前被修改了&#xff0c;整个事务被取消。EXEC 返回提示信息&#xff0c;表示 事务已…

【开源】JAVA+Vue.js实现高校宿舍调配管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能需求2.1 学生端2.2 宿管2.3 老师端 三、系统展示四、核心代码4.1 查询单条个人习惯4.2 查询我的室友4.3 查询宿舍4.4 查询指定性别全部宿舍4.5 初次分配宿舍 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的…

AlibabaCloud微服务:Linux 部署 Sentinel 流量控制

目录 一、实验 1.环境 2.Linux 部署 Sentinel 3. 微服务接入Sentinel配置 二、 问题 1.Linux本地启动Sentinel控制台 2.JDBC连接失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统软件版本IP备注Linuxopenjdk 1.8.0192.168.204.200 maven3.5.0nac…

go 程序被意外kill后出现僵尸进程解决方案

go 管理自身子进程(防止僵尸进程出现) 写这篇文章是因为最近有同事竟然会知道异步启动子进程&#xff0c;不会关闭&#xff0c;最后导致导致僵尸进程出现&#xff0c;而且由于子进程会随着业务的使用越开越多&#xff0c;主进程一旦被kill掉就会不得不手动一个一个kill。 大概…

pytest 教程

1. 安装pytest 目前我使用的python版本是3.10.8 pip install pytest命令会安装下面的包&#xff1a; exceptiongroup-1.2.0-py3-none-any.whl iniconfig-2.0.0-py3-none-any.whl packaging-23.2-py3-none-any.whl pluggy-1.4.0-py3-none-any.whl pytest-8.0.2-py3-none-any.…

2024阿里云服务器ECS u1实例性能测评,CPU内存、网络和存储

阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xff0c…

JavaWeb - 3 - JavaScript(JS)

JavaScript(JS)官方参考文档&#xff1a;JavaScript 教程 JavaScript&#xff08;简称&#xff1a;JS&#xff09;是一门跨平台、面向对象的脚本语言&#xff0c;是用来控制网页行为的&#xff0c;它能使网页可交互&#xff08;脚本语言就不需要编译&#xff0c;直接通过浏览器…

基于Spring Boot+ Vue的房屋租赁系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

算法学习01:排序二分

算法学习01&#xff1a;排序&&二分 文章目录 算法学习01&#xff1a;排序&&二分前言需要记忆的模版&#xff1a;快速排序归并排序&#xff1a;整数二分&#xff1a;浮点数二分 一、排序1.快速排序2.归并排序&#xff1a; 二、二分1.整数2.浮点数 总结 前言 需要…

动态规划(算法竞赛、蓝桥杯)--树形DP积蓄程度

1、B站视频链接&#xff1a;E35 树形DP 积蓄程度_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; const int N200010,INF0x3f3f3f3f; int h[N],to[N*2],w[N*2],ne[N*2],tot; //邻接表 int d[N]; //d[i]记录从i点向下流出的最大流量 int f[N]; //f…

Linux Ubuntu 部署SVN

最近需要在ubuntu server上部署一个svn&#xff0c;记录 不需要特定版本 如果不需要特定版本&#xff0c;这样安装就是最简单的 sudo apt update然后开始安装 sudo apt install subversion等到安装完成之后执行查看版本命令&#xff0c;如果正常输出那就没问题了 svnadmin …

给一篇word注音可不可以只要拼音不要汉字 word中如何只保留拼音不要汉字

word中如何只保留拼音不要汉字&#xff0c;如果你想要只保留拼音而去除汉字&#xff0c;可以通过一系列步骤来实现。以下是一个详细的教程&#xff0c;帮助你完成这个任务。 首先&#xff0c;确保你的电脑已经安装了“汇帮注音大师”软件。如果没有&#xff0c;你需要安装一下…

基于单片机的商品RFID射频安全防盗报警系统设计

目 录 摘 要 I Abstract II 引 言 1 1 系统方案设计 3 1.1 总体设计要求 3 1.2 总体设计方案选择 3 1.3 总体控制方案选择 4 1.4 系统总体设计 5 2 项目硬件设计 7 2.1 单片机控制设计 7 2.2 按键电路设计 10 2.3 蜂鸣器报警电路设计 10 2.4 液晶显示电路设计 11 2.5 射频识别…

HTML—基本介绍

HTML是一种超文本标记语言(HyperText Markup Language)&#xff0c;用于创建网页的标记语言超文本&#xff1a;是指页面内可以包含图片、链接、声音、视频等内容标记&#xff1a;HTML富含大量的标签供程序员使用&#xff0c;通过标记符号来规定指定内容的样式 浏览器最终根据不…

FPGA 按键控制串口发送

按键消抖 消抖时间一般为10ms&#xff0c;我使用的板子是ACX720&#xff0c;晶振为50MHZ&#xff0c;20ns为一周期。 状态机 模块设计 设计文件 timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2023/01/11 12:18:36 // Design Name: // Module Name…

蓝牙系列七:开源蓝牙协议栈BTStack数据处理

继续蓝牙系列的研究。 在上篇博客,通过阅读BTStack的源码,大体了解了其框架,对于任何一个BTStack的应用程序都有一个main函数,这个main函数是统一的。这个main函数做了某些初始化之后,最终会调用到应用程序提供的btstack_main,在btstack_main里面首先做一些初始化,然后…

【Node.js从基础到高级运用】二、搭建开发环境

Node.js入门&#xff1a;搭建开发环境 在上一篇文章中&#xff0c;我们介绍了Node.js的基础概念。现在&#xff0c;我们将进入一个更实际的阶段——搭建Node.js的开发环境。这是每个Node.js开发者旅程中的第一步。接下来&#xff0c;我们将详细讨论如何安装Node.js和npm&#…