MQ消息队列八股系列(2)

MQ消息队列八股系列(2)

整理后的内容如下:


解决消息队列的延时问题

1.1 增加消费者实例

  • 方法:增加消费者实例可以提高消息处理的并发度,从而减少消息的等待时间。

1.2 优化消费者处理逻辑

  • 方法:优化消费者的处理逻辑,减少每条消息的处理时间。例如,通过批量处理、异步处理、减少不必要的计算和 I/O 操作等方式来提高处理效率。

1.3 调整消息队列的分区(Partition)

  • 方法:增加消息队列的分区数,使更多的消费者能够并行处理消息,从而提高处理速度。

1.4 使用更高性能的硬件

  • 方法:使用更高性能的硬件(如更快的 CPU、更大的内存、更高性能的存储设备)来提高消息处理的性能。

1.5 调整消息队列的配置

  • 方法:调整消息队列的配置参数,如增加内存缓冲区大小、优化网络配置等,以减少消息传输和处理的延时。

解决消息过期失效问题

2.1 设置合理的消息过期时间

  • 方法:根据业务需求设置合理的消息过期时间,确保消息在有效期内被处理。

2.2 定期清理过期消息

  • 方法:定期清理过期消息,避免过期消息占用队列资源。

2.3 使用死信队列(DLQ)

  • 方法:将处理失败或过期的消息转移到死信队列,进行后续处理或人工干预。

整理后的内容如下:


解决消息队列满的问题

1.1 增加队列容量

  • 方法:增加消息队列的容量,如增加存储空间、增加内存缓冲区大小等,以容纳更多的消息。

1.2 增加消费者实例

  • 方法:增加消费者实例,提高消息的处理速度,减少消息在队列中的积压。

1.3 实施流量控制

  • 方法:在生产者端实施流量控制,限制消息的生产速率,避免消息队列过载。

1.4 分布式消息队列

  • 方法:使用分布式消息队列,将消息分散到多个节点上,避免单个节点的队列满。

整理后的内容如下:


解决几百万消息持续积压几小时的问题

1.1 增加消费者实例

  • 方法:增加消费者实例,提高消息的处理并发度,尽快处理积压的消息。

1.2 优化消费者处理逻辑

  • 方法:优化消费者的处理逻辑,提高处理效率,减少每条消息的处理时间。

1.3 批量处理

  • 方法:采用批量处理的方式,一次处理多条消息,减少处理开销。

1.4 限流和降级

  • 方法:在生产者端进行限流,控制消息的产生速率。同时,根据业务需求,实施降级策略,优先处理高优先级的消息。

1.5 临时扩容

  • 方法:在消息积压严重时,可以临时扩容消息队列的存储容量和消费者实例数量,尽快处理积压的消息。

1.6 使用更高性能的硬件

  • 方法:在积压严重时,可以考虑临时使用更高性能的硬件,提高消息处理的性能。

1.7 分布式处理

  • 方法:将消息队列和消费者分布到多个节点上,进行分布式处理,分散压力。

整理后的RabbitMQ相关内容如下:


RabbitMQ 基础概念

1. Producer(生产者)

  • 定义:生产者是负责将消息发送到 RabbitMQ 的应用程序。消息首先发送到交换机(Exchange),而不是直接发送到队列。

2. Consumer(消费者)

  • 定义:消费者是从 RabbitMQ 队列中接收并处理消息的应用程序。消费者从指定的队列中拉取消息进行处理。

3. Message(消息)

  • 定义:消息是生产者发送、消费者接收的基本数据单元,通常包含有效载荷(payload)和元数据(如路由键、头部信息等)。

4. Queue(队列)

  • 定义:队列是 RabbitMQ 中用于存储消息的缓冲区。消息在队列中等待被消费者处理。队列支持消息持久化、优先级等特性。

5. Exchange(交换机)

  • 定义:交换机是 RabbitMQ 的消息路由组件,生产者将消息发送到交换机,由交换机根据路由规则将消息分发到一个或多个队列。
  • 类型
  • Direct Exchange:根据路由键精确匹配队列。
  • Fanout Exchange:将消息广播到所有绑定到该交换机的队列。
  • Topic Exchange:根据路由键模式匹配队列(支持通配符)。
  • Headers Exchange:根据消息头部属性匹配队列。

