Redis的集群模式

Redis Cluster是Redis的分布式解决方案,在3.0版本正式推出。当遇到单机内存、并发、流量等瓶颈时,可 以采用Cluster架构方案达到负载均衡的目的。
想直接看简单的安装,直接拉到最后面看C语言的工具安装。不过我建议整体看一遍,然后选择使用工具安装

1. 几个概念

1.1 数据分布理论

分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的 问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。一般分为哈希分区和顺序分区:

  • 哈希分区
    • 离散度好;数据分布与业务无关;无法顺序访问;
    • 代表有:Redis Cluster;Cassandra;Dynamo;
  • 顺序分区
    • 离散容易倾斜;数据分布业务相关;可顺序访问
    • 代表有: Bigtable; HBase;Hypertable;
1.2 常见的Hash分区规则
  1. 节点区域分区
    使用特定的数据,比如Redis的key,然后通过公式hash(key)%N计算出哈希值,在决定映射到哪个节点;

  2. 一致性哈希分区
    为系统中每个节 点分配一个token,范围一般在0~232,这些token构成一个哈希环。数据读写 执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于 等于该哈希值的token节点。

  3. 虚拟槽分区
    虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有 数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围 一般远远大于节点数,比如Redis Cluster槽范围是0~16383。槽是集群内数据 管理和迁移的基本单位。采用大范围槽的主要目的是为了方便数据拆分和集 群扩展。每个节点会负责一定数量的槽。


    image.png

    当前集群有5个节点,每个节点平均大约负责3276个槽。由于采用高质 量的哈希算法,每个槽所映射的数据通常比较均匀,将数据平均划分到5个 节点进行数据分区。Redis Cluster就是采用虚拟槽分区。

Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整 数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部 分槽以及槽所映射的键值数据

2. 搭建集群

2.1 普通搭建

这里只是为了演示,所以集群的主在一台机子上,备在一台机子上,按理来说,是需要在不同的机子上的,这个主要在节点握手的时候IP控制就可以了。

2.1.1. 准备节点
  1. 安装redis
    因为是CentOS,所以可以通过yum安装,yum install redis
    安装完成后,redis的配置文件在/etc下面,但是暂时使用
  2. 准备3个redis文件
    分别使用6910, 6911, 6912端口,在/etc/redis文件夹下
# redis-6910.conf
bind 0.0.0.0
port 6910
#logfile=/data/redis/logs/redis-6910.log
logfile /data/redis/logs/redis-6910.log
dir /data/redis/data/6910
dbfilename dump.rdb
daemonize yes

pidfile /var/run/redis_6910.pid
# 密码
requirepass 123456

# 集群配置
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 1500
# 集群内部配置文件
cluster-config-file /data/redis/conf/node-6910.conf


# redis-6911.conf
bind 0.0.0.0
port 6911
logfile /data/redis/logs/redis-6911.log
dir /data/redis/data/6911
dbfilename dump.rdb
daemonize yes

pidfile /var/run/redis_6911.pid
# 密码
requirepass 123456

# 集群配置
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 1500
# 集群内部配置文件
cluster-config-file /data/redis/conf/node-6911.conf

# redis-6912.conf
bind 0.0.0.0
port 6912
logfile /data/redis/logs/redis-6912.log
dir /data/redis/data/6912
dbfilename dump.rdb
daemonize yes

pidfile /var/run/redis_6912.pid
# 密码
requirepass 123456

# 集群配置
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 1500
# 集群内部配置文件
cluster-config-file /data/redis/conf/node-6912.conf

配置文件准备好后,启动redis,分别执行以下命令:

redis-server /etc/redis/redis-6910.conf
redis-server /etc/redis/redis-6911.conf
redis-server /etc/redis/redis-6912.conf

在启动之后,在/data/redis/conf/下会有三个文件,这三个文件是集群的配置文件,不要手动更改,在集群发生变化的时候,会自动去更新:

5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected
2.1.2. 节点握手

登录其中一台redis:redis-cli -h 127.0.0.1 -p 6910
登录后通过密码认证:auth 123456
先查看集群状态:

cluster nodes
####显示内容,只有一台#####
5ede6b4b1368afa21bc400968e614e6ed68d1d44 :6910 myself,master - 0 0 0 connected

节点握手

cluster meet 127.0.0.1 6911
cluster meet 127.0.0.1 6912
# 这时候再查看节点状态
cluster nodes
####显示内容,有三台了####
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607417226801 0 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607417227001 1 connected

