初识RabbitMQ

一、消息队列

1、消息队列的介绍

        在介绍RabbitMQ之前,首先来介绍下消息队列。消息队列是生产者-消费者模型的一个典型的代表,由一端往消息队列中不断的写入消息,而另一端则可以读取或者订阅队列中的消息。当新的消息入队时,就会通知接收方进行处理,消息的发送方称为生产者,消息的接收方称为消费者。那么,当我们发起HTTP请求的时候,就可以将请求丢到消息队列中,由消费者取出。这种加入“中间商”的方式,很好的实现了解耦,并且在高并发的情况下,由于消费者能力有限,消息队列也可以扮演“削峰填谷”的作用,先堆积一部分的请求,然后由消费者进行慢处理,避免大量请求导致接口崩溃的情况。常见的消息队列有以下几种:

  • RabbitMQ:性能很强、吞吐量很高,且支持多种协议、集群化,适合于企业级开发
  • Kafka:提供了超高的吞吐量,ms级别的延迟,具有极高的可用性以及可靠性,且分布式可以任意扩展
  • RocketMQ:阿里巴巴推出的消息队列,单机吞吐量高、消息的高可靠性,扩展性很强,支持事务

 2、消息队列的作用

(1)解耦

        假设有系统B、C、D都需要系统A的数据,于是系统A调用三个方法发送数据到B、C、D。这时,系统D不需要了,那就需要在系统A把相关的代码删掉。假设这时有个新的系统E需要数据,这时系统A又要增加调用系统E的代码。为了降低这种强耦合,就可以使用MQ,系统A只需要把数据发送到MQ,其他系统如果需要数据,则从MQ中获取即可。

(2)异步

        一个客户端请求发送进来,系统A会调用系统B、C、D三个系统,同步请求的话,响应时间就是系统A、B、C、D的总和,也就是800ms。如果使用MQ,系统A发送数据到MQ,然后就可以返回响应给客户端,不需要再等待系统B、C、D的响应,可以大大地提高性能。对于一些非必要的业务,比如发送短信,发送邮件等等,就可以采用MQ。

(3)削峰填谷

        假设系统A在某一段时间请求数暴增,有5000个请求发送过来,系统A这时就会发送5000条SQL进入MySQL进行执行,MySQL对于如此庞大的请求当然处理不过来,MySQL就会崩溃,导致系统瘫痪。如果使用MQ,系统A不再是直接发送SQL到数据库,而是把数据发送到MQ,MQ短时间积压数据是可以接受的,然后由消费者每次拉取2000条进行处理,防止在请求峰值时期大量的请求直接发送到MySQL导致系统崩溃。

此处参考博客:https://blog.csdn.net/Rok728/article/details/123106242

二、RabbitMQ介绍

1、RabbitMQ

(1)RabbitMQ的设计架构       

        RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件,其架构如下:

可以看到:

  • 生产者:消息的发送方
  • 消费者:消息的接收方
  • Channel:每个客户端连接都会使用一个Channel,再通过Channel去访问到RabbitMQ服务器,这里的通信协议不是HTTP,而是amqp协议
  • Exchange:类似于交换机,会根据我们的请求转发给相应的消息队列,每个队列都可以绑定到Exchange上,这样Exchange就可以将数据转发给队列了。Exchange可以存在很多个,不同的Exchange类型可以用于实现不同的消息的模式。
  • Queue:消息队列本体,生产者的所有消息都在消息队列中,由消费者取出
  • Virtual Host:类似于环境隔离。不同环境可以单独配置一个Virtual Host,每个Virtual Host可以包含很多个Exchange和Queue,每个Virtual Host之间互不影响

(2)RabbitMQ的特点

  • 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。
  • 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。
  • 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
  • 多种协议。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。
  • 支持多种语言客户端。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。
  • 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。
  • 插件机制。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。

2、RabbitMQ安装

        前面介绍了RabbitMQ是一款使用Erlang语言开发的,因此在RabbitMQ的安装的过程中需要下载ErLang安装包和RabbitMQ安装包,这里需要注意的是ErLang和RabbitMQ的版本的匹配,可参考此网站,查看Erlang和RabbitMQ不同版本之间的对应关系:RabbitMQ Erlang Version Requirements — RabbitMQ

我在安装使用的Erlang的版本是26.1.2,RabbitMQ的版本是3.12.2,这里以Windows下的RabbitMQ安装为例进行介绍。

