消息中间件之RabbitMQ基本使用

前言

上一篇已经讲解了一些基础知识以及安装过程,这一篇主要演示一下三种常用的交换机模式.最近琐事儿比较多,所以更新的比较慢,见谅!

创建项目

首先创建一个springboot项目,然后引入相关maven依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.study</groupId>
    <artifactId>rabbitmq</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rabbitmq</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

接着改一下配置:

server.port=8081

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5675
spring.rabbitmq.username=root
spring.rabbitmq.password=123456

项目就这样创建成功了,下面来实际操作一下rabbitmq。

rabbitmq-direct(直接交换模式)

在这里插入图片描述
这种类型的交换机的路由规则是根据一个routingKey的标识,交换机通过一个routingKey与队列绑定 ,在生 产者生产消息的时候 指定一个routingKey 当绑定的队列的routingKey 与生产者发送的一样 那么交换机会把这个消息发送给对应的队列。

先创建一个direct的config类:

package com.study.rabbitmq.config;

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

/**
 * RabbitMq配置类
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Configuration
public class DirectExchangeConfig {

    @Bean
    public Queue queue(){
        return new Queue("queue_one");
    }

    @Bean
    public Queue queue2(){
        return new Queue("queue_two");
    }

    @Bean
    public Queue queue3(){
        return new Queue("queue_three");
    }

}

意思就是创建三个队列,并交给spring容器管理,先创建3个队列留着备用

一对一

生产者:

package com.study.rabbitmq.producter;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 生产者发送消息
 */
@Component
public class ProducterOne {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send(){
        String message = "当前时间:"+ LocalDateTime.now();
        System.out.println("生产者发送消息了:"+message);
        amqpTemplate.convertSendAndReceive("queue_one",message);
    }
}

意思比较简单,获取当前时间,打印在控制台,并发送消息到 queue_one 队列

消费者:

package com.study.rabbitmq.customer;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者接收消息
 */
@Component
public class CustomerOne {

    @RabbitListener(queues = "queue_one")
    @RabbitHandler
    public void process(String message){
        System.out.println("消费者接收消息:"+message);
    }
}

@Component 将当前类实例化到spring容器中
@RabbitListener(queues = “queue_one”) 监听queue_one这个队列
@RabbitHandler : @RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理

代码就是接收到消息并打印到控制台

现在写一个测试类来测试一下:

package com.study.rabbitmq;

import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {

    @Autowired
    private ProducterOne producterOne;

    @Test
    public void one(){
        producterOne.send();
    }
}

这个代码就不用解释了吧,运行一下看一下效果

在这里插入图片描述

一对多

生产者:

package com.study.rabbitmq.producter;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 生产者发送消息
 */
@Component
public class ProducterOne {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send(){
        String message = "当前时间:"+ LocalDateTime.now();
        System.out.println("生产者发送消息了:"+message);
        amqpTemplate.convertSendAndReceive("queue_one",message);
    }

    public void send2(int i){
        String message = "send2 -- "+i;
        System.out.println("生产者发送了消息:"+message);
        amqpTemplate.convertSendAndReceive("queue_two",message);
    }
}

消费者:

package com.study.rabbitmq.customer;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者接收消息
 */
@Component
public class CustomerOne {

    @RabbitListener(queues = "queue_one")
    @RabbitHandler
    public void process(String message){
        System.out.println("消费者接收消息:"+message);
    }

    @RabbitListener(queues = "queue_two")
    @RabbitHandler
    public void process1(String message){
        System.out.println("process1:"+message);
    }

    @RabbitListener(queues = "queue_two")
    @RabbitHandler
    public void process2(String message){
        System.out.println("process2:"+message);
    }
}

这里写了两个消费者方法 process1 和 process2 都是监听 queue_two这个队列

测试类:

package com.study.rabbitmq;