6. Binding(绑定)

  • 定义:绑定是交换机与队列之间的连接关系。通过绑定,交换机可以将消息路由到一个或多个队列,每个绑定可以有一个路由键,用于匹配消息的路由键。

7. Routing Key(路由键)

  • 定义:路由键是生产者发送消息时指定的字符串,用于交换机将消息路由到对应的队列。不同类型的交换机对路由键的处理方式不同。

8. Virtual Host(虚拟主机)

  • 定义:虚拟主机是 RabbitMQ 中的逻辑隔离机制,用于隔离不同的应用程序或租户。每个虚拟主机有自己的队列、交换机、绑定等资源,提供多租户环境下的资源隔离。

9. Connection(连接)

  • 定义:连接是生产者或消费者与 RabbitMQ 服务器之间的 TCP 连接,一个连接可以包含多个信道(Channel)。

10. Channel(信道)

  • 定义:信道是建立在连接之上的虚拟连接,生产者和消费者通过信道与 RabbitMQ 服务器通信。信道提供了一种高效的方式来复用 TCP 连接,减少连接开销。

11. Acknowledgement(确认)

  • 定义:确认是消费者成功处理消息后发送给 RabbitMQ 的信号,表示消息已成功处理。RabbitMQ 收到确认后会将消息从队列中移除,如果未收到确认,RabbitMQ 可以重新投递消息。

12. Durability(持久化)

  • 定义:持久化是指将队列和消息保存到磁盘,以便在 RabbitMQ 重启后仍然存在。持久化队列和消息可以提高系统的可靠性。

13. Prefetch Count(预取计数)

  • 定义:预取计数是消费者一次性从队列中获取的最大消息数。通过设置预取计数,可以控制消息流量,防止消费者因处理大量消息而过载。

14. Dead Letter Exchange(死信交换机)

  • 定义:死信交换机用于处理无法被正常消费的消息。当消息被拒绝(nack)或超过最大重试次数时,可以将消息路由到死信交换机进行进一步处理。

15. Cluster(集群)

  • 定义:RabbitMQ 支持集群模式,可以将多个 RabbitMQ 节点组成一个集群,提高系统的可用性和扩展性。集群中的节点共享队列、交换机等资源。

16. Federation(联邦)和 Shovel

  • 定义:联邦和 Shovel 是 RabbitMQ 提供的跨数据中心或网络的消息传输机制。联邦允许不同 RabbitMQ 服务器之间共享消息,而 Shovel 是一个插件,用于在不同的 RabbitMQ 实例之间转移消息。

这个整理可以帮助你快速理解RabbitMQ的主要概念和功能。

广播消息在RabbitMQ中主要通过不同类型的交换机(Exchange)来实现,以下是三种主要的实现机制:

1. Fanout Exchange(扇出交换机)

Fanout Exchange 是最常见的广播类型,它将接收到的每条消息广播到所有绑定到该交换机的队列,而不考虑路由键。适用于需要将消息分发给多个消费者的场景。

特点

  • 不考虑路由键,直接广播消息。
  • 适用于发布/订阅模式。

示例代码

import pika

# 连接到 RabbitMQ 服务器
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 声明一个扇出交换机
channel.exchange_declare(exchange='logs', exchange_type='fanout')

# 发送消息到交换机
message = "info: Hello World!"
channel.basic_publish(exchange='logs', routing_key='', body=message)

print(f"[x] Sent {message}")
connection.close()

2. Topic Exchange(主题交换机)

