为了账号安全,请及时绑定邮箱和手机立即绑定

MQ消息中间件教程:初学者快速入门指南

标签:
中间件
概述

本文档介绍了MQ消息中间件的基本概念、作用及优势,并列举了常见的MQ类型,如RabbitMQ、Apache Kafka、ActiveMQ和IBM MQ。我们详细探讨了MQ在分布式系统和异步通信中的应用场景,提供了安装配置和消息发布与订阅等入门操作,帮助读者快速掌握MQ消息中间件的使用方法。

MQ消息中间件简介
什么是MQ消息中间件

MQ消息中间件是一种软件系统,用于在不同应用程序或系统之间传递消息。它主要实现应用间的异步通信,允许应用程序通过发送和接收消息来互相通信。MQ消息中间件可以增强系统的可扩展性、可靠性和灵活性。

MQ消息中间件的作用和优势

MQ消息中间件的主要作用包括:

  1. 解耦应用:通过引入中间层,MQ可以将发送端和接收端解耦,使不同系统可独立开发、部署和扩展。
  2. 异步通信:MQ支持异步消息传递,发送者不必等待接收者处理完成,提高了系统响应速度和可用性。
  3. 负载均衡:MQ可以将消息负载均衡地分发到多个接收者,确保系统不会因某些节点过载而崩溃。
  4. 可靠传输:MQ通常提供消息的持久化存储,确保即使发送方和接收方之间的连接中断,消息也不会丢失。
  5. 事务处理:MQ支持事务处理,确保消息在传输过程中的完整性和一致性。
常见的MQ消息中间件类型

常见的MQ消息中间件包括:

  1. RabbitMQ
  2. Apache Kafka
  3. ActiveMQ
  4. IBM MQ

这些MQ消息中间件在功能和特性上各有不同,满足不同的应用场景和需求。

MQ消息中间件应用场景
分布式系统中的应用

在分布式系统中,MQ消息中间件可作为节点之间通信的桥梁。一个分布式系统可能包含多个服务,这些服务通过MQ进行异步通信。例如,一个服务需要向另一个服务发送消息时,可以直接将消息发送到MQ中间件,而无须直接与目标服务进行交互。以下是使用MQ在分布式系统中的示例:

// 发送端代码示例(使用RabbitMQ)
import com.rabbitmq.client.*;

public class Producer {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String message = "Hello World!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}

// 接收端代码示例(使用RabbitMQ)
import com.rabbitmq.client.*;

public class Consumer {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}
``

分布式系统中的这种消息传递模式确保了服务之间的解耦和异步通信,提升了系统的灵活性和可维护性。

## 异步通信的应用

MQ消息中间件的一个重要用途是在系统中实现异步通信。例如,在一个订单系统中,当用户提交订单后,系统可以将订单信息发送到MQ,然后继续处理其他事务,而不需要等待订单处理结果。订单处理服务可以从MQ中接收订单信息并独立处理,最终将处理结果返回给MQ。这种方式通过异步处理提高了系统的响应速度和吞吐量。

```java
// 示例:订单系统中的异步处理
import com.rabbitmq.client.*;

public class OrderService {
    private static final String QUEUE_NAME = "order_queue";

    public void submitOrder(Order order) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String message = order.toJson(); // 假设Order类有一个toJson方法,将订单转换为JSON字符串
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}

public class OrderProcessor {
    private static final String QUEUE_NAME = "order_queue";

    public void processOrders() throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            // 处理订单逻辑
            System.out.println(" [x] Done.");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}
``

在上述示例中,`OrderService`类负责将订单信息发送到MQ,而`OrderProcessor`类则从MQ接收订单信息并进行处理。这种方式实现了订单处理的异步化,系统可以在发送订单后立即响应,而无需等待订单处理完成。

## 解耦系统组件的应用

MQ消息中间件可以帮助系统中不同组件的解耦。例如,假设有一个电商系统,包含订单系统、库存系统和支付系统。订单系统在生成订单后,可以将订单信息发送到MQ,库存系统和支付系统分别从MQ接收订单信息并进行相应的处理。通过这种方式,各个系统可以独立运行和扩展,而不需要直接耦合在一起。

```java
// 示例:电商系统中的解耦
import com.rabbitmq.client.*;

public class OrderSystem {
    private static final String QUEUE_NAME = "order_queue";

