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

RocketMQ消息中间件项目实战入门教程

标签:
中间件
概述

RocketMQ是一款高性能的分布式消息中间件,本文将详细介绍RocketMQ的核心组件及其作用,并通过项目实战案例展示如何在实际业务中应用RocketMQ消息中间件。文章还将深入讲解RocketMQ的集群部署、性能优化及常见问题解决方案,帮助读者全面掌握RocketMQ消息中间件项目实战。

RocketMQ简介
RocketMQ基本概念

RocketMQ是由阿里巴巴开源的一款分布式消息中间件。它具有高吞吐量、低延时、可靠性高等特点。RocketMQ主要应用于分布式系统中的异步通信、解耦、流量削峰、以及海量数据处理等场景。RocketMQ基于JVM开发,支持多种编程语言,如Java、C++等。

RocketMQ的核心特点包括:

  1. 高吞吐量:单个Broker每秒可以处理十万级的消息。
  2. 低延时:在毫秒级别内完成消息的生产与消费。
  3. 可靠性:支持消息的重试、事务消息、定时消息等机制。
  4. 易于扩展:支持集群部署和水平扩展。
  5. 多种消息模式:支持发布/订阅、队列模式等。
  6. 丰富的客户端API:提供Java、C++等多种语言的客户端API。
  7. 多租户支持:支持多租户,每个租户可以独立管理和配置资源。
RocketMQ的核心组件及其作用
  • NameServer:NameServer类似于DNS服务器,负责维护Broker的地址信息,并为客户端提供Broker的地址列表。客户端通过NameServer获取到Broker的地址后,再直接与Broker进行通信。NameServer集群化部署,无状态,支持水平扩展。

  • Broker:Broker是RocketMQ的核心组件,负责消息的存储与转发。RocketMQ支持主备模式的Broker集群部署,其中主Broker负责消息的接收和转发,备Broker负责主Broker的备份和故障恢复。同时,Broker还支持集群部署,通过多个Broker实例来分摊消息处理的压力。

  • Producer:Producer负责发送消息到Broker,RocketMQ支持异步、同步发送方式。异步发送将消息发送到本地内存队列,由一个单独的线程异步地发送到Broker。同步发送直接调用网络IO发送消息,优点是发送成功后直接得到返回结果,缺点是消耗更多的资源。

  • Consumer:Consumer负责从Broker接收消息。RocketMQ支持多种消费模式,如集群消费和广播消费。集群消费中,同一Consumer Group下的多个Consumer实例平均地消费队列中的消息,避免消息重复消费。广播消费中,每个Consumer实例都会收到队列中的每条消息,适用于每个Consumer都需要处理相同消息的场景。

  • Client Library:RocketMQ提供了Java、C++等多语言的客户端库,支持消息发送、订阅、监听等操作。客户端库封装了与RocketMQ服务器通信的细节,提供了丰富的API接口。

  • 消息模型:RocketMQ支持多种消息模型,包括普通消息、有序消息、定时消息、事务消息等。这些消息模型满足了不同应用场景的需求。

  • 存储与持久化:RocketMQ将消息存储在磁盘上,并且支持消息的持久化。当Broker重启后,可以通过磁盘上的消息文件恢复未消费的消息,以确保消息的可靠传输。

  • 监控与告警:RocketMQ提供了监控插件,可以监控RocketMQ的运行状态,包括Broker的TPS、流量、延迟、存储等指标,并可以将这些指标数据上报到监控平台。当RocketMQ发生异常时,可以触发告警,便于运维人员及时发现并处理问题。
RocketMQ与其他消息中间件的对比
特性 RocketMQ Kafka RabbitMQ
高吞吐量 支持 支持 支持
低延迟 支持 支持 支持
可靠性 支持 支持 支持
异步通信 支持 支持 支持
解耦 支持 支持 支持
流量削峰 支持 支持 支持
海量数据处理 支持 支持 支持
集群部署 支持 支持 支持
多语言支持 支持 支持 支持
开发语言 Java Java Erlang
主要应用场景 分布式系统、大数据处理 大数据处理、日志聚合 分布式系统、微服务架构
兼容性 专有 开源 开源
性能优化 支持 支持 支持
消息存储与持久化 支持 支持 支持
监控与告警 支持 支持 支持