Topic Exchange 可以通过路由键模式匹配来广播消息。通过使用通配符(如 *#),可以将消息广播到匹配特定模式的多个队列。

特点

  • 基于路由键模式匹配。
  • 支持通配符 *(匹配一个单词)和 #(匹配零个或多个单词)。
  • 适用于复杂的路由规则和多种订阅场景。

示例代码

import pika

# 连接到 RabbitMQ 服务器
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 声明一个主题交换机
channel.exchange_declare(exchange='topic_logs', exchange_type='topic')

# 发送消息到交换机
routing_key = 'kern.critical'
message = "A critical kernel error!"
channel.basic_publish(exchange='topic_logs', routing_key=routing_key, body=message)

print(f"[x] Sent {routing_key}: {message}")
connection.close()

3. Headers Exchange(头部交换机)

Headers Exchange 是基于消息头部属性进行路由的。虽然它不是一种典型的广播机制,但通过设置特定的头部属性,可以将消息路由到多个队列。它更适用于需要基于消息元数据进行路由的场景。

特点

  • 基于消息头部属性进行路由。
  • 可实现复杂的路由规则。

示例代码

import pika

# 连接到 RabbitMQ 服务器
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 声明一个头部交换机
channel.exchange_declare(exchange='headers_logs', exchange_type='headers')

# 发送消息到交换机
headers = {'type': 'report', 'format': 'pdf'}
message = "Document in PDF format"
channel.basic_publish(
    exchange='headers_logs',
    routing_key='',
    body=message,
    properties=pika.BasicProperties(headers=headers)
)

print(f"[x] Sent message with headers {headers}: {message}")
connection.close()

总结

  • Fanout Exchange 是最直接的广播类型,不考虑路由键,直接将消息广播到所有绑定的队列。
  • Topic Exchange 通过路由键模式匹配实现广播,适用于需要复杂路由规则的场景。
  • Headers Exchange 基于消息头部属性进行路由,也可以实现广播,但更适用于基于消息元数据的路由需求。

这三种交换机各有特点,可以根据具体业务需求选择合适的类型来实现消息广播。

RabbitMQ 的消息路由主要依赖于交换器(Exchange),通过路由键和绑定关系将消息从生产者发送到适当的队列。以下是不同类型的交换器及其路由规则:

1. Direct Exchange(直连交换器)

Direct Exchange 根据消息的路由键(Routing Key)将消息路由到与该路由键完全匹配的队列。

特点

  • 绑定队列时使用一个绑定键。
  • 如果队列的绑定键与消息的路由键完全匹配,则消息会被发送到该队列。

应用场景

  • 适用于精确匹配的路由需求,例如日志系统中不同的日志级别(info、error、warning)被发送到不同的队列。

2. Fanout Exchange(扇形交换器)

Fanout Exchange 会将消息广播到所有与该交换器绑定的队列中,不考虑路由键。

特点

  • 忽略路由键,直接将消息发送到所有绑定的队列。
  • 适合需要将消息分发给多个消费者的场景。

应用场景

  • 适用于发布/订阅模式,例如广播通知、多用户实时更新等。

3. Topic Exchange(主题交换器)

Topic Exchange 通过路由键模式匹配将消息路由到相应的队列。支持使用通配符来实现灵活的路由规则。

特点

  • 路由键可以包含通配符 *(匹配一个单词)和 #(匹配零个或多个单词)。
  • 可以将消息路由到匹配特定模式的多个队列。

应用场景

  • 适用于复杂的路由需求,例如根据消息的类别或来源进行分类和处理。

4. Headers Exchange(头交换器)

Headers Exchange 根据消息头中的键值对来决定消息的路由,而不是依赖于路由键。

特点

  • 消息的路由完全基于消息头中的属性,而非路由键。
  • 相对复杂,通常在生产环境中使用较少。

应用场景

  • 适用于需要基于消息元数据(如消息类型、格式等)进行路由的场景。

路由过程

  1. 生产者发送消息:生产者将消息发送到指定的交换器,并可能附带一个路由键(对于直连交换器和主题交换器)。对于扇形交换器,则忽略路由键。
  2. 交换器路由消息:交换器根据绑定的规则(例如路由键或消息头)将消息路由到一个或多个队列。
  3. 消费者接收消息:消费者从队列中接收消息并进行处理。消费者可以订阅一个或多个队列,从而处理相应的消息。

总结

  • Direct Exchange 用于精确路由。
  • Fanout Exchange 用于广播消息。
  • Topic Exchange 用于模式匹配路由。
  • Headers Exchange 用于基于消息头的路由。

RabbitMQ 基于 AMQP(高级消息队列协议)进行消息的传输和交互,AMQP 是一种应用层协议的开发标准,为面向消息的中间件设计。RabbitMQ 选择 AMQP 作为通信协议,主要有以下几个原因:

1. 可靠性

  • 消息确认:AMQP 协议支持消息确认机制,确保消息在传输过程中被成功接收并处理。
  • 持久化:消息可以持久化到磁盘,确保即使在系统崩溃后消息也不会丢失。
  • 事务支持:AMQP 支持消息的事务处理,保证消息的一致性和可靠性。

2. 灵活性

  • 丰富的功能:AMQP 协议提供了队列、交换机、路由、绑定等功能,支持复杂的消息路由和分发策略。
  • 多种交换机类型:如直连交换机、主题交换机、扇出交换机等,适应不同的消息路由需求。

3. 跨平台性

  • 多语言支持:AMQP 提供了多语言的 API,使得 RabbitMQ 能够与多种编程语言编写的应用程序进行集成。
  • 广泛的兼容性:RabbitMQ 支持各种操作系统和开发环境,方便在不同平台上进行开发和部署。

RabbitMQ 的核心组成部分

  • Server(Broker):接受客户端连接,实现 AMQP 实体服务。
  • Connection:客户端与 RabbitMQ 服务器之间的 TCP 连接。
  • Channel:在连接上建立的虚拟连接,允许并发处理多个消息。
  • Message:由生产者发送、消费者接收的基本数据单元,包含有效载荷和元数据。
  • Exchange:路由消息到队列的组件,根据绑定规则将消息分发到一个或多个队列。
  • Virtual Host:逻辑隔离机制,用于隔离不同的应用程序或租户。
  • Bindings:交换机与队列之间的绑定关系,定义了消息的路由规则。
  • Routing Key:消息发送时指定的字符串,用于交换机将消息路由到绑定的队列。
  • Queue:存储消息的缓冲区,消费者从队列中获取并处理消息。

RabbitMQ 的工作流程

  1. 生产者发送消息:生产者将消息发送到指定的交换机,并附带路由键(对于直连交换机和主题交换机)。
  2. 交换机路由消息:交换机根据路由键和绑定规则将消息路由到一个或多个队列。
  3. 消费者处理消息:消费者从队列中获取消息并进行处理,消费者可以订阅一个或多个队列。

总结

RabbitMQ 使用 AMQP 协议提供了可靠、灵活和可扩展的消息传递机制。其核心组成部分和多种交换机类型使其在分布式系统、微服务架构和任务队列等场景中得到广泛应用。

在 RabbitMQ 中,确保消息可靠传递和处理是至关重要的。以下是一些主要的机制和实践,用于确保消息的持久性、确认、事务处理以及高可用性:

1. 消息持久化

  • 持久化队列
    创建时声明队列为持久化队列(durable)。持久化队列在 RabbitMQ 重启后依然存在。
  channel.queueDeclare("queue_name", true, false, false, null);
  • 持久化消息
    在发送消息时,将消息标记为持久化(persistent)。持久化消息会被写入磁盘,即使 RabbitMQ 重启,消息也不会丢失。
  AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
      .deliveryMode(2) // 2 表示持久化消息
      .build();
  channel.basicPublish("exchange_name", "routing_key", props, messageBody);

2. 消息确认机制

  • 消费者确认
    消费者处理完消息后,向 RabbitMQ 发送确认(ack)。如果消费者未确认消息(如消费者崩溃),RabbitMQ 会将消息重新投递给其他消费者。
  channel.basicConsume("queue_name", false, consumer);
  // 消费者处理完消息后手动确认
  channel.basicAck(deliveryTag, false);
  • 发布者确认
    启用发布确认模式(publisher confirms),RabbitMQ 会在消息成功写入队列后发送确认给发布者。
  channel.confirmSelect();
  channel.basicPublish("exchange_name", "routing_key", null, messageBody);
  channel.waitForConfirmsOrDie(); // 等待确认

3. 事务支持

RabbitMQ 支持 AMQP 事务,可以在一个事务中发布多条消息,确保这些消息要么全部成功,要么全部失败。

channel.txSelect();
channel.basicPublish("exchange_name", "routing_key", null, messageBody);
channel.txCommit(); // 提交事务

4. 镜像队列

RabbitMQ 的集群模式支持镜像队列(也称为高可用队列),可以将队列的数据复制到多个节点上,确保在一个节点故障时,其他节点仍然可以提供服务。

# 定义队列策略,将队列配置为镜像队列
rabbitmqctl set_policy ha-all "^queue_name$" '{"ha-mode":"all"}'

5. 死信队列

RabbitMQ 支持死信队列(DLX),当消息在队列中被拒绝、过期或达到最大重试次数时,可以将消息发送到死信交换器,便于后续处理。

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");
channel.queueDeclare("queue_name", true, false, false, args);

6. 消费者重试机制

消费者可以实现重试机制,当消息处理失败时,可以将消息重新投递到队列或死信队列,避免消息丢失。

// 处理消息
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
  channel.basicNack(deliveryTag, false, true); // 重试
}