# 查看集群状态
cluster info
# 这时候集群是不可用的,禁止读写,因为还没有分配槽
###显示内容###
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:0
cluster_current_epoch:2
cluster_my_epoch:2
cluster_stats_messages_sent:43
cluster_stats_messages_received:43
2.1.3. 分配槽

Redis集群把所有的数据映射到16384个槽中。每个key会映射为一个固 定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过 cluster addslots命令为节点分配槽。

# 注意命令里是两个.不是3个
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {5462..10922}
redis-cli -h 127.0.0.1 -p 6910 -a 123456 cluster addslots {10923..16383}

这时候再查看集群状态,发现已经是可用了:

redis-cli -h 127.0.0.1 -p 6910 -a 123456
cluster info
# 显示内容
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3
cluster_size:1
cluster_current_epoch:2
cluster_my_epoch:2
cluster_stats_messages_sent:14120
cluster_stats_messages_received:14120
2.1.4 备节点
  1. 同样先准备节点,我在另一台服务器上准备了3个redis,端口分别以6920, 6921, 6922来启动,配置文件和上面的类似,这里放一个配置文件:
bind 0.0.0.0
port 6920
#logfile=/data/redis/logs/redis-6920.log
logfile /data/redis/logs/redis-6920.log
dir /data/redis/data/6920
dbfilename dump.rdb
daemonize yes

pidfile /var/run/redis_6920.pid
# 密码
requirepass 123456

# 集群配置
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 1500
# 集群内部配置文件
cluster-config-file /data/redis/conf/node-6920.conf
  1. 启动每台redis:
redis-server /etc/redis/redis-6920.conf
redis-server /etc/redis/redis-6921.conf
redis-server /etc/redis/redis-6922.conf
  1. 加入到集群中,在之前已经起来的redis集群中把这几台也加入进来,使用meet命令:
cluster meet 120.45.59.183 6920
cluster meet 120.45.59.183 6921
cluster meet 120.45.59.183 6922

# 查看节点状态
cluster nodes
d5eac57d1e4755cc9b0d73f1e2a642020a44edb7 120.45.59.183:6922 master - 0 1607426875916 0 connected
1d3bdedff56c924f6f759c90c3cdab25535ac4a0 120.45.59.183:6921 master - 0 1607426875515 0 connected
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected 0-16383
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607426875507 3 connected
ff2c70be220996e80eca74b4cd6f932412a880fa 120.45.59.183:6920 master - 0 1607426875716 0 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607426875908 1 connected

127.0.0.1:6910> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:1
cluster_current_epoch:3
cluster_my_epoch:2
cluster_stats_messages_sent:62442
cluster_stats_messages_received:62442
  1. 登录每一个备节点redis,使用cluster replicate{nodeId}命令让一个节点成为从节点。其中命令执行必须在对应的 从节点上执行,nodeId是要复制主节点的节点ID
redis-cli -h 127.0.0.1 -p 6920 -a 123456
cluster replicate 5ede6b4b1368afa21bc400968e614e6ed68d1d44
redis-cli -h 127.0.0.1 -p 6921 -a 123456
cluster replicate 0c04c840b911c96ad2f1e28f9c12323155d313f7
redis-cli -h 127.0.0.1 -p 6922 -a 123456
cluster replicate 685509c1b4c3c765f459b336d4995dbca2f938ae

# 再查看节点情况,可以看到有主从节点了
cluster nodes
d5eac57d1e4755cc9b0d73f1e2a642020a44edb7 120.45.59.183:6922 slave 685509c1b4c3c765f459b336d4995dbca2f938ae 0 1607427288089 3 connected
1d3bdedff56c924f6f759c90c3cdab25535ac4a0 120.45.59.183:6921 slave 0c04c840b911c96ad2f1e28f9c12323155d313f7 0 1607427288289 1 connected
5ede6b4b1368afa21bc400968e614e6ed68d1d44 127.0.0.1:6910 myself,master - 0 0 2 connected 0-16383
685509c1b4c3c765f459b336d4995dbca2f938ae 127.0.0.1:6912 master - 0 1607427288481 3 connected
ff2c70be220996e80eca74b4cd6f932412a880fa 120.45.59.183:6920 slave 5ede6b4b1368afa21bc400968e614e6ed68d1d44 0 1607427288490 2 connected
0c04c840b911c96ad2f1e28f9c12323155d313f7 127.0.0.1:6911 master - 0 1607427288681 1 connected

到此为止,集群就搭建完毕了。如果要更多的备节点或者主节点,都可以类似的添加。但是在搭建完成后,数据落进去之后再进行扩容,那就要进行槽迁移、数据迁移等一系列工作了。这里先不说了。

