微服务—RabbitMQ

目录

初识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();
}

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

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

相关文章

vscode 如何修改c/c++格式化风格,大括号不换行

在Visual Studio Code&#xff08;VSCode&#xff09;中&#xff0c;若要修改C代码格式化的风格以实现大括号不换行&#xff0c;通常会借助于插件C/C扩展中的ClangFormat配置。以下是具体的步骤&#xff1a; 确保已安装了C/C扩展&#xff1a; 打开VSCode的扩展市场&#xff08;…

力扣热门100题刷题笔记 - 1.两数之和

力扣热门100题 - 1.两数之和 题目链接&#xff1a;1.两数之和 题目描述&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个答…

༺༽༾ཊ—Unity之-04-原型模式—ཏ༿༼༻

首先创建一个项目&#xff0c; 在这个初始界面我们需要做一些准备工作&#xff0c; 建基础通用文件夹&#xff0c; 创建一个Plane 重置后 缩放100倍 加一个颜色&#xff0c; 任务1&#xff1a;使用 建造者模式 创建三种 金刚猿猴 零部件 拼接组合 首先资源商店下载 金刚猿猴 模…

上位机图像处理和嵌入式模块部署(二进制图像的读写)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前我们说过&#xff0c;对于图像处理而言&#xff0c;势必会涉及到文件的读写。但是不同格式文件的读写&#xff0c;这本身又是体力活&#xff0…

vue yarn certificate has expired

背景&#xff1a;我在用ant design pro框架进行初始化时&#xff0c;安装脚手架时&#xff0c;安装yarn时显示报错 原因分析&#xff1a;查了很久的资料&#xff0c;这种情况应该是开了服务器代理访问导致ssl安全证书失效了 解决办法&#xff1a; 在终端输入&#xff1a;yarn…

【2024.2.1练习】01迷宫

题目描述 题目思路 一开始想用动态规划&#xff0c;但是这道题没有明显的顺序性和递推关系可以找&#xff0c;起点也是自定义的&#xff0c;鉴于迷宫尺寸不大&#xff0c;考虑使用DFS或BFS。 在自己推算几遍后发现&#xff0c;假设可以在地图上畅通无阻&#xff0c;那么格子只…

书签管理和稍后阅读工具Readeck

什么是 Readeck ? Readeck 是一个简单的 Web 应用程序&#xff0c;可让您保存您喜欢并希望永久保留的网页的宝贵可读内容。将其视为书签管理器和稍后阅读工具。 网络上的内容每天都会消失。很可能您几个月前偶然发现的一篇文章或图片现在已经消失了。当您在 Readeck 中保存某些…

运维SRE-02 正则表达式、grep

1.特殊符号补充 1.1位置相关的特殊符号 . 当前目录 .. 当前目录的上级目录 ~ 当前用户家目录 / 根目录 cd - 返回上次所在目录1.2熟练掌握 # 注释符号,root命令提示符 | 管道符号.1.3了解其他特殊符号 $ 取值(取出变量的值),普通用户的提示符 ! % ^ & * (){} [] ; ? \…

Quick BI中lod函数之lod_fixed

一、lod函数简介 LOD函数的全称是详细级别表达式&#xff08;Level Of Detail Expressisons&#xff09;。它主要是为了克服一些表达式之间计算颗粒度不一致的问题。比如&#xff0c;要计算第一季度各月销售收入占比&#xff0c;这里分子计算颗粒度为’月’&#xff0c;但是分…

2024年美国大学生数学建模D题思路分析 - 大湖区水资源问题

# 1 赛题 问题D&#xff1a;大湖区水资源问题 背景 美国和加拿大的五大湖是世界上最大的淡水湖群。这五个湖泊和连接的水道构成了一个巨大的流域&#xff0c;其中包含了这两个国家的许多大城市地区&#xff0c;气候和局部天气条件不同。 这些湖泊的水被用于许多用途&#xff…

TypeScript 学习笔记(Day4)

「写在前面」 本文为 b 站黑马程序员 TypeScript 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. TypeScript 学习笔记&#xff08;Day1&#xff09; 2. TypeScript 学习笔…

大力说视频号第三课:手把手教你视频号如何认证

视频号生态不断完善&#xff0c;越来越多的创作者认识到视频号认证的重要性。微信视频号认证不但能提升搜索排名&#xff0c;还能直播推流、与企业微信的关联等优势。 今天大力就来向大家介绍一下视频号如何做认证。 01 个人认证 个人认证又包括兴趣认证和职业认证。 A、兴趣…

基于Java SSM框架实现校园快领服务系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现校园快领服务系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于校园快领服务系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了…

保姆级的指针详解(超详细)

目录 一.内存和地址  1.初识指针 2.如何理解编址 二. 指针变量 三.指针的解引用操作符 1.指针变量的大小 四.指针变量类型的意义 五.指针的运算 1.指针加减整数 2.指针减指针 3.野指针 3.1指针未初始化 3.2指针越界访问 3.3指针指向的空间被提前释放 3.4如何规…

Rust学习之Features

Rust学习之Features 一 什么是 Features二 默认 feature三 简单的features应用示例四 可选(optional)的依赖五 依赖的特性5.1 在依赖表中指定5.2 在features表中指定 六 命令行中特性控制七 特性统一路径八 其它8.1 相互排斥特性8.2 观察启用特性8.3 [Feature resolver version…

从源码角度透视QTcpServer:解构QTcpServer的底层原理与技术细节

深入了解QTcpServer的底层原理和技术细节 一、背景二、QTcpServer的基本原理2.1、TCP协议简介2.2、QTcpServer的概念 三、QTcpServer源码解析3.1、QTcpServer的构造函数3.2、调用listen函数启动tcpserver3.3、QSocketNotifier的实现 总结 一、背景 QTcpServer是Qt网络模块中的…

花瓣网美女图片爬取

爬虫基础案例01 花瓣网美女图片 网站url&#xff1a;https://huaban.com 图片爬取 import requests import json import os res requests.get(url "https://api.huaban.com/search/file?text%E7%BE%8E%E5%A5%B3&sortall&limit40&page1&positionsear…

『C++成长记』string使用指南

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;C &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、string类介绍 二、string类的常用接口说明 &#x1f4d2;2.1string类对象的常…

数据结构+算法(第10篇):叉堆“功夫熊猫”的速成之路

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

Elasticsearch:集群故障排除和优化综合指南

Elasticsearch 是一个强大的搜索和分析引擎&#xff0c;是许多数据驱动应用程序和服务的核心。 它实时处理、分析和存储大量数据的能力使其成为当今快节奏的数字世界中不可或缺的工具。 然而&#xff0c;与任何复杂的系统一样&#xff0c;Elasticsearch 可能会遇到影响其性能和…