微服务—RabbitMQ

news2024/11/28 2:48:31

目录

初识MQ

同步和异步通讯

同步通讯的优缺点

异步调用方案

异步通信优缺点

常见MQ技术对比 

RabbitMQ快速入门 

安装RabbitMQ

RabbitMQ整体架构与相关概念 

常见消息模型​编辑

入门案例

SpringAMQP

基本介绍

SpringAMQP案例——模拟HelloWorld消息模型

SpringAMQP案例——模拟WorkQueue消息模型

SpringAMQP案例——模拟发布订阅消息模型

发布订阅消息模型介绍

案例——FanoutExchange

案例——DirectExchange

案例——TopicExchange

消息转换器


初识MQ

同步和异步通讯

        微服务间通讯有同步和异步两种方式:同步通讯就像打电话,需要实时响应,异步通讯就像发邮件,不需要马上回复。两种方式各有优劣,打电话可以立即得到响应,但是你却不能跟多个人同时通话。发送邮件可以同时与多个人收发邮件,但是往往响应会有延迟。


同步通讯的优缺点

优点:
时效性较强,可以立即得到结果。
缺点:
1. 耦合度高:每次加入新的需求,都要修改原来的代码;
2. 性能下降:调用者需要等待服务提供者响应,如果调用链过长则响应时间等于每次调用的时间之和;
3. 资源浪费:调用链中的每个服务在等待响应过程中,不能释放请求占用的资源,高并发场景下会极度浪费系统资源;
4. 级联失败:如果服务提供者出现问题,所有调用方都会跟着出问题如同多米诺骨牌一样,迅速导致整个微服务群故障。


异步调用方案

        我们以购买商品为例,用户支付后需要调用订单服务完成订单状态修改,调用物流服务,从仓库分配响应的库存并准备发货。
        在事件模式中,支付服务是事件发布者,在支付完成后只需要发布一个支付成功的事件,事件中带上订单id。
        订单服务和物流服务是事件订阅者,订阅支付成功的事件,监听到事件后完成自己业务即可。
        为了解除事件发布者与订阅者之间的耦合,两者并不是直接通信,而是有一个中间Broker。发布者发布事件到Broker,不关心谁来订阅事件。订阅者从Broker订阅事件,不关心谁发来的消息。


异步通信优缺点

优点:
1. 吞吐量提升:无需等待订阅者处理完成,响应更快速;
2. 故障隔离:服务没有直接调用,不存在级联失败问题;
3. 调用间没有阻塞,不会造成无效的资源占用;
4. 耦合度极低,每个服务都可以灵活插拔,可替换;
5. 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件。
缺点:
1. 架构复杂了,业务没有明显的流程线,不好管理;
2. 需要依赖于Broker的可靠、安全、性能。


常见MQ技术对比 


RabbitMQ快速入门 

安装RabbitMQ

步骤1 在线拉取镜像

​​​​​​​docker pull rabbitmq:3-management

步骤2 执行下面的命令来运行MQ容器,在命令行中设置用户名和密码

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

步骤3 进入RabbitMQ管理平台

        安装好后,安装好后通过IP+端口访问管理界面。管理界面端口是15672,tcp连接的端口是5672。在浏览器中访问192.168.237.128:1567进入RabbitMQ管理平台,其中192.168.237.128为虚拟机ip地址。


RabbitMQ整体架构与相关概念 

RabbitMQ架构图

RabbitMQ中的几个概念
channel:操作MQ的工具
exchange:路由消息到队列中
queue:缓存消息
virtualhost:虚拟主机,是对queue、exchange等资源的逻辑分组 


常见消息模型


入门案例

发布者发送消息代码:

public class PublisherTest {
    @Test
    public void testSendMessage() throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("192.168.150.101");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("itcast");
        factory.setPassword("123321");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.发送消息
        String message = "hello, rabbitmq!";
        channel.basicPublish("", queueName, null, message.getBytes());
        System.out.println("发送消息成功:【" + message + "】");

        // 5.关闭通道和连接
        channel.close();
        connection.close();

    }
}

运行之后可以在RabbitMQ管理平台看到队列里已经有一个消息

点击该消息,通过Get Message可以查看接收到的消息内容

 消费者建立连接代码:

public class ConsumerTest {
    @Test
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.建立连接
        ConnectionFactory factory = new ConnectionFactory();
        // 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
        factory.setHost("192.168.237.128");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("root");
        factory.setPassword("123456");
        // 1.2.建立连接
        Connection connection = factory.newConnection();

        // 2.创建通道Channel
        Channel channel = connection.createChannel();

        // 3.创建队列
        String queueName = "simple.queue";
        channel.queueDeclare(queueName, false, false, false, null);

        // 4.订阅消息
        channel.basicConsume(queueName, true, new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                // 5.处理消息
                String message = new String(body);
                System.out.println("接收到消息:【" + message + "】");
            }
        });
        System.out.println("等待接收消息。。。。");
    }
}

运行之后可以在RabbitMQ管理平台发现已经建立起一个连接,并且能够在idea控制台观察到从消息队列里获取的消息。

​​​​​​​

小结:

发布者和生产者的代码中,都有创建队列这一部分,是否会产生冲突?
由于无法确定发布者和生产者运行的前后顺序,为避免寻找不到所需绑定的队列,因此都需要创建队列,如果该队列已经存在,也不会产生冲突。

基本消息队列的消息发送流程
1.建connection
2.创建channel
3.利用channel声明队列
4.利用channel向队列发送消息

基本消息队列的消息接收流程
1.建connection
2.创建channel
3.利用channel声明队列
4.定义consumer的消费行为handleDelivery05.利用channel将消费者与队列绑定


SpringAMQP

基本介绍

AMQP(Advanced Message Queuing Protocol)是用于在应用程序或之间传递业务消息的开放标准。该协议与语言和平台无关,更符合微服务中独立性的要求。

Spring AMQP是基于AMQP协议定义的一套API规范,提供了模板来发送和接收消息。包含两部分,其中spring-amqp是基础抽象spring-rabbit是底层的默认实现。


SpringAMQP案例——模拟HelloWorld消息模型

模拟消息发送:

步骤1. 在工程中引入AMQP依赖

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

步骤2. 配置MQ地址:在publisher服务的application.yml中添加配置

spring:
  rabbitmq:
    host: 192.168.237.128 # rabbitMQ的ip地址
    port: 5672 # 端口
    username: root
    password: 123456
    virtual-host: /

步骤3. 在publisher服务中设置测试类,利用convertAndSend方法实现信息发送

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void testSendMessage2SimpleQueue() {
        String queueName = "simple.queue";
        String message = "hello,spring amqp";
        rabbitTemplate.convertAndSend(queueName,message);
    }
}

运行之后可以看到消息队列中存在一条消息,消息内容为我们发送的内容


模拟消息接收:

步骤1. 在工程中引入AMQP依赖

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

步骤2. 配置MQ地址:在consumer服务的application.yml中添加配置

spring:
  rabbitmq:
    host: 192.168.237.128 # rabbitMQ的ip地址
    port: 5672 # 端口
    username: root
    password: 123456
    virtual-host: /

步骤2. 在consumer服务中新建一个类,编写消费逻辑

@Component
public class SpringRabbitListener {

    @RabbitListener(queues = "simple.queue")
    public void listenSimpleQueue(String msg){
        System.out.println("消费者接收到simple.queue的消息:【" + msg + "】");
    }
}

运行之后,通过idea控制台可以看到已经成功接收到消息

注意:消息一旦消费就会从队列删除,RabbitMQ没有消息回溯功能。


SpringAMQP案例——模拟WorkQueue消息模型

根据WorkQueue消息模型,有一个发布者和两个消费者,因此我们模拟消费者发送大量消息的情况。