    public void createOrder(Order order) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String message = order.toJson(); // 假设Order类有一个toJson方法,将订单转换为JSON字符串
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}

public class InventorySystem {
    private static final String QUEUE_NAME = "order_queue";

    public void updateInventory(Order order) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            // 更新库存逻辑
            System.out.println(" [x] Done.");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}

public class PaymentSystem {
    private static final String QUEUE_NAME = "order_queue";

    public void processPayment(Order order) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            // 处理支付逻辑
            System.out.println(" [x] Done.");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}
``

在上述示例中,`OrderSystem`类负责生成订单并将其发送到MQ,`InventorySystem`类和`PaymentSystem`类分别从MQ接收订单信息并进行相应的处理。这种方式使得系统中的各个组件可以独立运行和扩展,而不需要直接耦合在一起。

# MQ消息中间件基本概念

## 发布/订阅模型

发布/订阅模型(Publish/Subscribe Model)是一种消息传递模式,在此模式下,消息的发送者(发布者)不需要直接与接收者(订阅者)进行通信。发布者将消息发布到一个主题(Topic),所有订阅了该主题的订阅者都会收到消息。这种模式通常用于一对多或多对多的消息传递场景。

### 消息发布示例
```java
import org.apache.activemq.ActiveMQConnectionFactory;

public class Publisher {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String queue = "pubsub";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination destination = session.createTopic(queue);
        javax.jms.MessageProducer producer = session.createProducer(destination);
        javax.jms.Message message = session.createTextMessage("Hello World!");
        producer.send(message);
        session.close();
        connection.close();
    }
}

消息订阅示例

import org.apache.activemq.ActiveMQConnectionFactory;

public class Subscriber {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String queue = "pubsub";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination destination = session.createTopic(queue);
        javax.jms.MessageConsumer consumer = session.createConsumer(destination);
        consumer.setMessageListener(message -> {
            System.out.println("Received message: " + ((javax.jms.TextMessage) message).getText());
        });
        session.close();
        connection.close();
    }
}
``

在发布/订阅模型中,发布者和订阅者之间不需要知道对方的存在,这提供了更高的解耦度和灵活性。

## 请求/响应模型

请求/响应模型(Request/Reply Model)是一种消息传递模式,在此模式下,发送者(请求者)发送一个请求消息,接收者(响应者)接收该消息并发送一个响应消息。请求者通常会阻塞等待响应,直到收到响应消息后才继续执行。这种模式通常用于需要请求和响应的场景,如远程过程调用(RPC)。

### 请求发送示例
```java
import org.apache.activemq.ActiveMQConnectionFactory;

public class Requester {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String requestQueue = "request";
        String responseQueue = "response";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination requestDestination = session.createQueue(requestQueue);
        javax.jms.Destination responseDestination = session.createTemporaryQueue();
        javax.jms.MessageProducer producer = session.createProducer(requestDestination);
        javax.jms.MessageConsumer consumer = session.createConsumer(responseDestination);
        javax.jms.Message response = null;

        javax.jms.Message message = session.createTextMessage("Hello World!");
        message.setStringProperty("JMSReplyTo", responseDestination.toString());
        producer.send(message);

        response = consumer.receive();

        System.out.println("Received response: " + ((javax.jms.TextMessage) response).getText());
        session.close();
        connection.close();
    }
}

响应发送示例

import org.apache.activemq.ActiveMQConnectionFactory;

public class Responder {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String requestQueue = "request";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination destination = session.createQueue(requestQueue);
        javax.jms.MessageConsumer consumer = session.createConsumer(destination);
        javax.jms.MessageProducer producer = session.createProducer(null);

        consumer.setMessageListener(message -> {
            javax.jms.TextMessage textMessage = (javax.jms.TextMessage) message;
            System.out.println("Received request: " + textMessage.getText());
            String responseQueue = textMessage.getStringProperty("JMSReplyTo");
            javax.jms.TextMessage response = session.createTextMessage("Hello back at you!");
            response.setJMSCorrelationID(textMessage.getJMSCorrelationID());
            producer.send(session.createQueue(responseQueue), response);
        });

        session.close();
        connection.close();
    }
}
``