(1)Erlang安装

Erlang官网:http://www.erlang.org/downloads

下载之后一路点击next即可。

(2)RabbitMQ安装

 RabbitMQ官网:http://www.rabbitmq.com/

 下载安装之后,在RabbitMQ的安装路径的sbin目录下,打开终端,执行:rabbitmq-plugins enable rabbitmq_management命令安装管理页面的插件。

 

然后双击rabbitmq-server.bat启动脚本,打开浏览器输入http://localhost:15672,账号密码默认是:guest/guest

 

3、消息发送接收案例

这里我们先通过界面模拟一个消息的发送案例。

(1)新建队列

点击Quues and Streams创建一个队列first_queue。

(2)查看交换机

点击Exchanges,查看当前的交换机。

(3)发送消息

这里先选择一个交换机,以amp.direct为例,点击进入。

 在其下方可以看到Publish message,然后在Payload中填写消息信息:this is a message,点击发送,这时弹出提示可以看到:消息发布了,但是没有被路由。在前面我们介绍了:消息发送给交换机之后,交换机会将消息路由到队列中。但是这里并没有路由到队列中,这是为什么呢?很简单,因为目前amq.redirect交换机和first_queue队列之间并没有绑定,那么自然而然的交换机就不会将消息路由到队列中。从这里我们可以看到:交换机是负责消息的路由转发的,并没有存储消息的能力。那么如何实现交换机和队列的绑定呢?请继续。

(4)绑定交换机和队列

在amq.direct交换机里面可以看到:目前该交换机并没有绑定任何队列。

因此,我们可以在这里选择first_queue队列,并与之绑定。在Bingdings里面填写队列名称之后,即可绑定。

 再回到队列里面查看,同样也可以看到队列已经绑定。

(5)发送消息验证

前面绑定了队列,这里我们再次发送消息,可以看到:消息已经发送成功!

我们看下交换机的信息,可以看到有个消息已经发送。 

 

 我们再去队列里面看下,同样可以在Overview里面看到有一个消息,在 get message里面也可以看到消息。

 

三、Java操作RabbitMQ

1、基本准备

(1)引入依赖包

创建Maven工程,引入下面的依赖:

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

(2)创建工具类

package com.yht.RabbitMQDemo.simple.utils;

import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class RabbitMQUtils {
    //队列名称
    public    static final String QUEUE_NAME = "learn_queue";
    public static Connection getConnection() throws Exception {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //主机地址  如果是本机就是localhost   如果在其他 地方比如:虚拟机中 那么就是ip地址
        connectionFactory.setHost("localhost");
        //连接端口;默认为 5672
        connectionFactory.setPort(5672);
        //虚拟主机名称   就是和你用户绑定的虚拟机  在创建用户时候就指定了
        connectionFactory.setVirtualHost("/virtualHost1");
        //连接用户名
        connectionFactory.setUsername("guest");
        //连接密码
        connectionFactory.setPassword("guest");
        //创建连接
        return connectionFactory.newConnection();
    }

}

2、生产者生产消息

创建生产者Producer

package com.yht.RabbitMQDemo.simple;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yht.RabbitMQDemo.simple.utils.RabbitMQUtils;


public class Producer {
    public static void main(String[] args) throws Exception {
        //创建连接
        Connection connection = RabbitMQUtils.getConnection();
        // 创建频道
        Channel channel = connection.createChannel();
        // 声明(创建)队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(RabbitMQUtils.QUEUE_NAME, true, false, false, null);

        // 要发送的信息
        String message = "Hello RabbitMQ!";
        /**
         * 参数1:交换机名称,如果没有指定则使用默认Default Exchage
         * 参数2:路由key,简单模式可以传递队列名称
         * 参数3:消息其它属性
         * 参数4:消息内容
         */
        channel.basicPublish("", RabbitMQUtils.QUEUE_NAME, null, message.getBytes());
        System.out.println("消息:" + message + "已经发送!");
        // 关闭资源
        channel.close();
        connection.close();
    }
}

执行上述代码之后,可以看到控制台的打印信息:

同时,也可以进入RabbitMQ管理界面,即在浏览器地址栏输入: http://localhost:15672/  ,并输入用户名和密码之后,可以看到:

 

 

 

3、消费者取出消息

创建消费者Consumer

package com.yht.RabbitMQDemo.simple;

import com.rabbitmq.client.*;
import com.yht.RabbitMQDemo.simple.utils.RabbitMQUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