/**
     * 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);
    }
}

再设置两个消费者,通过设置sleep休眠,明显区分两个消费者的性能。

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

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

先运行发布者,发布50条消息,再运行消费者,控制台信息如下:

        通过观察控制台信息,我们可以发现,消费者1只接收单数的消息,而消费者2只接收双数的消息。当消费者1很快完成了自己的25条消息,消费者2却在缓慢的处理自己的25条消息。

        这是由WorkQueue消息模型中的消息预取机制所导致的。在该机制下,消费者无论当前是否能够处理消息,都会提前从队列中取出消息。因此,所有的消息会被平均分配给两个消费者。结果是,无论哪个消费者的接收速度较快,整体的消息接收速度都取决于接收速度较慢的消费者。

        为了解决这个问题,我们可以通过设置消费者每次获取消息的数量来进行限制。只允许每个消费者一次获取一条消息,并且要求在当前消息处理完毕之后才能获取下一条消息。通过这种方式,我们可以实现能者多劳的效果。

经过设置后,我们再次运行程序,可以发现结果不再像之前一样,消费者1能够根据自己的性能接收更多消息。

​​​​​​​


SpringAMQP案例——模拟发布订阅消息模型

发布订阅消息模型介绍

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

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

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

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

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

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

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

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


案例——FanoutExchange

步骤1. 在consumer中创建一个类,用于声明队列和交换机,并将队列与交换机进行绑定

@Configuration
public class FanoutConfig {
    // 声明交换机 itcast.fanout
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(("itcast.fanout"));
    }

    // 声明fanout.queue1
    @Bean
    public Queue fanoutQueue1() {
        return new Queue("fanout.queue1");
    }

    // 绑定队列1到交换机
    @Bean
    public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder
                .bind(fanoutQueue1)
                .to(fanoutExchange);
    }

    // 声明fanout.queue2
    @Bean
    public Queue fanoutQueue2() {
        return new Queue("fanout.queue2");
    }

    // 绑定队列2到交换机
    @Bean
    public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
        return BindingBuilder
                .bind(fanoutQueue2)
                .to(fanoutExchange);
    }

}

重新运行后,在RabbitMQ管理平台中可以看到已经生成该交换机,并且与两个队列绑定。

      

步骤2:在consumer服务的SpringRabbitListener中添加两个方法,作为消费者,对两个队列进行监听

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

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

步骤3. 在publisher服务的SpringAmqpTest类中添加测试方法,将消息发送到交换机

@Test
public void testSendFanoutExchange() {
    //交换机名称
    String exchangeName = "itcast.fanout";
    //消息
    String message = "hello, every one!";
    //发送消息
    rabbitTemplate.convertAndSend(exchangeName,"", message);
}

运行该测试方法,可以在控制台中看到消费者成功接收到消息。 

总结

交换机的作用是什么?
1. 接收publisher发送的消息
2. 将消息按照规则路由到与之绑定的队列
3. 不能缓存消息,路由失败,消息丢失
4. FanoutExchange的会将消息路由到每个绑定的队列

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


案例——DirectExchange

在Direct模型下:
1. 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)
2. 消息的发送方在向 Exchange发送消息时,也必须指定消息的 RoutingKey
3. Exchange不再把消息交给每一个绑定的队列,而是根据消息的 outing key 进行判断,只有队列的Routingkey 与消息的 Routing key 完全一致,才会接收到消息

基于@Bean的方式声明队列和交换机比较麻烦,Spring还提供了基于注解方式来声明。
步骤1. 在consumer的SpringRabbitListener中添加两个消费者,同时基于注解来声明队列和交换机

    @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("消费者接收到direct.queue1的消息:【" + msg + "】");
    }

    @RabbitListener( bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
            key = {"red", "yellow"}
    ))
    public void listenDirectQueue2(String msg) {
        System.err.println("消费者接收到direct.queue2的消息:【" + msg + "】");
    }

步骤2. 在publisher服务的SpringAmqpTest类中添加测试方法

@Test
public void testSendDirectExchange() {
    //交换机名称
    String exchangeName = "itcast.direct";
    //消息
    String message = "hello, blue!";
    //发送消息
    rabbitTemplate.convertAndSend(exchangeName,"yellow", message);
}

当key为red时,控制台输出如下:

当key为blue时,控制台输出如下:

​​​​​​​

当key为yellow时,控制台输出如下:

总结

描述下Direct交换机与Fanout交换机的差异?
1. Fanout交换机将消息路由给每一个与之绑定的队列
2. Direct交换机根据RoutingKey判断路由给哪个队列
3. 如果多个队列具有相同的RoutingKey,则与Fanout功能类似

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


案例——TopicExchange

步骤1. 在consumer的SpringRabbitListener中添加两个消费者,同时基于注解来声明队列和交换机

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

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

步骤2. 在publisher服务的SpringAmqpTest类中添加测试方法

@Test
public void testSendTopicExchange() {
    //交换机名称
    String exchangeName = "itcast.topic";
    //消息
    String message = "世界新闻";
    //发送消息
    rabbitTemplate.convertAndSend(exchangeName,"world.news", message);
}

当key为china.news时,控制台输出如下:

 当key为china.weather时,控制台输出如下:

 当key为workd.news时,控制台输出如下:

 


消息转换器

Spring会把发送的消息序列化为字节发送给MQ,接收消息的时候,还会把字节反序列化为Java对象。只不过,默认情况下Spring采用的序列化方式是JDK序列化,而JDK序列化存在一些问题,如数据体积过大、有安全漏洞、可读性差等。

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

步骤1. 在publisher和consumer两个服务中都引入依赖:

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

步骤2. 配置消息转换器,在启动类中添加一个Bean即可:

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

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

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

相关文章

【数据结构 08】红黑树

一、概述 红黑树&#xff0c;是一种二叉搜索树&#xff0c;每一个节点上有一个存储位表示节点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从根到叶子的路径上各个节点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长上两倍&#xff0c;因而是接进…

《区块链简易速速上手小册》第8章:区块链的技术挑战(2024 最新版)

文章目录 8.1 可扩展性问题8.1.1 基础知识8.1.2 主要案例&#xff1a;比特币的可扩展性挑战8.1.3 拓展案例 1&#xff1a;以太坊的可扩展性改进8.1.4 拓展案例 2&#xff1a;侧链和分层解决方案 8.2 安全性与隐私8.2.1 基础知识8.2.2 主要案例&#xff1a;比特币交易的安全性8.…

没有外网Nginx如何配置如何开启https

判断是否支持open-ssl 在服务器执行如下命令 openssl version没有则安装open-ssl&#xff0c;由于服务器没有外网&#xff0c;可以离线安装openssl-3.0.1.tar.gz&#xff0c;我是在有网的服务器直接下载的&#xff0c;然后再上传到这台无网的服务器上 wget https://www.open…

45 漏洞发现-API接口服务之漏洞探针类型利用修复

目录 端口服务类安全测试API接口-webservice RESTful APT 演示案例:端口服务类-Tomcat弱口令安全问题端口服务类-Glassfish任意文件读取其他补充类-基于端口WEB站点又测试其他补充类-基于域名WEB站点又测试其他补充类-基于IP配合端口信息再收集口令安全脚本工具简要使用-Snetcr…

小白级教程,10秒开服《幻兽帕鲁》

在帕鲁的世界&#xff0c;你可以选择与神奇的生物「帕鲁」一同享受悠闲的生活&#xff0c;也可以投身于与偷猎者进行生死搏斗的冒险。帕鲁可以进行战斗、繁殖、协助你做农活&#xff0c;也可以为你在工厂工作。你也可以将它们进行售卖&#xff0c;或肢解后食用。 前言 马上过年…

全网最简单的幻兽帕鲁服务器搭建教程

幻兽帕鲁是一款备受欢迎的多人在线游戏&#xff0c;为了提供更好的游戏体验&#xff0c;许多玩家选择自行搭建服务器。本文将指导大家如何简单快速地搭建幻兽帕鲁服务器&#xff0c;轻松享受游戏的乐趣。 第一步&#xff1a;购买游戏联机服务器 购买入口&#xff1a;https://tx…

充电桩项目实战:短信功能 分布式限流

你好&#xff0c;我是田哥 最近&#xff0c;我在对充电桩项目进行微服务升级中&#xff0c;肯定会遇到一些问题 前面分享了&#xff1a;充电桩项目实战&#xff1a;搞定多数据源&#xff01; 题外话&#xff1a;如果想年后找到更好的工作&#xff0c;推荐看这篇文章&#xff1a…

了解Ansible自动化运维工具及模块的使用

一、Ansible的相关知识 1.1 Ansible工具的了解 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可以实现。Ansible…

如何使用内网穿透工具在公网实现实时监测DashDot服务器仪表盘

文章目录 1. 本地环境检查1.1 安装docker1.2 下载Dashdot镜像 2. 部署DashDot应用3. 本地访问DashDot服务4. 安装cpolar内网穿透5. 固定DashDot公网地址 本篇文章我们将使用Docker在本地部署DashDot服务器仪表盘&#xff0c;并且结合cpolar内网穿透工具可以实现公网实时监测服务…

如何通过CVE漏洞编码找到对应的CVE漏洞详情及源码修改地址

背景&#xff1a; 最近正在使用docker进行一些cve漏洞的复现&#xff0c;有时候就要通过CVE的漏洞编码&#xff0c;找到对应的漏洞详情&#xff0c;以及漏洞的源码修改 以我上一篇文章的CVE-2020-17518编码为例 Apache Flink文件上Apache Flink文件上 方法&#xff1a; 通…

【C++4】内存管理

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…

初识C语言·动态内存开辟

目录 1 为什么要有动态内存开辟 2 malloc函数的使用 3 free函数的使用 4 calloc函数的使用 5 realloc函数的使用 6 常见的动态内存开辟的错误 1&#xff09;对空指针的解引用 2&#xff09;对动态内存开辟空间的越界访问 我们使用了calloc函数开辟了10个整型空间&…

【MyBatis】MyBatis是什么?作用?怎么实现?

一、MyBatis是什么 MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff08;Plain …

spring-boot-admin的介绍和使用

概述 Spring Boot 有一个非常好用的监控和管理的源软件&#xff0c;这个软件就是 Spring Boot Admin。该软件能够将 Actuator 中的信息进行界面化的展示&#xff0c;也可以监控所有 Spring Boot 应用的健康状况&#xff0c;提供实时警报功能。 主要的功能点有&#xff1a; 显…

排序(4)——快速排序

五、快速排序 1.简介 快速排序是Hoare于1962年提出的&#xff0c;主要采取了分治的思想。快速排序首先确定一个基准值&#xff0c;然后以这个选出的基准值为标准&#xff0c;将整个数组进行按大小进行区分&#xff0c;使得小于该基准值的位于其一侧&#xff0c;大于基准值的位…

超越GPT4 Turbo?科大讯飞发布星火认知大模型3.5版本

简介 1月30日&#xff0c;科大讯飞举行星火认知大模型V3.5升级发布会。科大讯飞董事长刘庆峰、研究院院长刘聪正式发布基于首个全国产算力训练的讯飞星火V3.5&#xff0c;七大核心能力全面提升。 功能展示多模交互 多模理解&#xff1a;上传图片素材&#xff0c;大模型完成识…

C++/数据结构:二叉搜索树的实现与应用

目录 一、二叉搜索树简介 二、二叉搜索树的结构与实现 2.1二叉树的查找与插入 2.2二叉树的删除 2.3二叉搜索树的实现 2.3.1非递归实现 2.3.2递归实现 三、二叉搜索树的k模型和kv模型 一、二叉搜索树简介 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0…

vue——实现多行粘贴到table事件——技能提升

最近在写后台管理系统时&#xff0c;遇到一个需求&#xff0c;就是要从excel表格中复制多行内容&#xff0c;然后粘贴到后台系统中的table表格中。 如下图所示&#xff1a;一次性复制三行内容&#xff0c;光标放在红框中的第一个框中&#xff0c;然后按ctrlv粘贴事件&#xff0…

路由备份聚合排错

目录 实验拓扑图 实验要求 实验排错 故障一 故障现象 故障分析 故障解决 故障二 故障现象 故障分析 故障解决 故障三 故障现象 故障分析 故障解决 故障四 故障现象 故障分析 故障解决 故障五 故障现象 故障分析 故障解决 实验拓扑图 实验要求 按照图示配…

Typora导出html文件图片自动转换成base64

Typora导出html文件图片自动转换成base64 一、出现问题二、解决方案三、编码实现3.1.创建Java项目3.2.代码3.3.打包成Jar包 四、如何使用endl 一、出现问题 typora 导出 html 的时候必须带有原图片&#xff0c;不方便交流学习&#xff0c;文件太多显得冗余&#xff0c;只有将图…