import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {

    @Autowired
    private ProducterOne producterOne;

    @Test
    public void one(){
        producterOne.send();
    }

    @Test
    public void two(){
        for (int i=0;i<100;i++){
            producterOne.send2(i);
        }
    }
}

运行结果如下:
在这里插入图片描述
一个消息只能被消费一次,消费者轮流去消费

多对多

生产者

package com.study.rabbitmq.producter;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 生产者发送消息
 */
@Component
public class ProducterOne {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send(){
        String message = "当前时间:"+ LocalDateTime.now();
        System.out.println("生产者发送消息了:"+message);
        amqpTemplate.convertSendAndReceive("queue_one",message);
    }

    public void send2(int i){
        String message = "send2 -- "+i;
        System.out.println("生产者发送了消息:"+message);
        amqpTemplate.convertSendAndReceive("queue_two",message);
    }

    public void send3(int i){
        String message = "send3 -- "+i;
        System.out.println("生产者发送了消息:"+message);
        amqpTemplate.convertSendAndReceive("queue_two",message);
    }
}

这里看send2和send3两个方法,都是像queue_two这个队列发送消息

消费者:

package com.study.rabbitmq.customer;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者接收消息
 */
@Component
public class CustomerOne {

    @RabbitListener(queues = "queue_one")
    @RabbitHandler
    public void process(String message){
        System.out.println("消费者接收消息:"+message);
    }

    @RabbitListener(queues = "queue_two")
    @RabbitHandler
    public void process1(String message){
        System.out.println("process1:"+message);
    }

    @RabbitListener(queues = "queue_two")
    @RabbitHandler
    public void process2(String message){
        System.out.println("process2:"+message);
    }
}

两个消费者去消费消息

测试类:

package com.study.rabbitmq;

import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {

    @Autowired
    private ProducterOne producterOne;

    @Test
    public void one(){
        producterOne.send();
    }

    @Test
    public void two(){
        for (int i=0;i<100;i++){
            producterOne.send2(i);
        }
    }

    @Test
    public void three(){
        for (int i=0;i<100;i++){
            producterOne.send2(i);
            producterOne.send3(i);
        }
    }
 }

运行结果:
在这里插入图片描述
消费者也是轮流消费消息的

对象消息传递

创建一个实体类

package com.study.rabbitmq.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;

/**
 * Order
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {

    /**
     * 订单编号
     */
    private String orderId;

    /**
     * 商品名称
     */
    private String productName;

    /**
     * 订单金额
     */
    private BigDecimal amount;

}

生产者:

package com.study.rabbitmq.producter;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 生产者发送消息
 */
@Component
public class ProducterOne {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send4(Order order){
        amqpTemplate.convertSendAndReceive("queue_three",order);
    }
}

消费者:

package com.study.rabbitmq.customer;

import com.study.rabbitmq.entity.Order;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 消费者接收消息
 */
@Component
public class CustomerOne {

    @RabbitListener(queues = "queue_three")
    @RabbitHandler
    public void process3(Order order){
        System.out.println("order::"+order);
    }
}

测试类:

package com.study.rabbitmq;

import com.study.rabbitmq.entity.Order;
import com.study.rabbitmq.producter.ProducterOne;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqOneTest {

    @Autowired
    private ProducterOne producterOne;

    @Test
    public void four(){
        producterOne.send4(Order.builder()
                .orderId("123")
                .productName("测试")
                .amount(BigDecimal.valueOf(3.3))
                .build());
    }
}

运行结果:在这里插入图片描述

rabbitmq-fanout(发布订阅模式)

在这里插入图片描述
这种类型的交换机路由规则很简单,只要与他绑定了的队列, 他就会吧消息发送给对应队列(与routingKey 没关系)。优点是转发消息最快,性能最好。

新建一个fanout的config类;