//消费者
public class Consumer {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitMQUtils.getConnection();

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

        // 声明(创建)队列
        /**
         * 参数1:队列名称
         * 参数2:是否定义持久化队列
         * 参数3:是否独占本次连接
         * 参数4:是否在不使用的时候自动删除队列
         * 参数5:队列其它参数
         */
        channel.queueDeclare(RabbitMQUtils.QUEUE_NAME, true, false, false, null);

        //创建消费者;并设置消息处理
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            /**
             * consumerTag 消息者标签,在channel.basicConsume时候可以指定
             * envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
             * properties 属性信息
             * body 消息
             */
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //路由key
                System.out.println("路由key为:" + envelope.getRoutingKey());
                //交换机
                System.out.println("交换机为:" + envelope.getExchange());
                //消息id
                System.out.println("消息id为:" + envelope.getDeliveryTag());
                //收到的消息
                System.out.println("接收到的消息为:" + new String(body, StandardCharsets.UTF_8));
            }
        };
        //监听消息
        /**
         * 参数1:队列名称
         * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动确认
         * 参数3:消息接收到后回调
         */
        channel.basicConsume(RabbitMQUtils.QUEUE_NAME, true, consumer);

        //不关闭资源,应该一直监听消息
        //channel.close();
        //connection.close();
    }
}

这里可以执行下消费者,控制台输出如下:

我们再登进网页端可以去看下队列里面的消息,提示队列为空,这是因为在basicConsume()方法中,我们将第二个参数设置为true,代表当取出这个消息之后,就会将其删除;如果取出消息后不想删除,则可以将其置为false。

四、SpringBoot整合RabbitMQ

1、整合过程

(1)依赖包

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

(2)编写yml文件

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /virtualHost1

(3)编写配置类


import org.springframework.amqp.core.*;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {
    //定义交换机Bean
    @Bean("directExchange")
    public Exchange exchange(){
        return ExchangeBuilder.directExchange("amq.direct").build();
    }

    //定义消息队列
    @Bean("learnQueue")
    public Queue queue(){
        return QueueBuilder.nonDurable("learn_queue").build();
    }

    @Bean("binding")
    public Binding binding(@Qualifier("directExchange")Exchange exchange, @Qualifier("learnQueue")Queue queue){
        return BindingBuilder.bind(queue).to(exchange).with("my-key").noargs();
    }
}

(4)生产者生产消息

在测试类里编写测试方法

    @Test
	void producer() {
		//最后一个是消息
		rabbitTemplate.convertAndSend("amq.direct", "my-key", "Hello SpringBoot");
	}

执行完成之后,可以在learn_queue里面看到在该队列里面已经有一个消息了。 

(5)消费者消费消息

创建监听器


import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class MQListener {

    @RabbitListener(queues = "learn_queue")
    public void getMessage(Message message){
        System.out.println( "消费者取出消息:" + new String(message.getBody()));
    }
}

启动SpringBoot,就可以看到消费者取出了消息。 

五、RabbitMQ案例

1、Work Queues模型

前面在SpringBoot里面整合RabbitMQ的案例是比较简单的,只有一个消费者和一个生产者,这里我们介绍一个新的模型,实现一个队列绑定多个消费者。如下图:

这里我们先来模拟一个场景,即:生产者生产50个消息,由两个消费者来进行消费。因此,我们对上面的代码做一个改变。

(1)创建一个队列:work.queue

 

(2)生产者生产50个消息到work.queue

    @Test
	void producer() {
		for (int i = 1; i <= 50; i++) {
			String msg = "Hello SpringBoot: + 【" + i + "】";
			rabbitTemplate.convertAndSend("amq.direct", "my-key", msg);
		}
		System.out.println("消息已经发送");
	}

 (3)消费者取出消息

在监听器里面创建两个消费者,都监听work.queue队列。

@Component
public class MQListener {

    @RabbitListener(queues = "learn_queue")
    public void getMessage1(Message message){
        System.out.println( "【消费者1】取出消息:" + new String(message.getBody()));
    }

    @RabbitListener(queues = "learn_queue")
    public void getMessage2(Message message){
        System.err.println( "【消费者2】取出消息:" + new String(message.getBody()));
    }
}

这里我们来看下执行的结果:

从上面的图我们可以看到几个关键点:

  • 两个消费者是以轮询的方式来处理消息的,即生产者的消息平均分给了两个消费者
  • 每个消息只会被消费者消费一次