从上表可以看出,RocketMQ与其他消息中间件在高吞吐量、低延时、可靠性、异步通信等方面具有相似的功能,但RocketMQ在集群部署、多语言支持、开发语言等方面有一些独特的优势,使其更适合大规模分布式系统的使用。RocketMQ的多租户支持和丰富的消息模型也使其在企业级应用中更有优势。

RocketMQ快速入门
RocketMQ的安装与配置

操作系统要求

  • RocketMQ支持的操作系统包括但不限于Linux、Windows。

下载与安装

  1. 下载RocketMQ的压缩包,可以从RocketMQ的GitHub仓库下载。当前版本为4.9.0,下载地址为https://github.com/apache/rocketmq/releases。下载后解压到本地目录,例如/opt/rocketmq

  2. 进入解压后的RocketMQ目录,执行如下命令启动NameServer和Broker:
# 启动NameServer
nohup sh bin/mqnamesrv &
# 启动Broker
nohup sh bin/mqbroker -n localhost:9876 > broker.log 2>&1 &

配置文件说明

RocketMQ的配置文件主要包含conf/目录下的broker.propertieslogback.xml等文件,其中:

  • broker.properties:Broker的配置文件,主要配置Broker的IP地址、端口号、名称服务地址等信息。
  • logback.xml:日志配置文件,配置日志的输出级别、格式和存储路径等。
  • broker_cluster.json:集群配置文件,用于配置集群中的Broker信息。
  • broker_role.properties:角色配置文件,指定Broker的角色信息,如主备模式。

启动与停止RocketMQ服务

启动服务:

# 启动NameServer
nohup sh bin/mqnamesrv &
# 启动Broker
nohup sh bin/mqbroker -n localhost:9876 > broker.log 2>&1 &

停止服务:

# 停止NameServer
ps -ef | grep mqnamesrv | grep -v grep | awk '{print $2}' | xargs kill -9
# 停止Broker
ps -ef | grep mqbroker | grep -v grep | awk '{print $2}' | xargs kill -9

验证安装成功

启动RocketMQ服务后,可以通过浏览器访问http://localhost:9876,查看NameServer的信息。如果NameServer显示正常,说明RocketMQ安装成功。

使用RocketMQ发送和接收消息的基本示例

发送消息

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.util.concurrent.TimeUnit;