package com.study.rabbitmq.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * FanoutExchangeConfig
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Configuration
public class FanoutExchangeConfig {

    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanoutExchange");
    }

    @Bean(name = "fanout1")
    public Queue queue1(){
        return new Queue("fanout1");
    }

    @Bean(name = "fanout2")
    public Queue queue2(){
        return new Queue("fanout2");
    }

    /**
     * 将队列和交换机绑定
     * @param fanout1 队列
     * @param fanoutExchange 交换机
     * @return
     */
    @Bean
    public Binding binding1(@Qualifier("fanout1") Queue fanout1, FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanout1).to(fanoutExchange);
    }

    /**
     * 将队列和交换机绑定
     * @param fanout2 队列
     * @param fanoutExchange 交换机
     * @return
     */
    @Bean
    public Binding binding2(@Qualifier("fanout2") Queue fanout2,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanout2).to(fanoutExchange);
    }
}

先创建一个交换机,再创建两个队列,最后将队列绑定到交换机上面

生产者:

package com.study.rabbitmq.producter;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * ProductThree
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Component
public class ProductThree {

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send(){
        String message = "**********这是发布订阅模式****************";
        System.out.println("send:"+message);
        /**
         * 参数依次为
         * 1.交换机名称(我们创建的是fanoutExchange交换机)
         * 2.路由键
         * 3.消息
         */
        amqpTemplate.convertSendAndReceive("fanoutExchange","",message);
    }
}

发送消息到交换机

消费者:

  package com.study.rabbitmq.customer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * CustomerThree
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Component
public class CustomerThree {

    @RabbitListener(queues = "fanout1")
    @RabbitHandler
    public void process1(String message){
        System.out.println("process1:"+message);
    }

    @RabbitListener(queues = "fanout2")
    @RabbitHandler
    public void process2(String message){
        System.out.println("process2:"+message);
    }
}

两个消费者监听不同的队列

测试类:

package com.study.rabbitmq;

import com.study.rabbitmq.producter.ProductThree;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * FanoutExchangeTest
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class FanoutExchangeTest {

    @Autowired
    private ProductThree productThree;

    @Test
    public void fanout(){
        productThree.send();
    }
}

运行结果
在这里插入图片描述

rabbitmq-Topic(主题模式)

在这里插入图片描述
该类型的交换器将所有发送到Topic Exchange的消息被转发到所有RoutingKey中指定的Topic的队列上面。

Exchange将RoutingKey和某Topic进行模糊匹配,其中“”用来匹配一个词,“#”用于匹配一个或者多个词。打个比方 假设 我绑定的routingKey 有队列A和B ,A的 routingKey是:*.user ,B的routingKey是: #.user,那么我生产一条消息routingKey 为: error.user 那么此时 2个队列都能接受到, 如果改为 topic.error.user 那么这时候 只有B能接收到了

新建一个topic的config类:

package com.study.rabbitmq.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * TopicExchangeConfig
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@Configuration
public class TopicExchangeConfig {

    /**
     * 创建队列
     * @return
     */
    @Bean("topic1")
    public Queue topicQueueOne(){
        return new Queue("topic1");
    }

    /**
     * 创建队列
     * @return
     */
    @Bean("topic2")
    public Queue topicQueueTwo(){
        return new Queue("topic2");
    }

    /**
     * 创建队列
     * @return
     */
    @Bean("topic3")
    public Queue topicQueueThree(){
        return new Queue("topic3");
    }

    /**
     * 创建交换机
     * @return
     */
    @Bean
    public TopicExchange exchange(){
        return new TopicExchange("topicExchange");
    }

    /**
     * 将队列绑定在交互机上并设置routing_key
     * @param topicExchange 交换机
     * @param topic1 队列名称
     * @return
     */
    @Bean
    public Binding bindingOne(TopicExchange topicExchange, @Qualifier("topic1") Queue topic1){
        return BindingBuilder.bind(topic1).to(topicExchange).with("*.topic.message");
    }