7. 消息 TTL (Time-To-Live)

消息可以设置 TTL(存活时间),超过 TTL 的消息会自动被移除或转移到死信队列,避免消息在队列中无限积压。

AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .expiration("60000") // 消息 TTL 为 60 秒
    .build();
channel.basicPublish("exchange_name", "routing_key", props, messageBody);

这些机制和实践帮助确保 RabbitMQ 的消息传递系统的可靠性和稳定性。

确保消息正确地发送和处理是消息系统中非常重要的部分。以下是确保消息正确发送至 RabbitMQ 以及确保消息正确消费的几种方法和代码示例:

确保消息正确发送至 RabbitMQ

1. 确认模式(Publisher Confirms)

在确认模式下,RabbitMQ 在消息成功到达 Broker 时会向发布者发送确认。可以通过 Channel.confirmSelect() 开启确认模式,并使用 Channel.waitForConfirms() 或异步方式 Channel.addConfirmListener() 处理确认回调。

代码示例:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");

try (Connection connection = factory.newConnection(); 
     Channel channel = connection.createChannel()) {

    channel.confirmSelect(); // 开启确认模式

    String message = "Hello, RabbitMQ!";
    channel.basicPublish("", "queue_name", null, message.getBytes());

    if (channel.waitForConfirms()) {
        System.out.println("Message sent successfully.");
    } else {
        System.out.println("Message sending failed.");
    }
} catch (IOException | TimeoutException e) {
    e.printStackTrace();
}

