2.8日学习打卡----初学RabbitMQ(三)
2.8日学习打卡
一.springboot整合RabbitMQ

之前我们使用原生JAVA操作RabbitMQ较为繁琐,接下来我们使用
SpringBoot整合RabbitMQ,简化代码编写
创建SpringBoot项目,引入RabbitMQ起步依赖
<!-- RabbitMQ起步依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starteramqp</artifactId>
</dependency>
编写配置文件
spring:rabbitmq:host: 192.168.66.100port: 5672username: jjypassword: jjyvirtual-host: /
# 日志格式
logging:pattern:console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
SpringBoot整合RabbitMQ时,需要在配置类创建队列和交换机,
写法如下:
package com.jjy.springrabbitmqdemo;import org.springframework.amqp.core.*;import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitConfig {private final String EXCHANGE_NAME = "boot_topic_exchange";private final String QUEUE_NAME = "boot_queue";//创建交换机@Bean("bootExchange")public Exchange getExchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME)//交换机类型.durable(true)//是否持久化.build();}//创建队列@Bean("bootQueue")public Queue getMessageQueue() {return new Queue(QUEUE_NAME); // 队列名}//交换机绑定队列@Beanpublic Binding bindMessageQueue(@Qualifier("bootExchange") Exchange exchange, @Qualifier("bootQueue") Queue queue){return BindingBuilder.bind(queue).to(exchange).with("#.message.#").noargs();}
}
SpringBoot整合RabbitMQ_编写生产者

SpringBoot整合RabbitMQ时,提供了工具类RabbitTemplate发送
消息,编写生产者时只需要注入RabbitTemplate即可发送消息。
package com.jjy.springrabbitmqdemo;import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class TestProducer {//注入RabbitTemplate工具类@Autowiredprivate RabbitTemplate rabbitTemplate;@Test/*** 发送消息* 参数1:交换机* 参数2:路由key* 参数3:要发送的消息*/public void testSendMessage(){rabbitTemplate.convertAndSend("boot_topic_exchange","message","双十一开始了!");}
}
SpringBoot整合RabbitMQ_编写消费者

