RabbitMQ的几种常见模式的详细介绍和使用---实践

RabbitMQ的几种常见模式的详细介绍和使用---实践

官网:RabbitMQ

官方文档:各个模式简介

RabbitMQ就不详细介绍了,以下就是各个模式的原理和实践操作:

1.安装配置

查看mq镜像: docker search rabbitmq:management
下载mq镜像: docker pull rabbitmq:management
安装镜像:docker run -d --name rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management

说明
5672:默认的客户端连接的端口
15672:默认的web管理界面的端口
 命令中的【RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin】是web管理平台的用户名和密码
【 -p 15672:15672】 是控制平台docker映射到系统的对应端口
【 -p 5672:5672】 是应用程序的访问端口

访问地址
http://ip:15672

如果是linux服务器,首先开放服务器端口,例如阿里云,先配置安全组:

image-20210926180156983

添加:

image-20210926180312617

开始安装:

查询 docker search rabbitmq:management

image-20210926175821524

下载 docker search rabbitmq:management

安装 docker run -d --name rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management

image-20210926175859729

安装成功

image-20210926180008591

访问地址:
http://你的ip:15672

2.测试

先创建一个连接类:

package boot.spring.controller;

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * @description
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class ConnectionUtil {
    /**
     * 获取连接
     * @return Connection
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("你的ip");
        factory.setPort(5672);
        //设置vhost
        factory.setVirtualHost("/");
        factory.setUsername("admin");
        factory.setPassword("admin");
        //通过工厂获取连接
        Connection connection = factory.newConnection();
        return connection;
    }
}

2.1简单模式

一个生产者,一个消费者。

原理图:
image-20210927094435976

发送:

package boot.spring.controller.easy;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * @description 1.简单模式:一个生产者一个消费者
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TestSend {
    public final static String QUEUE_NAME = "test-queue";
    //创建队列,发送消息
    public static void main(String[] args) throws Exception {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明创建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //消息内容
        String message = "Hello World!";
        channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
        System.out.println("发送消息:"+message);
        //关闭连接和通道
        channel.close();
        connection.close();
    }

}

image-20210926185732305

生产的一条消息未被消费:

image-20210926185843146

接收:

package boot.spring.controller.easy;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 简单模式一个生产者一个消费者
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TestResive {
    //消费者消费消息
    public static void main(String[] args) throws Exception {
        //获取连接和通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明通道
        channel.queueDeclare(TestSend.QUEUE_NAME,false,false,false,null);
        //定义消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //监听队列
        channel.basicConsume(TestSend.QUEUE_NAME,true,consumer);

        while(true){
            //这个方法会阻塞住,直到获取到消息
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("接收到消息:"+message);
        }
    }
}

image-20210926185917182

已被消费:

image-20210926185945971
2.2 work模式

竞争消费者模式

一个生产者,多个消费者,每个消费者获取到的消息唯一,生产的消息会被消费者瓜分。

原理图:
image-20210927094551158

生产100条消息:

package boot.spring.controller.work;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * @description 2.work模式:一个生产者多个消费者
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class WorkSend2 {
    public final static String QUEUE_NAME = "test2";
    //消息生产者
    public static void main(String[] args) throws Exception {
        //获取连接和通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        String message = "";
        for(int i = 0; i<100; i++){
            message = "" + i;
            channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
            System.out.println("发送消息:"+message);
            Thread.sleep(i);
        }

        channel.close();
        connection.close();
    }
}
image-20210927095421093

消费者1:

package boot.spring.controller.work;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 2.work模式:一个生产者多个消费者
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class WorkResive1 {

    //消费者1  自动模式
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(WorkSend2.QUEUE_NAME,false,false,false,null);

        //同一时刻服务器只发送一条消息给消费端
        channel.basicQos(1);

        QueueingConsumer consumer = new QueueingConsumer(channel);

        channel.basicConsume(WorkSend2.QUEUE_NAME,false,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("recive1:"+message);
            Thread.sleep(100);
            //消息消费完给服务器返回确认状态,表示该消息已被消费
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
        }
    }
}

消费者1,消费了100条消息中的一半:

image-20210927095602819

消费者2:

package boot.spring.controller.work;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 2.work模式:一个生产者多个消费者
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class WorkResive2 {

    //消费者2  手动模式
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("test2",false,false,false,null);

        channel.basicQos(1);

        QueueingConsumer consumer = new QueueingConsumer(channel);

        channel.basicConsume("test2",true,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("recive1:"+message);
            Thread.sleep(10);
            //channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
        }
    }
}

消费者2消费了100条消息的另一半:

image-20210927095726835
2.3 订阅模式

生产者将消息发送到交换机,消费者从交换机获取消息。

原理图:
image-20210927100245937

生产者发送消息到交换机:

package boot.spring.controller.exchange;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * @description 3.订阅者模式:一个生产者发送的消息会被多个消费者获取
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class Send {
    public static final String EXCHANGE_NAME = "test_exchange_fanout";
    //生产者,发送消息到交换机
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机 fanout:交换机类型 主要有fanout,direct,topics三种
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");

        String message = "订阅模式:消息007!";
        channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
        System.out.println(message);
        channel.close();
        connection.close();
    }
}

生产者产生的消息:

image-20210927101447291

消费者1:

package boot.spring.controller.exchange;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 3.订阅者模式:一个生产者发送的消息会被多个消费者获取
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class Resive1 {
    //消费者1
    public final static String QUEUE_NAME = "test_queue_exchange_1";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上
        channel.queueBind(QUEUE_NAME,Send.EXCHANGE_NAME,"");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);
        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("消费者1:"+message);
        }
    }
}
image-20210927101551786

消费者2:

package boot.spring.controller.exchange;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 3.订阅者模式:一个生产者发送的消息会被多个消费者获取
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class Resive2 {
    //消费者2
    public final static String QUEUE_NAME = "test_queue_exchange_2";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上
        channel.queueBind(QUEUE_NAME,Send.EXCHANGE_NAME,"");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("消费者2:"+message);
        }
    }
}
image-20210927101633853

由此可见,订阅者模式中,所有的消费者都通过交换机收到了消息。

2.4 路由模式

生产者发送消息到队列中时可自定义一个key,消费者可根据key去选择对应的消息,各取所需。
注意:路由key,是一种完全匹配,只有匹配到的消费者才能消费消息。

原理图:
image-20210927102501361

生产者生产带key的消息:(key=“dog”)

package boot.spring.controller.rout;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * @description 4.路由模式:发送消息到交换机并且要指定路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class RoutSend {
    public static final String EXCHANGE_NAME = "test_exchange_direct";
    //生产者
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机 fanout:交换机类型 主要有fanout,direct,topics三种
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");

        String message = "路由模式产生的消息!";
        channel.basicPublish(EXCHANGE_NAME,"dog",null,message.getBytes());
        System.out.println(message);
        channel.close();
        connection.close();
    }
}
image-20210927102827292

消费者1:(key=“dog”)

package boot.spring.controller.rout;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 4.路由模式:消费者将队列绑定到交换机时需要指定路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class RoutResive1 {

    //消费者1
    public final static String QUEUE_NAME = "test_queue_direct_1";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并制定路由键为"dog"
        channel.queueBind(QUEUE_NAME, RoutSend.EXCHANGE_NAME,"dog");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);
        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("RoutResive1:"+message);
        }
    }
}
image-20210927103033299

消费者2:(key=“cat”)

package boot.spring.controller.rout;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 4.路由模式:消费者将队列绑定到交换机时需要指定路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class RoutResive2 {

    //消费者2
    public final static String QUEUE_NAME = "test_queue_direct_2";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并制定路由键为"cat"
        channel.queueBind(QUEUE_NAME, RoutSend.EXCHANGE_NAME,"cat");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);
        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("RoutResive2:"+message);
        }
    }
}
image-20210927103119368

很显然,消费者1获取到了消息,消费者2并没有获取到消息,因为消费者2的key与生产者的key不一致。

2.5 通配符模式

原理和路由模式类似,只是key值作了模糊匹配而已。

  • *(星号)可以正好代替一个词。

  • # (hash) 可以代替零个或多个单词

  • topic交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“”。#匹配0个或多个单词,匹配一个单词。如下图所示:

    20180628164513643

原理图:
image-20210927103418777

生产者产生消息:

package boot.spring.controller.topic;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * @description 5.路由模式:发送消息到交换机并且要指定通配符路由
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TopicSend {
    //生产者
    public static final String EXCHANGE_NAME = "test_exchange_topic";

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机 topic:交换机类型
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
        String message = "通配符模式产生的消息";
        channel.basicPublish(EXCHANGE_NAME,"dog.1",null,message.getBytes());
        System.out.println(message);
        channel.close();
        connection.close();
    }
}
image-20210927104718676

消费者1:

package boot.spring.controller.topic;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 5.路由模式:消费者将队列绑定到交换机时需要指定通配符路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TopicResive1 {

    //消费者1
    public final static String QUEUE_NAME = "test_queue_topic_1";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并制定路由键匹配规则为"dog.*"
        channel.queueBind(QUEUE_NAME, TopicSend.EXCHANGE_NAME,"dog.*");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("TopicResive1:"+message);
        }
    }
}
image-20210927104759387

消费者2:

package boot.spring.controller.topic;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 5.路由模式:消费者将队列绑定到交换机时需要指定通配符路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TopicResive2 {

    //消费者2
    public final static String QUEUE_NAME = "test_queue_topic_2";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并制定路由键匹配规则为"#.1"
        channel.queueBind(QUEUE_NAME, TopicSend.EXCHANGE_NAME,"#.1");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("TopicResive2:"+message);
        }
    }
}
image-20210927104900197

消费者3:

package boot.spring.controller.topic;

import boot.spring.controller.ConnectionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;

/**
 * @description 5.路由模式:消费者将队列绑定到交换机时需要指定通配符路由key
 * @AUTHER: sk
 * @DATE: 2021/9/26
 **/

public class TopicResive3 {

    //消费者3
    public final static String QUEUE_NAME = "test_queue_topic_3";
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //绑定队列到交换机上,并制定路由键匹配规则为"cat.#"
        channel.queueBind(QUEUE_NAME, TopicSend.EXCHANGE_NAME,"cat.#");
        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        channel.basicConsume(QUEUE_NAME,true,consumer);

        while(true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("TopicResive3:"+message);
        }
    }
}
image-20210927104935796

结果:消费者1和消费者2可以收到消息,消费者3不能收到消息。

完整代码地址:

https://github.com/DongFangXiaoYu/springBoot-RabbitMq/tree/master

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容