这里就带来了一个问题:如果两个消费者的能力不一样,那么这种轮询的方式就无法很好的利用消费者的性能。我们更希望的是如果消费者1的性能比较好,处理的比较快,那么完全可以让消费者1多处理一些消息,这样更符合我们的实际场景。

综上所述,在默认情况下,RabbitMQ会将消息依次轮询的绑定到队列的每一个消费者,这种情况下因为消费者的性能不同会导致出现消息堆积的情况。因此,我们就需要修改配置文件,设置preFetch值为1,确保同一时刻最多给消费者一条消息,即:每个消费者只有在处理完当前消息之后,才能获取下一个消息。如下:

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /virtualHost1
    listener:
      simple:
        prefetch: 1

这里修改下消费者的代码,模拟两个消费者的能力不同。

@Component
public class MQListener {

    @RabbitListener(queues = "learn_queue")
    public void getMessage1(Message message) throws InterruptedException {
        Thread.sleep(20);
        System.out.println( "【消费者1】取出消息:" + new String(message.getBody()));
    }

    @RabbitListener(queues = "learn_queue")
    public void getMessage2(Message message) throws InterruptedException {
        Thread.sleep(200);
        System.err.println( "【消费者2】取出消息:" + new String(message.getBody()));
    }
}

执行结果如下:

2、Fanout交换机

Fanout交换机会将接收到的消息光波导每一个跟其绑定的queue,因此也称为广播模式,如下:

(1) 在RabbitMQ控制台创建两个队列,并绑定到fanout交换机。

 

(2)生产者生产消息

    @Test
	void fanout() {
		rabbitTemplate.convertAndSend("amq.fanout", "fanout", "Hello Everyone");

	}

(3)消费者监听消息

     @RabbitListener(queues = "fanout.queue1")
    public void fanout1(Message message) throws InterruptedException {
        System.out.println( "fanout.queue1的消息:" + new String(message.getBody()));
    }
    @RabbitListener(queues = "fanout.queue2")
    public void fanout(Message message) throws InterruptedException {
        System.err.println( "fanout.queue2的消息:" + new String(message.getBody()));
    }

执行结果如下: 

3、Direct交换机

该交换机会将接收到的消息根据规则路由到指定的Queue,因此又被称为定向路由。发布者在发送消息时,会指定消息的RoutingKey,而每个Queue都与Exchange会设置一个BindingKey,之后交换机会将消息路由到BindingKey与消息队列RoutingKey一致的队列。 如下图:

(1) 在RabbitMQ控制台创建两个队列,并绑定到fanout交换机。

 

(2)创建消费者

    @RabbitListener(queues = "direct.queue1")
    public void direct1(Message message){
        System.out.println( "direct.queue1的消息:" + new String(message.getBody()));
    }
    @RabbitListener(queues = "direct.queue2")
    public void direct2(Message message) {
        System.err.println( "direct.queue2的消息:" + new String(message.getBody()));
    }

(3)生产者生产消息

	@Test
	void direct() {
		rabbitTemplate.convertAndSend("amq.direct", "red", "明天暴雨");
	}

	@Test
	void direct() {
		rabbitTemplate.convertAndSend("amq.direct", "blue", "明天微风,适合游玩");
	}

 

 

4、Topic交换机

该交换机与Direct Exchange类似,区别在于routingKey可以时多个单词的列表,并且以.分割。也就是说,Topic交换机接收的消息的RoutingKey可以是多个单词,单词之间以.分割。

Topic交换机的通配符有两种形式:

  • *(星号):表示匹配一个单词。
  • #(井号):表示匹配零个或多个单词。

 (1)创建两个队列

(2)修改消费者

    @RabbitListener(queues = "topic.queue1")
    public void topic1(Message message) {
        System.out.println( "topic.queue1的消息:" + new String(message.getBody()));
    }
    @RabbitListener(queues = "topic.queue2")
    public void topic2(Message message){
        System.err.println( "topic.queue2的消息:" + new String(message.getBody()));
    }

(3)创建生产者

 第一种:两个队列的routingkey都匹配成功

	@Test
	void Topic() {
		rabbitTemplate.convertAndSend("amq.topic", "zhejiang.weather", "今天天气挺好");
	}

  第二种:只有一个队列的routingkey匹配成功

	@Test
	void Topic() {
		rabbitTemplate.convertAndSend("amq.topic", "zhejiang.news", "杭州发优惠券了");
	}