2.2 利用工具搭建

上面搭建的方式确实是可以把集群搭建起来,但是怎么看也是有些繁琐的,所以会有使用工具搭建。
使用工具搭建的话可以使用redis-trib.rb,这是使用Ruby开发的(Ruby是一个很有意思的语言)。

  1. 安装ruby的环境
    这个环境在不同的系统又不同的方法,具体可以查看官网(中文网:http://www.ruby-lang.org/zh_cn/documentation/installation/#yum),比如CentOS可以选择下载后编译安装,也可以通过yum安装。但是不推荐,因为yum安装的ruby版本很低,下面只是一个示例,还是推荐编译安装。
# centOS
sudo yum install ruby
# macos
brew install ruby

在安装完ruby之后,就可以使用ruby的包管理工具gem,这个类似于yum的工具;通过ruby的gem工具安装redis依赖(可能要等好久):

gem install redis

编译安装:

# 解决安装的依赖库
yum install gcc gcc-c++ gdbm-devel readline-devel openssl-devel wget -y
# 下载源码包
wget https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.gz
# 解压
tar -zxvf ruby-2.7.2.tar.gz
# 编译安装
cd ruby-2.7.2/
./configure --prefix=/usr/local/ruby --enable-shared
make && make install
# 设置环境变量
vim /etc/profile
# 在最下面增加
export RUBY_HOME=/usr/local/ruby
export PATH=$PATH:$RUBY_HOME/bin:$RUBY_HOME/lib
# 保存后执行
source /etc/profile
# 验证安装
ruby -v
gem -v
# 安装ruby的redis库,可能会有点慢
gem install redis
# 安装后显示内容
Fetching redis-4.2.5.gem
Successfully installed redis-4.2.5
Parsing documentation for redis-4.2.5
Installing ri documentation for redis-4.2.5
Done installing documentation for redis after 0 seconds
1 gem installed

然后问题来了,我的redis是yum安装的,一直找不到redis-trib.rb,然后我就又去下载了一个源码版,在里面就找到了,下载的时候根据yum安装的redis版本来下载的:

wget https://download.redis.io/releases/redis-3.2.12.tar.gz
# 解压
tar -zxvf redis-3.2.12.tar.gz
# 复制文件
cp ./redis-3.2.12/src/redis-trib.rb   /usr/bin

接下来同样是要准备节点,准备节点后只要将每个节点启动就行了,然后节点握手和分配槽就是集群管理工具来做。
准备节点的步骤和之前是一样的,如果已经按照上面的步骤做过,那就把每一台redis停掉,然后删除掉redis的集群信息(/data/conf下的文件),再启动就可以了

# 集群创建,这里的--replicas 1的意思是一个从节点,然后后面是所有的redis的ip和端口,会自己分配槽,自己握手
redis-trib.rb create --replicas 1 120.55.134.85:6910 120.55.134.85:6911 120.55.134.85:6912 122.58.124.87:6920 122.58.124.87:6921 122.58.124.87:6922

# 这时候我遇到了2个问题,1个是用的云服务器,没有对自己放开端口,也就是自己访问自己的公网IP访问不了,导致创建失败;
一个是之前我设置了redis密码,创建集群的时候不支持密码,所以先要把密码去掉启动后再设置。当然也可以通过修改ruby源码连设置密码
# 集群创建后的显示内容
Using 3 masters:
120.55.134.85:6910
122.58.124.87:6920
120.55.134.85:6911
Adding replica 122.58.124.87:6921 to 120.55.134.85:6910
Adding replica 120.55.134.85:6912 to 122.58.124.87:6920
Adding replica 122.58.124.87:6922 to 120.55.134.85:6911
M: 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6 120.55.134.85:6910
   slots:0-5460 (5461 slots) master
M: 3640a0e3f5c2607053ace32a1e2f192a13b96b48 120.55.134.85:6911
   slots:10923-16383 (5461 slots) master
S: 26f7831c0e1ee4ec2fab2987e57fbe02ae050934 120.55.134.85:6912
   replicates 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6
M: 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6 122.58.124.87:6920
   slots:5461-10922 (5462 slots) master
S: 60f1726c401c977c9c1ef4610dd6db05a3688f89 122.58.124.87:6921
   replicates 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6
S: 46d3c7a05979e29edd34b53cf3138d331805f818 122.58.124.87:6922
   replicates 3640a0e3f5c2607053ace32a1e2f192a13b96b48
Can I set the above configuration? (type 'yes' to accept):
# 输入yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join.
>>> Performing Cluster Check (using node 120.55.134.85:6910)
M: 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6 120.55.134.85:6910
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 26f7831c0e1ee4ec2fab2987e57fbe02ae050934 120.55.134.85:6912
   slots: (0 slots) slave
   replicates 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6
S: 46d3c7a05979e29edd34b53cf3138d331805f818 122.58.124.87:6922
   slots: (0 slots) slave
   replicates 3640a0e3f5c2607053ace32a1e2f192a13b96b48
M: 7799daaec5f3ce5c3b2c1f64446fc133d1f20de6 122.58.124.87:6920
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
M: 3640a0e3f5c2607053ace32a1e2f192a13b96b48 120.55.134.85:6911
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 60f1726c401c977c9c1ef4610dd6db05a3688f89 122.58.124.87:6921
   slots: (0 slots) slave
   replicates 66d6f3ef1523226d64b4ef3baaf8b12f07f961e6
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

# 加上密码
# 登录每一个节点,每个节点密码需要一样
redis-cli -h 122.58.124.87 -p 6920
config set masterauth 123456
config set requirepass 123456
auth 123456
config rewrite

到这里为止,使用工具搭建集群也就完成了。
但是,为了搭建一个集群还要装一个ruby的环境,有点蠢是不是?
所以在redis5.x的版本中,搭建集群的工具就是使用C去写的了,不用再通过ruby环境去搭建了,所以接下来写一下redis5.x的集群搭建方式:

2.3 redis5.x利用工具搭建集群

首先,yum安装redis默认安装的还是3.x的版本,我们需要安装redis5.x或以上版本,一种是通过编译安装,还有一种是修改存储库的方式,那就2个方式都说一下,一台修改存储库,一台通过编译安装:

1. 通过修改存储库安装
# 安装redis4/5版本通过IUS存储库(仅支持redhat/centos)
# 1. 安装 epel repo
yum install -y epel-release bash-completion
# 2. 安装 IUS repo
yum install -y https://repo.ius.io/ius-release-el7.rpm https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# 3.  安装 redis5
yum install -y redis5

然后向上面一样,启动就可以了:redis-server /etc/redis/redis-6910.conf

2. 编译安装
# 安装依赖包
yum -y install gcc
yum -y install libc
# 下载
wget https://download.redis.io/releases/redis-5.0.10.tar.gz?_ga=2.194157549.1777432635.1607479744-1380992581.1607479744
# 解压安装
tar -zxvf wget https://download.redis.io/releases/redis-5.0.10.tar.gz?_ga=2.194157549.1777432635.1607479744-1380992581.1607479744
mv redis-5.0.10/ /usr/bin/redis
cd /usr/bin/redis
make && make install
# 安装完毕

然后向之前一样,启动各个节点:
redis-server /etc/redis/redis-6920.conf

3. 使用redis5中自带的集群管理工具创建集群
redis-cli --cluster create 120.55.134.85:6910 120.55.134.85:6911 120.55.134.85:6912 122.58.124.87:6920 122.58.124.87:6921 122.58.124.87:6922 --cluster-replicas 1
# 详细的使用可以搜索redis-cli --cluster
# 然后每个节点设置密码,我是推荐集群也是要加上密码的
config set masterauth 123456
config set requirepass 123456
auth 123456
config rewrite

到此为止,redis5.x利用自带的C语言写的集群管理器搭建也就完成了

3. SpringBoot使用

1. 添加相关的依赖
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    // 连接池需要的
    implementation 'org.apache.commons:commons-pool2'
2. 配置文件添加redis集群信息
spring:
  redis:
    timeout: 6000ms
    password: 123456
    cluster:
      max-redirects: 3 # 获取失败 最大重定向次数
      nodes:
        - 120.55.134.85:6910
        - 120.55.134.85:6911
        - 120.55.134.85:6912
        - 122.58.124.87:6920
        - 122.58.124.87:6921
        - 122.58.124.87:6922
    lettuce:
      pool:
        max-active: 1000 # 连接池最大连接数
        max-idle: 10  # 连接池最大空闲数
        min-idle: 5 # 连接池最小空闲数
        max-wait: -1  # 连接池最大阻塞等待时间
3. 测试类编写
@SpringBootTest
class RedisClusterDemoApplicationTests {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("hello3", "world");
        redisTemplate.opsForValue().set("hugh3", "22");
        redisTemplate.opsForValue().set("name3", "1990");
        redisTemplate.opsForList().leftPush("number2", "001");
        redisTemplate.opsForList().leftPush("number2", "002");
        redisTemplate.opsForList().leftPush("number2", "003");
    }

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

推荐阅读更多精彩内容