在请求/响应模型中,请求者发送请求消息并等待响应消息,响应者接收请求消息并发送响应消息。这种模式确保了请求和响应之间的紧密耦合。

## 消息队列与主题
### 消息队列
消息队列(Queue)是一种点对点(Point-to-Point)的消息传递模式,一个消息只能被一个接收者接收。每个消息队列都有一个唯一的标识符,发送者将消息发送到队列,接收者从队列中接收消息。

### 消息主题
消息主题(Topic)是一种发布/订阅(Publish/Subscribe)的消息传递模式,多个接收者可以接收同一个主题发布的消息。发送者将消息发布到主题,所有订阅了该主题的接收者都会收到消息。

## 消息持久化与非持久化
### 消息持久化
消息持久化(Persistent Message)是指消息在发送到MQ中间件时被存储在持久化介质中。如果消息发送过程中出现故障,消息仍然可以被恢复和重新传递。这种特性保证了消息传递的可靠性,即使在系统故障或网络中断的情况下,消息也不会丢失。

### 消息非持久化
消息非持久化(Non-Persistent Message)是指消息在发送过程中没有被存储在任何持久化介质中。这种消息传递方式通常用于实时性要求较高的场景,因为非持久化消息在传输过程中一旦丢失,将无法恢复。

## 消息传递模式与协议
### 消息传递模式
常见的消息传递模式包括:

1. **点对点模式**:消息只能被一个接收者接收。
2. **发布/订阅模式**:消息可以被多个接收者接收。
3. **请求/响应模式**:请求者发送请求,接收者发送响应。

### 消息协议
常见的消息协议包括:

1. **AMQP**:高级消息队列协议,用于在应用程序之间传输消息。
2. **MQTT**:消息队列遥测传输协议,用于物联网设备之间的消息传递。
3. **STOMP**:简单文本消息协议,用于在不同系统之间传输消息。

# MQ消息中间件入门操作

## 安装与配置MQ消息中间件
以RabbitMQ为例,安装和配置步骤如下:

### 安装RabbitMQ
1. **下载安装包**:访问[RabbitMQ下载页面](https://www.rabbitmq.com/download.html),根据操作系统选择合适的安装包。
2. **安装**:按照安装包中的说明进行安装。例如,对于Linux系统,可以使用以下命令安装:

   ```sh
   sudo apt-get update
   sudo apt-get install rabbitmq-server
  1. 启动RabbitMQ
    sudo systemctl start rabbitmq-server

配置RabbitMQ

RabbitMQ的配置可以通过配置文件完成,也可以通过命令行工具进行管理。常用的命令行工具包括rabbitmqctlrabbitmq-plugins

  1. 启动管理插件

    sudo rabbitmq-plugins enable rabbitmq_management
  2. 访问管理界面:启动RabbitMQ管理界面后,可以通过浏览器访问http://localhost:15672来查看和管理RabbitMQ。
创建和管理队列与主题

创建队列

队列(Queue)是用于存储消息的容器,发送者可以将消息发送到队列,接收者可以从队列中接收消息。

import com.rabbitmq.client.*;

public class QueueExample {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    }
}

创建主题

主题(Topic)用于发布/订阅模式,发送者可以将消息发布到主题,所有订阅了该主题的接收者都会收到消息。

import org.apache.activemq.ActiveMQConnectionFactory;

public class TopicExample {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String topicName = "myTopic";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination destination = session.createTopic(topicName);
    }
}

管理队列和主题

  • 查看队列:可以通过管理界面或命令行工具查看队列的状态和配置。
  • 删除队列:可以通过管理界面或命令行工具删除不需要的队列。
  • 查看主题:可以通过管理界面或命令行工具查看主题的状态和配置。
  • 删除主题:可以通过管理界面或命令行工具删除不需要的主题。
发布与订阅消息

发布消息

发送者(Publisher)将消息发布到队列或主题。

import com.rabbitmq.client.*;

public class Publisher {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String message = "Hello World!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println(" [x] Sent '" + message + "'");
    }
}

订阅消息

接收者(Subscriber)从队列或主题接收消息。

import com.rabbitmq.client.*;

public class Subscriber {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
    }
}
消息的消费与响应

消费消息

接收者从队列中消费消息。当接收者接收到消息后,如果处理成功,可以发送一个确认消息给发送者,表示消息已经被成功处理。

import com.rabbitmq.client.*;

public class Consumer {
    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            // 模拟处理时间
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(" [x] Done.");
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        };
        channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { });
    }
}

响应消息

在请求/响应模型中,接收者发送响应消息给请求者。

import org.apache.activemq.ActiveMQConnectionFactory;

public class Responder {
    public static void main(String[] args) throws Exception {
        String url = "tcp://localhost:61616";
        String requestQueue = "request";
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
        javax.jms.Connection connection = connectionFactory.createConnection();
        connection.start();
        javax.jms.Session session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
        javax.jms.Destination destination = session.createQueue(requestQueue);
        javax.jms.MessageConsumer consumer = session.createConsumer(destination);
        javax.jms.MessageProducer producer = session.createProducer(null);

        consumer.setMessageListener(message -> {
            javax.jms.TextMessage textMessage = (javax.jms.TextMessage) message;
            System.out.println("Received request: " + textMessage.getText());
            String responseQueue = textMessage.getStringProperty("JMSReplyTo");
            javax.jms.TextMessage response = session.createTextMessage("Hello back at you!");
            response.setJMSCorrelationID(textMessage.getJMSCorrelationID());
            producer.send(session.createQueue(responseQueue), response);
        });

        session.close();
        connection.close();
    }
}
消息中间件的常见问题与解决方案
常见问题解析

消息丢失

消息在传输过程中可能会丢失,常见原因包括网络问题、系统故障等。为了解决这个问题,可以启用消息持久化特性,确保消息在传输过程中即使出现故障也不会丢失。

消息重复

消息在传输过程中可能会因为网络重试机制等原因导致消息重复。为了解决这个问题,可以使用唯一的消息标识符(如消息ID),并确保接收者处理重复消息时不会对业务造成影响。

性能瓶颈

消息中间件在高负载情况下可能会出现性能瓶颈。为了解决这个问题,可以采用集群部署、负载均衡等方式来提升系统性能。

性能优化策略

集群部署

通过将消息中间件部署在多个节点上并形成集群,可以提高系统的可扩展性和可用性。例如,RabbitMQ支持集群部署,可以通过配置多个节点来分担负载。

负载均衡

通过负载均衡技术,可以将消息均匀地分发到各个节点上,避免某些节点过载。例如,可以使用消息中间件的负载均衡特性或第三方负载均衡器来实现负载均衡。

消息压缩

通过压缩消息数据,可以减少网络传输的流量,提高传输效率。例如,可以使用消息中间件提供的消息压缩功能或自定义压缩算法来实现消息压缩。

安全性问题及对策

访问控制

通过设置访问控制策略,可以限制客户端对消息中间件的访问权限。例如,可以使用消息中间件提供的权限管理功能来实现访问控制。

数据加密

通过加密传输的数据,可以保护消息在传输过程中的安全性。例如,可以使用SSL/TLS协议来加密传输的数据。

消息签名

通过给消息添加签名,可以确保消息在传输过程中的完整性。例如,可以使用数字签名技术来实现消息签名。

总结与进阶学习方向
MQ消息中间件的总结回顾

MQ消息中间件是一种强大的工具,可以帮助实现应用间的异步通信,提高系统的可扩展性、可靠性和灵活性。通过学习和实践,可以更好地利用MQ消息中间件来构建高性能、高可用性的分布式系统。

进阶学习资源推荐

RabbitMQ

Apache Kafka

ActiveMQ

IBM MQ

实战项目建议

电商系统

在电商系统中,可以使用MQ消息中间件实现订单生成、支付和库存更新等操作的异步通信。例如,当用户提交订单时,订单系统可以将订单信息发送到MQ,库存系统和支付系统分别从MQ接收订单信息并进行相应的处理。

日志收集系统

在日志收集系统中,可以使用MQ消息中间件收集不同系统产生的日志数据。例如,各个服务可以将日志信息发送到MQ,日志收集系统从MQ接收日志信息并进行集中存储和分析。

事件驱动架构

在事件驱动架构中,可以通过MQ消息中间件实现不同事件的处理。例如,当某个事件发生时,事件处理系统可以将事件信息发送到MQ,订阅了该事件的处理系统从MQ接收事件信息并进行相应的处理。

通过实际项目,可以更好地理解并应用MQ消息中间件的相关知识。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消