2. 事务模式(Transactional Mode)

事务模式下,消息只有在事务提交后才会被发送。此方法会带来额外开销,一般在需要强一致性的场景下使用。

代码示例:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");

try (Connection connection = factory.newConnection(); 
     Channel channel = connection.createChannel()) {

    channel.txSelect(); // 开启事务模式

    String message = "Hello, RabbitMQ!";
    channel.basicPublish("", "queue_name", null, message.getBytes());

    channel.txCommit(); // 提交事务
    System.out.println("Message sent successfully.");

} catch (IOException | TimeoutException e) {
    try {
        channel.txRollback(); // 事务回滚
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    e.printStackTrace();
}

3. 持久化消息

持久化消息会被写入磁盘,即使 RabbitMQ 重启,消息也不会丢失。可以通过设置消息属性为 persistent 实现消息持久化。

代码示例:

AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .deliveryMode(2) // 2 表示持久化消息
    .build();

channel.basicPublish("", "queue_name", props, message.getBytes());

确保消息接收方消费了消息

1. 消息确认(Message Acknowledgements)

消费者在成功处理消息后,需要发送一个确认(ACK)给 RabbitMQ。如果消费者未能确认消息(如崩溃),RabbitMQ 会重新将消息投递给其他消费者。

代码示例:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");

try (Connection connection = factory.newConnection(); 
     Channel channel = connection.createChannel()) {

    boolean autoAck = false; // 关闭自动确认
    channel.basicConsume("queue_name", autoAck, (consumerTag, delivery) -> {
        String message = new String(delivery.getBody(), "UTF-8");
        System.out.println("Received message: " + message);

        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    }, consumerTag -> {});
}

2. 死信队列(Dead Letter Exchange,DLX)

配置死信队列可以处理未能成功消费的消息。例如,消息被拒绝(nack)或超过重试次数后,可以将消息转发到死信队列进行进一步处理。

代码示例:

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");

channel.queueDeclare("queue_name", true, false, false, args);

3. 消费端幂等性

确保消费者的幂等性,即同一条消息被多次消费时,结果是相同的。可以通过在消费逻辑中引入唯一性检查来实现。

代码示例:

if (!isMessageProcessed(delivery.getMessageId())) {
    processMessage(delivery.getBody());
    markMessageAsProcessed(delivery.getMessageId());
}

通过上述方法和代码示例,可以有效地确保消息在 RabbitMQ 中的发送和消费过程的可靠性。

RabbitMQ 支持多种消费模式,适用于不同的应用场景。以下是几种常见的消费模式及其特点:

1. 集群消费(Clustered Consumption)

在集群消费模式下,多个消费者实例组成一个消费组(Consumer Group),每条消息只会被消费组中的一个消费者实例消费。这种模式适用于需要负载均衡的场景。

特点:

  • 消费者实例之间负载均衡。
  • 每条消息只会被消费一次。
  • 适合需要处理大量消息的场景。

示例:
假设有一个消费组 GroupA,包含两个消费者实例 Consumer1 和 Consumer2。当生产者发送消息到队列 QueueA 时,消息会被分配给 Consumer1 或 Consumer2,但不会同时被两个消费者实例消费。

2. 广播消费(Broadcast Consumption)

在广播消费模式下,多个消费者实例组成一个消费组,每条消息会被消费组中的所有消费者实例消费。这种模式适用于需要将消息传递给所有消费者的场景。

特点:

  • 每条消息会被所有消费者实例消费。
  • 适合需要将消息发送给多个消费者的场景,如日志处理、监控数据等。

示例:
假设有一个消费组 GroupB,包含两个消费者实例 Consumer3 和 Consumer4。当生产者发送消息到队列 QueueB 时,消息会被 Consumer3 和 Consumer4 同时消费。

3. 顺序消费(Ordered Consumption)

顺序消费模式确保消息按照发送的顺序被消费。这种模式适用于需要严格顺序处理的场景,如交易处理、日志记录等。

特点:

  • 消息按照发送的顺序被消费。
  • 适合需要严格顺序处理的业务场景。

示例:
假设有一个队列 QueueC,生产者发送消息 Msg1、Msg2、Msg3。在顺序消费模式下,消费者会按照 Msg1、Msg2、Msg3 的顺序依次消费消息。

4. 延时消费(Delayed Consumption)

延时消费模式允许消息在指定的延时后才会被消费者消费。这种模式适用于需要延迟处理的场景,如订单超时处理、定时任务等。

特点:

  • 消息在指定的延时后被消费。
  • 适合需要延迟处理的业务场景。

示例:
假设有一个队列 QueueD,生产者发送消息 Msg4 并设置延时 10 秒。在延时消费模式下,消费者会在 10 秒之后才消费 Msg4。

5. 事务消费(Transactional Consumption)

事务消费模式确保消息的生产和消费具有事务性。事务消息可以确保消息在生产者和消费者之间的事务一致性。

特点:

  • 支持事务性操作,确保消息的生产和消费一致性。
  • 适合需要事务保证的业务场景。

示例:
假设有一个队列 QueueE,生产者发送事务消息 Msg5。在事务消费模式下,消费者会在事务提交之后消费 Msg5,确保消息的消费与生产事务一致。

这些消费模式在不同的应用场景中可以帮助提高系统的处理能力、保证消息的顺序性、实现负载均衡、处理延时任务等。根据业务需求选择合适的消费模式,可以更好地满足系统的性能和可靠性要求。

© 版权声明
THE END
喜欢就支持一下吧
点赞5赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容