5、声明队列和交换机

(1)方式一:代码方式

SpringAMQP提供了几个类,用来声明队列、交换机及其绑定关系:

  • Queue:用于声明队列,可以用工厂类QueueBuilder创建
  • Exchange:用于声明交换机,可以用工厂类ExchangeBuilder构建
  • Binding:用于声明队列和交换机的绑定关系,可以用工厂类BindingBuilder构建


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DirectListener {
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("amq.direct");
    }

    @Bean
    public Queue directQueue(){
        return new Queue("direct.queue1");
    }

    @Bean
    public Binding directBind(Queue directQueue, DirectExchange directExchange){
        return BindingBuilder.bind(directQueue).to(directExchange).withQueueName();
    }
}

(2)方式二:注解方式

Spring AMQP还提供了基于@RabbitListener注解来声明队列和交换机的方式。

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1", durable = "true"),
            exchange = @Exchange(name = "amq.direct", type = ExchangeTypes.DIRECT),
            key = {"red"}
    ))
    public void directByAnno(Message message){
        System.out.println( "direct.queue1的消息:" + new String(message.getBody()));
    }

6、消息转换器

如果我们需要利用Spring AMQP发送对象类型的消息,需要基于JDK的ObejctOutputStream完成序列化。否则的话,拿到的消息就是有问题的,如下图:

(1)引入依赖

        <dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>

(2)配置消息转换器MessageConverter

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

(3)发送消息

    @Test
	void fanout() {
		Map<String, Object> map = new HashMap<>();
		map.put("name", "tom");
		map.put("age", "23");
		rabbitTemplate.convertAndSend("amq.fanout", "fanout", map);

	}

 执行之后,可以去获取消息,如下:


 

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

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

相关文章

12.11

1.q&#xff0c;w&#xff0c;e亮led1&#xff0c;2&#xff0c;3&#xff1b; a&#xff0c;s&#xff0c;d灭led1&#xff0c;2&#xff0c;3&#xff1b; main.c #include "uar1.h"#include "led.h"void delay(int ms){int i,j;for(i0;i<ms;i){for…

红队攻防实战之Redis-RCE集锦

心若有所向往&#xff0c;何惧道阻且长 Redis写入SSH公钥实现RCE 之前进行端口扫描时发现该机器开着6379&#xff0c;尝试Redis弱口令或未授权访问 尝试进行连接Redis&#xff0c;连接成功&#xff0c;存在未授权访问 尝试写入SSH公钥 设置redis的备份路径 设置保存文件名 …

透析跳跃游戏

关卡名 理解与贪心有关的高频问题 我会了✔️ 内容 1.理解跳跃游戏问题如何判断是否能到达终点 ✔️ 2.如果能到终点&#xff0c;如何确定最少跳跃次数 ✔️ 1. 跳跃游戏 leetCode 55 给定一个非负整数数组&#xff0c;你最初位于数组的第一个位置。数组中的每个元素代表…

[MySQL]SQL优化之索引的使用规则

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、索引失效 &#x1f4d5;最左前缀法则 &#x1f4d5;范围查询> &#x1f4d5;索引列运算&#xff0c;索引失效 &#x1f4d5;前模糊匹配 &#x1f4d5;or连接的条件 &#x1f4d5;字符串类型不加 …

C语言中的一维数组与二维数组

目录 一维数组数组的创建初始化使用在内存中的存储 二维数组创建初始化使用在内存中的存储 数组越界 一维数组 数组的创建 数组是一组相同类型元素的集合。 int arr1[10]; char arr3[10]; float arr4[10]; double arr5[10];下面这个数组能否成功创建&#xff1f; int count…

计算机丢失msvcp140dll怎么恢复?快速解决dll缺失问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140dll丢失”。msvcp140.dll是一个动态链接库文件&#xff0c;它包含了许多C标准库函数的实现。这些动态链接库文件是程序运行所必需的&#xff0c;它们包含了许多函数和资源&#xf…

spring结合设计模式之策略模式

策略模式基本概念&#xff1a; 一个接口或者抽象类&#xff0c;里面两个方法&#xff08;一个方法匹配类型&#xff0c;一个可替换的逻辑实现方法&#xff09;不同策略的差异化实现(就是说&#xff0c;不同策略的实现类) 使用策略模式替换判断&#xff0c;使代码更加优雅。 …

