RabbitMQ的工作模式(Java原生代码版)

零:连接

public class RabbitUtils {
    private static ConnectionFactory connectionFactory = new ConnectionFactory();
    static {
        connectionFactory.setHost("");
        connectionFactory.setPort(5671);//5672是RabbitMQ的默认端口号
        connectionFactory.setUsername("");
        connectionFactory.setPassword("");
        //connectionFactory.setVirtualHost("/");
    }
    public static Connection getConnection(){
        Connection conn = null;
        try {
            conn = connectionFactory.newConnection();
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

一、简单模式

图解:

代码: 

生产者:

package com.baiqi.rabbitmq.helloworld;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @author 白起老师
 */
public class Producer {

    public static void main(String[] args) throws IOException, TimeoutException {


        //获取TCP长连接
        Connection conn = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        Channel channel = conn.createChannel();

        //创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
        //第一个参数:队列名称ID
        //第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
        //第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
        //第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
        //其他额外的参数, null
        channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,true, false, false, null);

        String message = "成功";
        //四个参数
        //exchange 交换机,暂时用不到,在后面进行发布订阅时才会用到
        //队列名称
        //额外的设置属性
        //最后一个参数是要传递的消息字节数组
        channel.basicPublish("", RabbitConstant.QUEUE_HELLOWORLD, null,message.getBytes());
        channel.close();
        conn.close();
        System.out.println("===发送成功===");

    }
}

消费者:

package com.baiqi.rabbitmq.helloworld;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) throws IOException, TimeoutException {


        //获取TCP长连接
         Connection conn = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        Channel channel = conn.createChannel();

        //创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
        //第一个参数:队列名称ID
        //第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
        //第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
        //第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
        //其他额外的参数, null
        channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,true, false, false, null);

        //从MQ服务器中获取数据

        //创建一个消息消费者
        //第一个参数:队列名
        //第二个参数代表是否自动确认收到消息,false代表手动编程来确认消息,这是MQ的推荐做法
        //第三个参数要传入DefaultConsumer的实现类
        channel.basicConsume(RabbitConstant.QUEUE_HELLOWORLD, false, new Reciver(channel));


    }
}


class  Reciver extends DefaultConsumer {

    private Channel channel;
    //重写构造函数,Channel通道对象需要从外层传入,在handleDelivery中要用到
    public Reciver(Channel channel) {
        super(channel);
        this.channel = channel;
    }

    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {

         String message = new String(body);
         System.out.println("消费者接收到的消息:"+message);

         System.out.println("消息的TagId:"+envelope.getDeliveryTag());
        //false只确认签收当前的消息,设置为true的时候则代表签收该消费者所有未签收的消息
        channel.basicAck(envelope.getDeliveryTag(), false);
    }
}

二、Work queues 工作队列模式

图解:

代码:

发送者:

package com.baiqi.rabbitmq.workqueue;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 *
 * 发送者
 */
public class OrderSystem {

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);

        for(int i = 1 ; i <= 100 ; i++) {
            SMS sms = new SMS("乘客" + i, "13900000" + i, "您的车票已预订成功");
            String jsonSMS = new Gson().toJson(sms);
            channel.basicPublish("" , RabbitConstant.QUEUE_SMS , null , jsonSMS.getBytes());
        }
        System.out.println("发送数据成功");
        channel.close();
        connection.close();
    }
}

要发送的信息:

public class SMS {

    private String name;
    private String mobile;
    private String content;

    public SMS(String name, String mobile, String content) {
        this.name = name;
        this.mobile = mobile;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

消费者1:

package com.baiqi.rabbitmq.workqueue;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author 白起老师
 * 消费者
 */
public class SMSSender1 {

    public static void main(String[] args) throws IOException {


        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();

        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);

        //如果不写basicQos(1),则自动MQ会将所有请求平均发送给所有消费者
        //basicQos,MQ不再对消费者一次发送多个请求,而是消费者处理完一个消息后(确认后),在从队列中获取一个新的
        channel.basicQos(1);//处理完一个取一个

        channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("SMSSender1-短信发送成功:" + jsonSMS);

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }


}

消费者2:

package com.baiqi.rabbitmq.workqueue;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

/**
 * @author 白起老师
 * 消费者
 */
public class SMSSender2 {

    public static void main(String[] args) throws IOException {


        Connection connection = RabbitUtils.getConnection();
        final Channel channel = connection.createChannel();

        channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);

        //如果不写basicQos(1),则自动MQ会将所有请求平均发送给所有消费者
        //basicQos,MQ不再对消费者一次发送多个请求,而是消费者处理完一个消息后(确认后),在从队列中获取一个新的
        channel.basicQos(1);//处理完一个取一个

        channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String jsonSMS = new String(body);
                System.out.println("SMSSender2-短信发送成功:" + jsonSMS);

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                channel.basicAck(envelope.getDeliveryTag() , false);
            }
        });
    }
}

三、Pub/Sub 订阅模式

图解:

代码:

注:此代码提前创建号交换机

发布者:


public class WeatherBureau {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtils.getConnection();
        //键盘输入要发送的信息
        String input=new Scanner(System.in).next();
        //获取一个通道
        Channel channel = connection.createChannel();
        //发送消息
        channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER , "" , null , input.getBytes());
        //关闭连接
        channel.close();
        connection.close();
    }
}

接收者1:

public class Baidu {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER, "");
        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("百度天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

接收者2:

public class Sina {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_SINA,  false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "");
        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("新浪天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

四、Routing 路由模式

图解:

注:此代码提前创建号交换机

 

代码:

发送者:

package com.baiqi.rabbitmq.routing;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

public class WeatherBureau {
    public static void main(String[] args) throws Exception {
        //创建一个存储省市的map集合
        //前面这个key充当消息的routing key
        Map area=new LinkedHashMap<String,String>();
        area.put("beijing","北京");
        area.put("shanghai","上海");
        area.put("guangzhou","广州");
        area.put("shenzhen","深圳");
        area.put("chongqing","重庆");
        area.put("hangzhou","杭州");
        area.put("wuhan","武汉");
        area.put("ningbo","宁波");


        Connection connection = RabbitUtils.getConnection();
        //键盘输入要发送的信息
        String input=new Scanner(System.in).next();
        //获取一个通道
        Channel channel = connection.createChannel();
        //发送消息
        //1.获取迭代器
        Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
        //2.遍历集合
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            //发送消息
            channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_ROUTING , entry.getKey() , null , entry.getValue().getBytes());
        }
        //关闭连接
        channel.close();
        connection.close();
    }
}

接收者1:

package com.baiqi.rabbitmq.routing;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Baidu {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER, "chongqing");
        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("百度天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

接收者2:

package com.baiqi.rabbitmq.routing;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Sina {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_SINA,  false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "beijing");
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "shanghai");
        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("新浪天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

五、Topics 通配符模式

Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符

Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert

图解:

注:此代码提前创建号交换机

代码:

package com.baiqi.rabbitmq.topic;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

public class WeatherBureau {
    public static void main(String[] args) throws Exception {
        //创建一个存储省市的map集合
        //前面这个key充当消息的routing key
        Map area=new LinkedHashMap<String,String>();
        area.put("China.huhan.changsha.20250101", "中国-湖南-长沙-2025年1月1日");
        area.put("China.guangdong.guangzhou.20250101", "中国-广东-广州-2025年1月1日");
        area.put("China.guangdong.shenzhen.20250101", "中国-广东-深圳-2025年1月1日");
        area.put("China.jiangsu.nanjing.20250101", "中国-江苏-南京-2025年1月1日");


        Connection connection = RabbitUtils.getConnection();
        //键盘输入要发送的信息
        String input=new Scanner(System.in).next();
        //获取一个通道
        Channel channel = connection.createChannel();
        //发送消息
        //1.获取迭代器
        Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
        //2.遍历集合
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            //发送消息
            channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC , entry.getKey() , null , entry.getValue().getBytes());
        }
        //关闭连接
        channel.close();
        connection.close();
    }
}

消费者1:

package com.baiqi.rabbitmq.topic;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Sina {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_SINA,  false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "China.#");

        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("新浪天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

消费者2:

package com.baiqi.rabbitmq.topic;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;

public class Baidu {
    public static void main(String[] args) throws Exception
    {
        //获取TCP长连接
        Connection connection = RabbitUtils.getConnection();
        //创建通信“通道”,相当于TCP中的虚拟连接
        final Channel channel = connection.createChannel();
        //创建队列
        channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
        //绑定队列到交换机,同时指定需要匹配的routing key。
        channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "*.*.*.20250101");
        //设置每次发送数据个数
        channel.basicQos(1);
        //接收数据
        channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
            @Override
            // 消息到达,触发这个方法
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("百度天气收到气象信息:" + new String(body));
                // 手动ACK
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        });
    }
}

总结

1. 简单模式 HelloWorld

      一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)。

2. 工作队列模式 Work Queue

      一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)。

3. 发布订阅模式 Publish/subscribe
需要设置类型为 fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列。
4. 路由模式 Routing
需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。

5. 通配符模式 Topic
需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。

消息确认机制

Broker:RabbitMQ 

package com.baiqi.rabbitmq.confirm;

import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

public class WeatherBureau {
    public static void main(String[] args) throws Exception {
        //创建一个存储省市的map集合
        //前面这个key充当消息的routing key
        Map area=new LinkedHashMap<String,String>();
        area.put("China.huhan.changsha.20250101", "中国-湖南-长沙-2025年1月1日");
        area.put("China.guangdong.guangzhou.20250101", "中国-广东-广州-2025年1月1日");
        area.put("China.guangdong.shenzhen.20250101", "中国-广东-深圳-2025年1月1日");
        area.put("China.jiangsu.nanjing.20250101", "中国-江苏-南京-2025年1月1日");

        //获取连接
        Connection connection = RabbitUtils.getConnection();
        //获取一个通道
        Channel channel = connection.createChannel();

        //开启confirm监听模式
        channel.confirmSelect();
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleAck(long l, boolean b) throws IOException {
                //第二个参数表示是否批量接收
                System.out.println("消息已经被Broker接收:" + l);
            }

            @Override
            public void handleNack(long l, boolean b) throws IOException {
                System.out.println("消息被Broker拒绝接收:" + l);
            }
        });

        channel.addReturnListener(new ReturnCallback() {
            @Override
            public void handle(Return aReturn) {
              System.err.println("Return编码:" + aReturn.getReplyCode()+ " Return信息:" + aReturn.getReplyText());
              System.err.println("交换机:" + aReturn.getExchange()+ " RoutingKey:" + aReturn.getRoutingKey());
              System.err.println("消息:" + new String(aReturn.getBody()));
            }
        });

        Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            //发送消息
            //第三个参数表示是否持久化,如果为false,消息会存到内存中,重启后丢失,直接将消息放弃
            channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC , entry.getKey() ,true, null , entry.getValue().getBytes());
        }

        //不能关,保持监听
        /*channel.close();
        connection.close();*/
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absinthe_苦艾酒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值