public class Producer {
    public static void main(String[] args) throws Exception {
        // 创建Producer实例对象
        DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
        // 指定NameServer地址
        producer.setNameserverAddress("localhost:9876");
        // 启动Producer
        producer.start();
        // 创建并发送消息
        for (int i = 0; i < 10; i++) {
            Message msg = new Message("TestTopic", "TagA", ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.println("SendResult: " + sendResult);
        }
        // 关闭Producer
        producer.shutdown();
    }
}

接收消息

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.topic.MessageModel;

public class Consumer {
    public static void main(String[] args) throws Exception {
        // 创建Consumer实例对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
        // 指定NameServer地址
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅主题和Tag
        consumer.subscribe("TestTopic", "TagA");
        // 设置消费模式为集群模式
        consumer.setMessageModel(MessageModel.CLUSTERING);
        // 注册消息监听器
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.println("Received message: " + new String(msg.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        // 启动Consumer
        consumer.start();
        System.out.println("Consumer started.");
        // 让主进程保持运行,防止JVM退出
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }
}

运行示例

将上述Producer类和Consumer类分别编译成类文件,并执行。可以看到Consumer接收到Producer发送的消息,并打印出来。

RocketMQ消息模型详解

消息的生产和消费模型

RocketMQ的消息生产和消费模型主要包括以下几种:

  1. 生产模型:消息生产者(Producer)发送消息到RocketMQ Broker,Broker负责将消息存储到磁盘上,并将消息推送给订阅该消息的消费者(Consumer)。

  2. 消费模型:消息消费分为同步消费和异步消费。

    • 同步消费:消费者在接收到消息后立即处理,处理完后向Broker发送确认信息,表示该消息已被处理。
    • 异步消费:消费者接收到消息后,将消息放入一个异步处理队列中,消费者继续接收新的消息。异步处理队列中的消息由一个单独的线程异步处理。
  3. 集群消费:集群消费模式中,每个消费者组中的消费者实例会平分队列中的消息。例如,有两个消费者组中的两个实例,一个实例会消费队列的一半消息,另一个实例会消费队列的另一半消息。

  4. 广播消费:广播消费模式中,每个消费者实例都会接收到队列中的每一条消息。适用于每个消费者实例都需要处理相同的消息的场景。

Topic与Tag的概念及其应用

  • Topic:Topic是RocketMQ中消息的基本分类,用于将消息进行分类和组织。生产者发送的消息会被发布到某个Topic下,消费者订阅该Topic下的消息进行消费。

  • Tag:Tag是Topic下更细粒度的消息分类,用于更精确地控制消息的路由和过滤。消费者可以通过指定Tag来过滤和消费特定的消息。

消息的过滤与路由机制

RocketMQ提供了多种消息过滤和路由机制,以满足不同场景的需求:

  1. 路由机制:RocketMQ通过Topic和Tag来实现消息的路由。当生产者发送消息到指定的Topic和Tag时,RocketMQ会根据路由规则将消息路由到相应的Broker和队列中。

  2. 过滤机制:RocketMQ支持多种消息过滤策略,如Tag过滤、SQL过滤等。消费者可以通过指定Tag或SQL语句来过滤消息。

    • Tag过滤:消费者可以通过订阅指定的Topic和Tag来过滤消息,只接收符合要求的消息。
    • SQL过滤:RocketMQ还支持通过SQL表达式来过滤消息,例如:
      consumer.subscribe("TestTopic", "* || (tag ~ 'TagA' || tag ~ 'TagB')");
  3. 消息优先级:RocketMQ还支持消息的优先级,生产者可以设置消息的优先级,消费者可以根据优先级来消费消息,优先处理高优先级的消息。

消息模型实例

假设有一个电商系统,需要处理商品的新增、修改、删除操作。可以使用RocketMQ的消息模型来实现这些操作。

  1. 消息生产和消费模型

    • 生产者根据不同的操作类型将消息发送到不同的Topic和Tag中。例如,发送新增商品的消息到Topic-GoodsTag-Add,发送修改商品的消息到Topic-GoodsTag-Update
    • 消费者订阅不同的Topic和Tag,根据接收到的消息类型来执行相应的操作。例如,订阅Topic-GoodsTag-Add的消息,执行商品新增操作;订阅Topic-GoodsTag-Update的消息,执行商品修改操作。
  2. 消息过滤

    • 消费者可以通过Tag过滤消息,只接收符合要求的消息。例如,订阅Topic-GoodsTag-Add的消息,只接收新增商品的消息。
  3. 路由机制
    • RocketMQ会根据路由规则将不同类型的消息路由到不同的Broker和队列中。例如,新增商品的消息会被路由到专门处理新增商品的Broker和队列中,修改商品的消息会被路由到专门处理修改商品的Broker和队列中。

通过上述消息模型,可以实现对商品操作的异步处理和解耦。生产者可以将商品操作的消息发送到RocketMQ中,消费者订阅相应的Topic和Tag,根据消息类型执行相应的操作。

RocketMQ项目实战

实战项目需求分析

假设有一个电商平台,需要处理大量的订单信息。为了提高系统的性能和解耦,决定使用RocketMQ来处理订单信息,包括订单创建、支付、发货等操作。

  1. 订单创建:当用户下单时,将订单信息发送到RocketMQ中,由后台系统处理订单创建。
  2. 支付通知:当用户支付成功后,将支付成功的通知发送到RocketMQ中,由后台系统处理订单支付。
  3. 发货通知:当订单支付成功后,将发货通知发送到RocketMQ中,由后台系统处理订单发货。

通过RocketMQ可以实现不同模块之间的异步通信,提高系统的性能和可扩展性。同时,RocketMQ还可以保证消息的可靠传输和顺序消费,确保订单信息的正确处理。

项目结构设计与功能模块划分

关键功能的实现步骤与代码解析

订单创建模块

生产者代码示例

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.util.concurrent.TimeUnit;

public class OrderCreateProducer {
    public static void main(String[] args) throws Exception {
        // 创建Producer实例对象
        DefaultMQProducer producer = new DefaultMQProducer("OrderCreateProducer");
        // 指定NameServer地址
        producer.setNameserverAddress("localhost:9876");
        // 启动Producer
        producer.start();
        // 创建并发送消息
        for (int i = 0; i < 10; i++) {
            Message msg = new Message("OrderCreateTopic", "TagOrderCreate", ("Order Create " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.println("SendResult: " + sendResult);
        }
        // 关闭Producer
        producer.shutdown();
    }
}

消费者代码示例

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.topic.MessageModel;

public class OrderCreateConsumer {
    public static void main(String[] args) throws Exception {
        // 创建Consumer实例对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("OrderCreateConsumer");
        // 指定NameServer地址
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅主题和Tag
        consumer.subscribe("OrderCreateTopic", "TagOrderCreate");
        // 设置消费模式为集群模式
        consumer.setMessageModel(MessageModel.CLUSTERING);
        // 注册消息监听器
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.println("Received message: " + new String(msg.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        // 启动Consumer
        consumer.start();
        System.out.println("Consumer started.");
        // 让主进程保持运行,防止JVM退出
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }
}

支付通知模块

生产者代码示例

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.util.concurrent.TimeUnit;

public class PaymentNotifyProducer {
    public static void main(String[] args) throws Exception {
        // 创建Producer实例对象
        DefaultMQProducer producer = new DefaultMQProducer("PaymentNotifyProducer");
        // 指定NameServer地址
        producer.setNameserverAddress("localhost:9876");
        // 启动Producer
        producer.start();
        // 创建并发送消息
        for (int i = 0; i < 10; i++) {
            Message msg = new Message("PaymentNotifyTopic", "TagPaymentNotify", ("Payment Notify " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.println("SendResult: " + sendResult);
        }
        // 关闭Producer
        producer.shutdown();
    }
}

消费者代码示例

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.topic.MessageModel;

public class PaymentNotifyConsumer {
    public static void main(String[] args) throws Exception {
        // 创建Consumer实例对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("PaymentNotifyConsumer");
        // 指定NameServer地址
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅主题和Tag
        consumer.subscribe("PaymentNotifyTopic", "TagPaymentNotify");
        // 设置消费模式为集群模式
        consumer.setMessageModel(MessageModel.CLUSTERING);
        // 注册消息监听器
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.println("Received payment notify: " + new String(msg.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        // 启动Consumer
        consumer.start();
        System.out.println("Consumer started.");
        // 让主进程保持运行,防止JVM退出
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }
}

发货通知模块

生产者代码示例

import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import java.util.concurrent.TimeUnit;

public class ShippingNotifyProducer {
    public static void main(String[] args) throws Exception {
        // 创建Producer实例对象
        DefaultMQProducer producer = new DefaultMQProducer("ShippingNotifyProducer");
        // 指定NameServer地址
        producer.setNameserverAddress("localhost:9876");
        // 启动Producer
        producer.start();
        // 创建并发送消息
        for (int i = 0; i < 10; i++) {
            Message msg = new Message("ShippingNotifyTopic", "TagShippingNotify", ("Shipping Notify " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.println("SendResult: " + sendResult);
        }
        // 关闭Producer
        producer.shutdown();
    }
}

消费者代码示例

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.topic.MessageModel;

public class ShippingNotifyConsumer {
    public static void main(String[] args) throws Exception {
        // 创建Consumer实例对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ShippingNotifyConsumer");
        // 指定NameServer地址
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅主题和Tag
        consumer.subscribe("ShippingNotifyTopic", "TagShippingNotify");
        // 设置消费模式为集群模式
        consumer.setMessageModel(MessageModel.CLUSTERING);
        // 注册消息监听器
        consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
            for (MessageExt msg : msgs) {
                System.out.println("Received shipping notify: " + new String(msg.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        });
        // 启动Consumer
        consumer.start();
        System.out.println("Consumer started.");
        // 让主进程保持运行,防止JVM退出
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
    }
}

RocketMQ集群部署与优化

RocketMQ集群的基本部署方案

RocketMQ集群部署方案主要包括NameServer集群、Broker集群和客户端集群三部分。

  1. NameServer集群:NameServer集群化部署,无状态,支持水平扩展。NameServer负责维护Broker的地址信息,并为客户端提供Broker的地址列表。
  2. Broker集群:Broker集群化部署,支持主备模式。主Broker负责消息的接收和转发,备Broker负责主Broker的备份和故障恢复。同时,支持集群部署,通过多个Broker实例来分摊消息处理的压力。
  3. 客户端集群:客户端集群部署,支持多实例消费。客户端可以订阅同一个Topic和Tag的消息,由集群中的多个实例来消费。

集群部署中的常见问题与解决方法

  1. NameServer不可用:NameServer集群化部署可以解决单点故障问题。当一个NameServer不可用时,客户端可以通过其他NameServer获取Broker的地址信息。可以通过增加NameServer实例数量来提高集群的可用性。
  2. Broker故障:RocketMQ支持Broker的主备模式和集群部署。当主Broker故障时,备Broker会接管主Broker的工作。当Broker集群中某个Broker故障时,可以通过其他Broker实例来分摊消息处理的压力。
  3. 客户端故障:客户端可以订阅同一个Topic和Tag的消息,由集群中的多个实例来消费。当一个客户端实例故障时,其他实例可以继续消费消息,保证系统的可用性。

性能优化的基本策略与实践

  1. 消息批量发送:通过批量发送消息可以减少网络通信的次数,提高消息的发送效率。可以设置Producer的批量发送参数,例如sendMessageBatchSize
  2. 消息压缩:通过压缩消息可以减少消息的传输和存储空间。可以设置Producer的压缩参数,例如compressMsgBodyOverHowmuch
  3. 消息路由优化:通过优化消息的路由规则可以减少消息的转发次数,提高消息的处理效率。可以设置Broker的路由规则,例如brokerClusterName
  4. 消息过滤优化:通过优化消息的过滤规则可以减少消息的过滤次数,提高消息的处理效率。可以设置Consumer的过滤规则,例如subscribe方法中的Tag和SQL过滤。

RocketMQ常见问题与解决方案

常见错误及异常处理方法

  1. NameServer不可达:检查NameServer的地址是否正确,确保NameServer服务正常启动。
  2. Broker不可达:检查Broker的地址是否正确,确保Broker服务正常启动。
  3. 消息发送失败:检查消息的格式是否正确,确保消息的内容和格式符合要求。
  4. 消息消费失败:检查消息的过滤规则是否正确,确保消息的过滤和路由规则符合要求。

高可用与故障恢复策略

  1. NameServer高可用:通过集群化部署NameServer可以提高集群的可用性。当一个NameServer不可用时,客户端可以通过其他NameServer获取Broker的地址信息。
  2. Broker高可用:通过主备模式部署Broker可以提高集群的可用性。当主Broker故障时,备Broker会接管主Broker的工作。
  3. 客户端高可用:通过集群化部署客户端可以提高集群的可用性。当一个客户端实例故障时,其他实例可以继续消费消息。
  4. 故障恢复:通过设置消息的重试机制可以实现故障恢复。当消息消费失败时,可以通过重试机制重新消费消息。

日志分析与监控工具介绍

  1. 日志分析:RocketMQ提供了日志分析工具,可以分析RocketMQ的日志文件,获取RocketMQ的运行状态和错误信息。可以通过日志文件的路径和格式来获取RocketMQ的日志信息。
  2. 监控工具:RocketMQ提供了监控插件,可以监控RocketMQ的运行状态,包括Broker的TPS、流量、延迟、存储等指标。可以通过监控插件将这些指标数据上报到监控平台,实现RocketMQ的实时监控和告警。
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消