Guava反射工具详解

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊Java反射&#xff0c;特别是在Guava这个强大的库中&#xff0c;它是怎么让反射变得更简单&#xff0c;更有趣的。咱们都知道&#xff0c;反射在Java中是个相当强大的特性&#xff0c;它允许程序在运…

AcWing 95. 费解的开关(递推)

题目链接 活动 - AcWing 本活动组织刷《算法竞赛进阶指南》&#xff0c;系统学习各种编程算法。主要面向有一定编程基础的同学。https://www.acwing.com/problem/content/97/ 题解 只要第一行开关的状态确定&#xff0c;则所有开关的状态都可以被推出来。第一行开关总共有种操…

从零开始学R语言?这个网站帮你快速入门,成为数据分析高手!

介绍&#xff1a;R语言&#xff0c;全称The R Programming Language&#xff0c;是一种属于GNU系统的自由、免费、源代码开放的软件。它主要被用于统计计算和统计制图&#xff0c;因此&#xff0c;它是统计分析和数据可视化的优秀工具。 R语言的特点丰富多样。首先&#xff0c;…

入职字节外包一个月,我离职了。。。

有一种打工人的羡慕&#xff0c;叫做“大厂”。 真是年少不知大厂香&#xff0c;错把青春插稻秧。 但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂…

AWS攻略——使用Public NAT解决私有子网实例访问互联网

文章目录 创建NAT网关编辑Private子网路由测试知识点参考资料 在《AWS攻略——子网》一文中&#xff0c;我们分别创建了一个Public子网和一个Private子网&#xff0c;并让Public子网中的实例可以SSH登录到Private子网的实例中。 现实场景中&#xff0c;我们可能存在如下需求&a…

用微元思想求解三重积分——基于Matlab

仅作自己学习使用 1. 题目 求解下列三重积分&#xff0c;其中A&#xff0c;μ&#xff0c;r都是常数。 求解的准确性可以用下式进行评估&#xff1a; 听过考研数一张宇课程的朋友应该指导&#xff0c;求解三重积分就是就一个面包&#xff0c;我们将面包无限细分为一个小块&a…

Python常见面试知识总结(二):数据结构、类方法及异常处理

【十三】Python中assert的作用&#xff1f; Python中assert&#xff08;断言&#xff09;用于判断一个表达式&#xff0c;在表达式条件为 f a l s e false false的时候触发异常。 断言可以在条件不满足程序运行的情况下直接返回错误&#xff0c;而不必等待程序运行后出现崩溃…

2023最新版JavaSE教程——第10天:多线程

目录 一、相关概念1.1 程序、进程与线程1.2 查看进程和线程1.3 线程调度1.4 多线程程序的优点1.5 补充概念1.5.1 单核CPU和多核CPU1.5.2 并行与并发 二、创建和启动线程2.1 概述2.2 方式1&#xff1a;继承Thread类2.3 方式2&#xff1a;实现Runnable接口2.4 变形写法2.5 对比两…

OpenAI接口调用示例

最近为公司做了一个ChatGPT工具&#xff0c;这里展示一下OpenAI接口的调用 前提条件 访问OpenAI官网&#xff08;国内需要翻墙&#xff09;的账号&#xff0c;需要sk 地址&#xff1a;https://platform.openai.com 依赖 使用开源工具调用OpenAI接口&#xff0c;依赖如下&am…

使用yum/dnf管理软件包

本章主要介绍使用 yum 对软件包进行管理。 yum 的介绍搭建yum源创建私有仓库yum客户端的配置yum的基本使用使用第三方yum源 使用rpm安装包时经常会遇到一个问题就是包依赖&#xff0c;如下所示。 [rootrhel03 ~]# rpm -ivh /mnt/AppStream/Packages/httpd-2.4.37-41.modulee…

mysql原理--B+树索引

1.没有索引的查找 1.1.在一个页中的查找 (1). 以主键为搜索条件 可以在 页目录 中使用二分法快速定位到对应的槽&#xff0c;然后再遍历该槽对应分组中的记录即可快速找到指定的记录。 (2). 以其他列作为搜索条件 这种情况下只能从 最小记录 开始依次遍历单链表中的每条记录&am…

Python爬虫实战之爬取京东商品数据并实实现数据可视化

文章目录 一、开发工具二、环境搭建三、原理简介四、数据可视化关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 …