    /**
     * 将队列绑定在交互机上并设置routing_key
     * @param topicExchange
     * @param topic2
     * @return
     */
    @Bean
    public Binding bindingTwo(TopicExchange topicExchange, @Qualifier("topic2") Queue topic2){
        return BindingBuilder.bind(topic2).to(topicExchange).with("#.message");
    }

    /**
     * 将队列绑定在交互机上并设置routing_key
     * @param topicExchange
     * @param topic3
     * @return
     */
    @Bean
    public Binding bindingThree(TopicExchange topicExchange, @Qualifier("topic3") Queue topic3){
        return BindingBuilder.bind(topic3).to(topicExchange).with("demo.message.topic");
    }
}

创建一个交换机,三个队列,将三个队列绑定在交换机上并设置routing_key

生产者:

    package com.study.rabbitmq.producter;

    import org.springframework.amqp.core.AmqpTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    /**
     * ProductTwo
     *
     * @author:gujunjie
     * @create:2020-05-12
     */
    @Component
    public class ProductTwo {

        @Autowired
        private AmqpTemplate amqpTemplate;

        public void send1(){
            String message = "************* send1 *************";
            System.out.println("send1:"+message);

            /**
             * 参数依次为
             * 1.交换机名称(我们创建的是topicExchange交换机)
             * 2.路由键(this.topic.message 会路由到 *.topic.message 和 #.message 也就是 交换机topic1 topic2)
             * 3.消息
             */
            amqpTemplate.convertSendAndReceive("topicExchange","this.topic.message",message);
        }

        public void send2(){
            String message = "************* send2 *************";
            System.out.println("send2:"+message);

            /**
             * 参数依次为
             * 1.交换机名称(我们创建的是topicExchange交换机)
             * 2.路由键(topic.study.message 会路由到  #.message 也就是 交换机 topic2)
             * 3.消息
             */
            amqpTemplate.convertSendAndReceive("topicExchange","topic.study.message",message);
        }

        public void send3(){
            String message = "************* send3 *************";
            System.out.println("send3:"+message);

            /**
             * 参数依次为
             * 1.交换机名称(我们创建的是topicExchange交换机)
             * 2.路由键(demo.message.topic 会路由到 demo.message.topic 和 #.message 也就是 交换机topic3)
             * 3.消息
             */
            amqpTemplate.convertSendAndReceive("topicExchange","demo.message.topic",message);
        }
    }

这里多看一下注释,这样就能懂啦

消费者:

    package com.study.rabbitmq.customer;

    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;

    /**
     * CustomerTwo
     *
     * @author:gujunjie
     * @create:2020-05-12
     */
    @Component
    public class CustomerTwo {

        /**
         * 监听队列1的消息
         * @param message 消息
         */
        @RabbitListener(queues = "topic1")
        @RabbitHandler
        public void proces1(String message){
            System.out.println("proces1:"+message);
        }

        /**
         * 监听队列2的消息
         * @param message 消息
         */
        @RabbitListener(queues = "topic2")
        @RabbitHandler
        public void proces2(String message){
            System.out.println("proces2:"+message);
        }

        /**
         * 监听队列3的消息
         * @param message 消息
         */
        @RabbitListener(queues = "topic3")
        @RabbitHandler
        public void proces3(String message){
            System.out.println("proces3:"+message);
        }
    }

三个消费者监听三个队列

测试类:

package com.study.rabbitmq;

import com.study.rabbitmq.producter.ProductTwo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * TopicExchangeTest
 *
 * @author:gujunjie
 * @create:2020-05-12
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class TopicExchangeTest {

    @Autowired
    private ProductTwo productTwo;

    @Test
    public void send(){
        productTwo.send1();
        productTwo.send2();
        productTwo.send3();
    }
}

运行结果:
在这里插入图片描述
结果与我们预期的一致

这就是常用的三种交换机,今天文字解释的比较少,毕竟运用主要是要靠写的,例子都比较简单.很容易理解,源码就不贴了.下一篇会记录一下 确认机制 死信 集群等知识!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值