消费者
package com.jjy.rabbitmqcosspring.consumer;import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;//消费者
@Component
public class Consumer {//监听队列@RabbitListener(queues = "boot_queue")public void listenMessage(String message){System.out.println("监听的消息: "+message);}
}
整合后的代码,就是不用自己去实例化(创建连接工厂,连接,信道);让spring容器来控制实例的创建到销毁。
代码的实现有生产者和消费者、还有配置类(创建交换机跟队列及其绑定操作),都独立为一个类(共3个类),yml文件中配置rabbitmq的一些属性。
Direct类型(默认,匹配发送)
它会把消息路由到那些binding key与routing key完全匹配的Queue中。
它是一个一对一的模型,一条消息一定会被发到指定的一个队列(完全匹配)。
配置代码
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitDirectConfig {@Beanpublic Queue directQueue(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("directQueue-One",false,false,false,null);}@Beanpublic Queue directQueue2(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("directQueue-Two",false,false,false,null);}@Beanpublic DirectExchange directExchange(){//参数介绍//1.交换器名 2.是否持久化 3.自动删除 4.其他参数return new DirectExchange("MqSendService-One",false,false,null);}@Beanpublic Binding bingExchange(){return BindingBuilder.bind(directQueue()) //绑定队列.to(directExchange()) //队列绑定到哪个交换器.with("One"); //绑定路由key,必须指定}@Beanpublic Binding bingExchange2(){return BindingBuilder.bind(directQueue2()) //绑定队列.to(directExchange()) //队列绑定到哪个交换器.with("Two"); //绑定路由key,必须指定}
}
Topic类型(拓展匹配发送)
它是Direct类型的一种扩展,提供灵活的匹配规则。
- routing key为一个句点号 " . " 分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如"One.Two"
- binding key与routing key一样也是句点号 " . " 分隔的字符串
- binding key中可以存在两种特殊字符 " * " 与 " # " ,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitTopicConfig {@Beanpublic Queue topicQueue(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("topicQueue-One",false,false,false,null);}@Beanpublic Queue topicQueue2(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("topicQueue-Two",false,false,false,null);}@Beanpublic TopicExchange topicExchange(){//参数介绍//1.交换器名 2.是否持久化 3.自动删除 4.其他参数return new TopicExchange("Topic-Ex",false,false,null);}@Beanpublic Binding bingExchange(){return BindingBuilder.bind(topicQueue()) //绑定队列.to(topicExchange()) //队列绑定到哪个交换器.with("*.Two.*"); //路由key,必须指定}@Beanpublic Binding bingExchange2(){return BindingBuilder.bind(topicQueue2()) //绑定队列.to(topicExchange()) //队列绑定到哪个交换器.with("#"); //路由key,必须指定}
}
Fanout 类型(广播发送)
它会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。
它是一种一对多的类型,无法指定Binding Key,发送的一条消息会被发到绑定的所有队列。
配置代码
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitFanoutConfig {@Beanpublic Queue fanoutQueue(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("fanoutQueue-One",false,false,false,null);}@Beanpublic Queue fanoutQueue2(){//参数介绍//1.队列名 2.是否持久化 3.是否独占 4.自动删除 5.其他参数return new Queue("fanoutQueue-Two",false,false,false,null);}@Beanpublic FanoutExchange fanoutExchange(){//参数介绍//1.交换器名 2.是否持久化 3.自动删除 4.其他参数return new FanoutExchange("Fanout-Ex",false,false,null);}@Beanpublic Binding bingExchange(){return BindingBuilder.bind(fanoutQueue()) //绑定队列.to(fanoutExchange()); //队列绑定到哪个交换器}@Beanpublic Binding bingExchange2(){return BindingBuilder.bind(fanoutQueue()) //绑定队列.to(fanoutExchange()); //队列绑定到哪个交换器}}
Headers(键值对匹配,不常用)
headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。
在绑定Queue与Exchange时指定一组键值对;当消息发送到ExchangeRabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
该类型不常用,暂不提供代码。
Message(消息)
当执行诸如 basicPublish() 之类的操作时,内容作为字节数组参数传递,而其他属性作为单独的参数传入。
public class Message {private final MessageProperties messageProperties;private final byte[] body;public Message(byte[] body, MessageProperties messageProperties) {this.body = body;this.messageProperties = messageProperties;}public byte[] getBody() {return this.body;}public MessageProperties getMessageProperties() {return this.messageProperties;}...
}
MessageProperties 接口定义了几个常见的属性,例如“messageId”“timestamp”、“contentType”等等。 还可以通过调用 setHeader(String key, Object value) 方法扩展这些属性
二. 消息的可靠性投递

RabbitMQ消息投递的路径为:
生产者 —> 交换机 —> 队列 —> 消费者
在RabbitMQ工作的过程中,每个环节消息都可能传递失败,那么RabbitMQ是如何监听消息是否成功投递的呢?
- 确认模式(confirm)可以监听消息是否从生产者成功传递到交换机。
- 退回模式(return)可以监听消息是否从交换机成功传递到队列。
- 消费者消息确认(Consumer Ack)可以监听消费者是否成功处理消息。
三种模式刚好监听完RabbitMQ的一整套流程。即我们能够由这三种模式得到消息的传递及处理的结果。
确认模式(confirm)

确认模式(confirm)可以监听消息是否从生产者成功传递到交换机
生产者配置文件开启确认模式
rabbitmq:host: 192.168.66.100port: 5672username: jjypassword: jjyvirtual-host: /# 开启确认模式publisher-confirm-type: correlated
package com.jjy.rabbitproducer;import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitmqConfig {private final String EXCHNAGE_NAME="my_topic_exchange";private final String QUEUE_NAME="my_queue";@Bean("bootExchange")public Exchange getExchange(){return ExchangeBuilder.topicExchange(EXCHNAGE_NAME)//交换机类型.durable(true).build();}// 2.创建队列@Bean("bootQueue")public Queue getMessageQueue(){return QueueBuilder.durable(QUEUE_NAME) // 队列持久化.build();}@Beanpublic Binding bindMessageQueue(@Qualifier("bootExchange") Exchange exchange, @Qualifier("bootQueue") Queue queue){return BindingBuilder.bind(queue).to(exchange).with("my_routing").noargs();}
}
@SpringBootTest
public class ProduceTest {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void sendMessage(){// 定义确认模式的回调方法,消息向交换机发送后会调用confirm方法rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {/*** 被调用的回调方法* @param correlationData 相关配置信息* @param ack 交换机是否成功收到了消息* @param cause 失败原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {if (ack){System.out.println("confirm接受成功!");}else{System.out.println("confirm接受失败,原因为:"+cause);// 做一些处理。}}});rabbitTemplate.convertAndSend("my_topic_exchange","my_routing","send message...");}
}
退回模式(return)

退回模式(return)可以监听消息是否从交换机成功传递到队列,
使用方法如下:
生产者配置文件开启退回模式
spring:rabbitmq:host: 192.168.66.100port: 5672username: jjypassword: jjyvirtual-host: /# 开启确认模式publisher-confirm-type: correlated# 开启回退模式publisher-returns: true
package com.jjy.rabbitproducer;import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitmqConfig {private final String EXCHNAGE_NAME="my_topic_exchange";private final String QUEUE_NAME="my_queue";@Bean("bootExchange")public Exchange getExchange(){return ExchangeBuilder.topicExchange(EXCHNAGE_NAME)//交换机类型.durable(true).build();}// 2.创建队列@Bean("bootQueue")public Queue getMessageQueue(){return QueueBuilder.durable(QUEUE_NAME) // 队列持久化.build();}@Beanpublic Binding bindMessageQueue(@Qualifier("bootExchange") Exchange exchange, @Qualifier("bootQueue") Queue queue){return BindingBuilder.bind(queue).to(exchange).with("my_routing").noargs();}
}
@Testpublic void testReturn(){// 定义退回模式的回调方法。交换机发送到队列失败后才会执行returnedMessage方法rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback(){/*** @param returned 失败后将失败信息封装到参数中*/@Overridepublic void returnedMessage(ReturnedMessage returned) {System.out.println("消息对象:"+returned.getMessage());System.out.println("错误码:"+returned.getReplyCode());System.out.println("错误信息:"+returned.getReplyText());System.out.println("交换机:"+returned.getExchange());System.out.println("路由键:"+returned.getRoutingKey());}});rabbitTemplate.convertAndSend("my_topic_exchange","my_routing1","send message...");}
消费者消息确认(Ack)

在RabbitMQ中,消费者接收到消息后会向队列发送确认签收的消息,只有确认签收的消息才会被移除队列。这种机制称为消费者消息确认(Consumer Acknowledge,简称Ack)。类似快递员派送快递也需要我们签收,否则一直存在于快递公司的系统中。
消息分为自动确认和手动确认。自动确认指消息只要被消费者接收到,无论是否成功处理消息,则自动签收,并将消息从队列中移除。但是在实际开发中,收到消息后可能业务处理出现异常,那么消息就会丢失。此时需要设置手动签收,即在业务处理成功再通知签收消息,如果出现异常,则拒签消息,让消息依然保留在队列当
中。
- 自动确认:spring.rabbitmq.listener.simple.acknowledge=“none”
- 手动确认:spring.rabbitmq.listener.simple.acknowledge=“manual”
消费者配置开启手动签收
spring:rabbitmq:host: 192.168.0.162port: 5672username: itbaizhanpassword: itbaizhanvirtual-host: /# 开启手动签收listener:simple:acknowledge-mode: manual
package com.jjy.rabbitconsumer;import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
public class AckConsumer {
// @RabbitListener(queues = "my_queue")
// public void listenMessage(String Mesage){
// int i=1/0;
// System.out.println("成功接收到消息:"+Mesage);
// }
//@RabbitListener(queues = "my_queue")
public void listenMessage(Message message, Channel channel) throws InterruptedException, IOException {//消息投递序号,消息每次投递该值都会+1long deliveryTag = message.getMessageProperties().getDeliveryTag();try{int i=1/0;System.out.println("成功接收到消息:"+message);// 签收消息/*** 参数1:消息投递序号* 参数2:是否一次可以签收多条消息*/channel.basicAck(deliveryTag,true);} catch (Exception e){System.out.println("消息消费失败!");Thread.sleep(2000);// 拒签消息/*** 参数1:消息投递序号* 参数2:是否一次可以拒签多条消息* 参数3:拒签后消息是否重回队列*/channel.basicNack(deliveryTag,true,true);}}}
三.RabbitMQ高级特性
消费端限流

之前我们讲过MQ可以对请求进行“削峰填谷”,即通过消费端限流的方式限制消息的拉取速度,达到保护消费端的目的。
消费端限流的写法如下:
1 生产者批量发送消息
@Test
public void testSendBatch() {// 发送十条消息for (int i = 0; i < 10; i++) {rabbitTemplate.convertAndSend("my_topic_e
xchange", "my_routing", "send
message..."+i);}
}
2 消费端配置限流机制
spring:rabbitmq:host: 192.168.66.100port: 5672username: jjypassword: jjyvirtual-host: /listener:simple:# 限流机制必须开启手动签收acknowledge-mode: manual# 消费端最多拉取5条消息消费,签收后不满5
条才会继续拉取消息。prefetch: 5
3.消费者监听队列
@Component
public class OosConsimer {//@RabbitListener(queues ="my_queue")public void listenMessage(Message message, Channel channel) throws IOException, InterruptedException {// 1.获取消息System.out.println(new String(message.getBody()));// 2.业务处理Thread.sleep(3000);//3.签收long deliveryTag = message.getMessageProperties().getDeliveryTag();channel.basicAck(deliveryTag,true);}
}
就是说从生产端发送过来的消息,在队列等待消费端接收,如果消费端处理消息业务的速度相对较慢,积累的消息过多从而处理不过来(资源耗尽),会导致系统性能降低或瘫痪。
因为消费端每秒处理消息的条数有限,所以我们需要在消费端进行一个限流,故而限制了队列消息的投递。
即消费端限流也就是限制队列投递到消费端的流,也可以说是在队列与消费端之间进行一个限流。
利用限流实现不公平分发

在RabbitMQ中,多个消费者监听同一条队列,则队列默认采用的轮询分发。但在某种场景下这种策略并不是很好,例如消费者1处理任务的速度非常快,而其他消费者处理速度却很慢。此时如果采用公平分发,则消费者1有很大一部分时间处于空闲状态。此时可以采用不公平分发,即谁处理的快,谁处理的消息多。
公平分发则不能在yml文件中设置限流(prefetch),公平分发即给多个消费者平分消息进行消费。这样会导致处理快的消费者在等待,故而浪费资源,降低性能。
不公平分发则需要在yml文件中设置限流(prefetch),并且prefetch: 1(即设置为1);不公平分发即每次拉取一条消息,谁处理得快就继续处理,这样可以极大的节约资源,从而提高性能。
1 生产者批量发送消息
@Test
public void testSendBatch() {// 发送十条消息for (int i = 0; i < 10; i++) {rabbitTemplate.convertAndSend("my_topic_e
xchange", "my_routing", "send
message..."+i);}
}
2 消费端配置不公平分发
spring:rabbitmq:host: 192.168.66.100port: 5672username: jjypassword: jjyvirtual-host: /listener:simple:# 限流机制必须开启手动签收acknowledge-mode: manual# 消费端最多拉取1条消息消费,这样谁处理
的快谁拉取下一条消息,实现了不公平分发prefetch: 1
3 编写两个消费者
@Component
public class UnfairConsumer {// 消费者1@RabbitListener(queues = "my_queue")public void listenMessage1(Message message, Channel channel) throws Exception{//1.获取消息System.out.println("消费者1:"+newString(message.getBody(),"UTF-8"));//2. 处理业务逻辑Thread.sleep(500); // 消费者1处理快//3. 手动签收channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);// 消费者2@RabbitListener(queues = "my_queue")public void listenMessage2(Messagemessage, Channel channel) throws Exception{//1.获取消息System.out.println("消费者2:"+newString(message.getBody(),"UTF-8"));//2. 处理业务逻辑Thread.sleep(3000);// 消费者2处理慢//3. 手动签收channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}}
消息存活时间

RabbitMQ可以设置消息的存活时间(Time To Live,简称TTL),当消息到达存活时间后还没有被消费,会被移出队列。RabbitMQ可以对队列的所有消息设置存活时间,也可以对某条消息设置存活时间
设置队列所有消息存活时间
就是说需要在配置类(RabbitConfig)中设置队列所有消息的存活时间;
return QueueBuilder.durable(QUEUE_NAME)//队列持久化.ttl(10000)//设置队列的所有消息存活10s.build();
即在创建bean队列时,就要设置队列所有消息的存活时间。
**设置某条消息的存活时间 **
就是说只需要在发送的时候指定它的存活时间即可。
实现比较稍微麻烦一点,创建消息属性并设置存活时间,然后创建消息对象,消息对象 将消息属性作为参数,并且传入发送的消息,最后再将消息对象作为参数传给交换机,即可实现对单条消息设置存活时间。
//发送消息,并设置该消息的存活时间@Testpublic void testSendMessage(){//1.创建消息属性MessageProperties messageProperties = new MessageProperties();//2.设置存活时间messageProperties.setExpiration("10000");//3.创建消息对象Message message = new Message("sendMessage...".getBytes(),messageProperties);//4.发送消息rabbitTemplate.convertAndSend("my_topic_exchange1","my_routing",message);}
若设置中间的消息的存活时间,当过期时,该消息不会被移除,但是该消息已经不会被消费了,需要等到该消息到队里顶端才会被移除。
因为队列是头出,尾进,故而要移除它需要等到它在顶端时才可以。
在队列设置存活时间,也在单条消息设置存活时间,则以时间短的为准。
消息过期后,并不会马上移除消息,只有消息消费到队列顶
端时,才会移除该消息。
@Testpublic void testSendMessage2() {for (int i = 0; i < 10; i++) {if (i == 5) {// 1.创建消息属性MessageProperties messageProperties = new MessageProperties();// 2.设置存活时间messageProperties.setExpiration("10000 ");// 3.创建消息对象Message message = new Message(("send message..." + i).getBytes(), messageProperties);// 4.发送消息rabbitTemplate.convertAndSend("my_topi c_exchange", "my_routing", message);} else {rabbitTemplate.convertAndSend("my_topi c_exchange", "my_routing", "send message..." + i);}}}
在以上案例中,i=5的消息才有过期时间,10s后消息并没有马上被移除,但该消息已经不会被消费了,当它到达队列顶端时会被移除。
优先级队列
假设在电商系统中有一个订单催付的场景,即客户在一段时间内未付款会给用户推送一条短信提醒,但是系统中分为大型商家和小型商家。比如像苹果,小米这样大商家一年能给我们创造很大的利润,所以在订单量大时,他们的订单必须得到优先处理,此时就需要为不同的消息设置不同的优先级,此时我们要使用优先级队列
优先级队列用法如下:
1 创建队列和交换机
@Configuration
public class RabbitmqConfig3 {private final String EXCHANGE_NAME="priority_exchange";private final String QUEUE_NAME="priority_queue";@Bean(EXCHANGE_NAME)public Exchange priorityExchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME)//交换机类型.durable(true)//是否持久化.build();}@Bean(QUEUE_NAME)public Queue producerQueue(){return QueueBuilder.durable(QUEUE_NAME)//队列持久化//设置队列的最大优先级,最大可以设置到255,官网推荐不要超过10,,如果设置太高比较浪费资源.maxPriority(10).build();}@Beanpublic Binding bindPriority(@Qualifier(EXCHANGE_NAME) Exchange exchange, @Qualifier(QUEUE_NAME) Queue queue){return BindingBuilder.bind(queue).to(exchange).with("my_routing").noargs();}
}
2 编写生产者
@Testpublic void testPriority() {for (int i = 0; i < 10; i++) {if (i == 5) {// i为5时消息的优先级较高MessageProperties messageProperties = new MessageProperties();messageProperties.setPriority(9);Message message = new Message(("send message..." + i).getBytes(StandardCharsets.UTF_8), messageProperties);rabbitTemplate.convertAndSend("priority_exchange", "my_routing", message);} else {rabbitTemplate.convertAndSend("priority_exchange", "my_routing", "send message..." + i);}}}
3 编写消费者
@Component
public class PriorityConsumer {@RabbitListener(queues = "priority_queue")public void listenMessage(Message message, Channel channel) throws IOException {System.out.println(new String(message.getBody(),"utf-8"));//手动签收channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);}
}
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

相关文章:
2.8日学习打卡----初学RabbitMQ(三)
2.8日学习打卡 一.springboot整合RabbitMQ 之前我们使用原生JAVA操作RabbitMQ较为繁琐,接下来我们使用 SpringBoot整合RabbitMQ,简化代码编写 创建SpringBoot项目,引入RabbitMQ起步依赖 <!-- RabbitMQ起步依赖 --> <dependency&g…...
Unity学习笔记(零基础到就业)|Chapter02:C#基础
Unity学习笔记(零基础到就业)|Chapter02:C#基础 前言一、复杂数据(变量)类型part01:枚举数组1.特点2.枚举(1)基本概念(2)申明枚举变量(3ÿ…...
容器化的基础概念:不可变基础设施解释:将服务器视为乐高积木,而非橡皮泥。
不可变基础设施解释:将服务器视为乐高积木,而非橡皮泥。 想象一下用乐高积木代替橡皮泥进行搭建。使用橡皮泥时,您可以直接塑形和改变它。而使用乐高积木,您需要逐个零件搭建特定结构,并在需要时整体替换它们。这就是…...
智胜未来,新时代IT技术人风口攻略-第二版(弃稿)
文章目录 抛砖引玉 鸿蒙生态小科普焦虑之下 理想要落到实处校园鼎力 鸿蒙发展不可挡培训入场 机构急于吃红利企业布局 鸿蒙应用规划动智胜未来 技术人风口来临 鸿蒙已经成为行业的焦点,未来的发展潜力无限。作为一名程序员兼UP主,我非常荣幸地接受了邀请…...
Git分支和迭代流程
Git分支 feature分支:功能分支 dev分支:开发分支 test分支:测试分支 master分支:生产环境分支 hotfix分支:bug修复分支。从master拉取,修复并测试完成merge回master和dev。 某些团队可能还会有 reale…...
数据库管理-第150期 Oracle Vector DB AI-02(20240212)
数据库管理150期 2024-02-12 数据库管理-第150期 Oracle Vector DB & AI-02(20240212)1 LLM2 LLM面临的挑战3 RAG4 向量数据库LLM总结 数据库管理-第150期 Oracle Vector DB & AI-02(20240212) 作者:胖头鱼的鱼…...
MySQL双写机制
双写机制 问题的出现 在发生数据库宕机时,可能Innodb正在写入某个页到表中,但是这个页只写了一部分,这种情况被称为部分写失效,虽然innodb会先写重做日志,在修改页,但是重做日志中记录的是对页的物理操作,但…...
uniapp的配置和使用
①安装环境和编辑器 注册小程序账号 微信开发者工具下载 uniapp 官网 HbuilderX 下载 首先先下载Hbuilder和微信开发者工具 (都是傻瓜式安装),然后注册小程序账号: 拿到appid: ②简单通过demo使用微信开发者工具和…...
【ES】--Elasticsearch的分词器深度研究
目录 一、问题描述及分析二、analyze分析器原理三、 multi-fields字段支持多场景搜索(如同时简繁体、拼音等)1、ts_match_analyzer配置分词2、ts_match_all_analyzer配置分词3、ts_match_1_analyzer配置分词4、ts_match_2_analyzer配置分词5、ts_match_3_analyzer配置分词6、ts…...
【Langchain Agent研究】SalesGPT项目介绍(三)
【Langchain Agent研究】SalesGPT项目介绍(二)-CSDN博客 上节课,我们介绍了salesGPT项目的初步的整体结构,poetry脚手架工具和里面的run.py。在run.py这个运行文件里,引用的最主要的类就是SalesGPT类,今天我…...
Java安全 URLDNS链分析
Java安全 URLDNS链分析 什么是URLDNS链URLDNS链分析调用链路HashMap类分析URL类分析 exp编写思路整理初步expexp改进最终exp 什么是URLDNS链 URLDNS链是Java安全中比较简单的一条利用链,无需使用任何第三方库,全依靠Java内置的一些类实现,但…...
【网站项目】026校园美食交流系统
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
使用raw.gitmirror.com替换raw.githubusercontent.com以解决brew upgrade python@3.12慢的问题
MacOS系统上,升级python3.12时,超级慢,而且最后还失败了。看了日志,发现是用curl从raw.githubusercontent.com上下载Python安装包超时了。 解决方案一:开启翻墙工具,穿越围墙 解决方案二:使用…...
深度学习的进展
#深度学习的进展# 深度学习的进展 深度学习是人工智能领域的一个重要分支,它利用神经网络模拟人类大脑的学习过程,通过大量数据训练模型,使其能够自动提取特征、识别模式、进行分类和预测等任务。近年来,深度学习在多个领域取得…...
[高性能] - 缓存架构
对于交易系统来说,低延时是核心业务的基本要求。因此需要对业务进行分级,还需要对数据按质量要求进行分类,主要包含两个维度:重要性,延时要求,数据质量。共包含以下三种场景: 1. 重要 延时性要…...
django实现外键
一:介绍 在Django中,外键是通过在模型字段中使用ForeignKey来实现的。ForeignKey字段用于表示一个模型与另一个模型之间的多对一关系。这通常用于关联主键字段,以便在一个模型中引用另一个模型的相关记录。 下面是一个简单的例子࿰…...
飞天使-k8s知识点14-kubernetes散装知识点3-Service与Ingress服务发现控制器
文章目录 Service与Ingress服务发现控制器存储、配置与角色 Service与Ingress服务发现控制器 在 Kubernetes 中,Service 和 Ingress 是两种不同的资源类型,它们都用于处理网络流量,但用途和工作方式有所不同。Service 是 Kubernetes 中的一个…...
任务调度
1.学习目标 1.1 定时任务概述 1.2 jdk实现任务调度 1.3 SpringTask实现任务调度 1.4 Spring-Task 分析 1.5 Cron表达式 https://cron.qqe2.com/ 2. Quartz 基本应用 2.1 Quartz 基本介绍 2.2 Quartz API介绍 2.3 入门案例 <dependency> <groupId>org.springframe…...
深刻反思现代化进程:20世纪与21世纪的比较分析及东西方思想家的贡献
深刻反思现代化进程:20世纪与21世纪的比较分析及东西方思想家的贡献 摘要:随着人类社会的快速发展,现代化已成为全球范围内的普遍追求。然而,20世纪至21世纪的现代化进程并非一帆风顺,它伴随着环境破坏、社会不平等和文…...
【FTP讲解】
FTP讲解 1. 介绍2. 工作原理3. 传输模式4. 安全5. 设置FTP服务器6. FTP命令 1. 介绍 FTP(File Transfer Protocol)是“文件传输协议”的英文缩写,它是用于在网络上进行数据传输的一种协议。FTP是因特网上使用最广泛的协议之一,它…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
Centos 7 服务器部署多网站
一、准备工作 安装 Apache bash sudo yum install httpd -y sudo systemctl start httpd sudo systemctl enable httpd创建网站目录 假设部署 2 个网站,目录结构如下: bash sudo mkdir -p /var/www/site1/html sudo mkdir -p /var/www/site2/html添加测试…...
记一次spark在docker本地启动报错
1,背景 在docker中部署spark服务和调用spark服务的微服务,微服务之间通过fegin调用 2,问题,docker容器中服务器来后,注册中心都有,调用服务也正常,但是调用spark启动任务后报错,报错…...
2025-06-01-Hive 技术及应用介绍
Hive 技术及应用介绍 参考资料 Hive 技术原理Hive 架构及应用介绍Hive - 小海哥哥 de - 博客园https://cwiki.apache.org/confluence/display/Hive/Home(官方文档) Apache Hive 是基于 Hadoop 构建的数据仓库工具,它为海量结构化数据提供类 SQL 的查询能力…...
基于Java的离散数学题库系统设计与实现:附完整源码与论文
JAVASQL离散数学题库管理系统 一、系统概述 本系统采用Java Swing开发桌面应用,结合SQL Server数据库实现离散数学题库的高效管理。系统支持题型分类(选择题、填空题、判断题等)、难度分级、知识点关联,并提供智能组卷、在线测试…...
