docker 部署 MySQL Group Replication-MGR集群

简介

创建容错系统的最常见方法是使组件冗余,换句话说,可以删除组件,系统应继续按预期运行。这带来了一系列挑战,将此类系统的复杂性提升到一个完全不同的水平。具体而言,复制的数据库必须处理这样一个事实,即它们需要维护和管理多个服务器,而不仅仅是一个服务器。此外,由于服务器正在协同创建组,因此必须处理其他几个经典的分布式系统问题,例如网络分区或裂脑场景。

因此,最终的挑战是将数据库和数据复制的逻辑与以一致和简单的方式协调多个服务器的逻辑融合在一起。换句话说,让多个服务器就系统的状态以及系统经历的每个更改的数据达成一致。这可以概括为让服务器就每个数据库状态转换达成协议,以便它们都作为单个数据库进行,或者它们最终收敛到相同的状态。这意味着它们需要作为(分布式)状态机运行。

MySQL 组复制提供分布式状态机复制,服务器之间具有强大的协调性。当服务器属于同一组时,它们会自动进行协调。该组可以在具有自动主选举的单主模式下运行,其中一次只有一台服务器接受更新。或者,对于更高级的用户,可以在多主模式下部署该组,其中所有服务器都可以接受更新,即使这些更新是同时发出的。这种能力是以牺牲应用程序必须绕过此类部署所施加的限制为代价的。

本文使用docker模拟三台mysql服务器,搭建一组单主多从集群。

系统环境

docker版本 20.10.11
MySQL镜像 mysql/mysql-server:8.0.20
节点01: 172.19.0.11:3311172.19.0.11:33011
节点02: 172.19.0.12:3312172.19.0.12:33012
节点03: 172.19.0.13:3313172.19.0.13:33013

创建网络:
docker network create --subnet=172.19.0.0/16 dev_network

docker配置文件

配置docker启动参数,后面方便启动mysql实例

version: "3"
services: 
  mysql-01:
    image: mysql/mysql-server:8.0.20
    container_name: mysql-01
    restart: always
    ports:
      - "3311:3306"
      - "33011:33011"
    environment:
      - MYSQL_ROOT_PASSWORD=mysql-666
    volumes:
      - ./data-01:/var/lib/mysql:cached
      - ./mysql-01.cnf:/etc/mysql/my.cnf:cached
    security_opt:
      - seccomp:unconfined
    logging: 
      driver: "json-file"
      options: 
        max-size: "500m"
    networks:
      dev_network:
        ipv4_address: 172.19.0.11

  mysql-02:
    image: mysql/mysql-server:8.0.20
    container_name: mysql-02
    restart: always
    ports:
      - "3312:3306"
      - "33012:33012"
    environment:
      - MYSQL_ROOT_PASSWORD=mysql-666
    volumes:
      - ./data-02:/var/lib/mysql:cached
      - ./mysql-02.cnf:/etc/mysql/my.cnf:cached
    security_opt:
      - seccomp:unconfined
    logging: 
      driver: "json-file"
      options: 
        max-size: "500m"
    networks:
      dev_network:
        ipv4_address: 172.19.0.12

  mysql-03:
    image: mysql/mysql-server:8.0.20
    container_name: mysql-03
    restart: always
    ports:
      - "3313:3306"
      - "33013:33013"
    environment:
      - MYSQL_ROOT_PASSWORD=mysql-666
    volumes:
      - ./data-03:/var/lib/mysql:cached
      - ./mysql-03.cnf:/etc/mysql/my.cnf:cached
    security_opt:
      - seccomp:unconfined
    logging: 
      driver: "json-file"
      options: 
        max-size: "500m"
    networks:
      dev_network:
        ipv4_address: 172.19.0.13

networks:
  dev_network:
    external:
      name: dev_network
配置文件 mysql-01.cnf

第一个节点的mysql配置信息,另外两个配置信息除了server-idloose-group_replication_local_addressreport_host 不一样,其他都需要保持一致

[client]
# 默认字符集
default-character-set=utf8mb4

[mysqld]
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default_authentication_plugin=mysql_native_password

# 最大连接数
max_connections=4096

# 日志到期自动删除
binlog_expire_logs_seconds=604800

# 对于组复制,数据必须存储在 InnoDB 事务性存储引擎,因此禁用以下引擎
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"

# server-id必须是唯一的
server-id=11

# 开启GTID,必须开启
gtid_mode=on

# 强制GTID的一致性
enforce_gtid_consistency=on

# binlog校验规则,8.0.21以后不需要设置
binlog_checksum=NONE

# binlog格式,MGR要求必须是ROW
binlog_format=row

# 生成各 Binlog 文件的前缀
log_bin=mysql-bin

# 因为集群会在故障恢复时互相检查binlog的数据, 所以需要记录下集群内其他服务器发过来已经执行过的binlog,按GTID来区分是否执行过.
log_slave_updates=on

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
master_info_repository=TABLE

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
relay_log_info_repository=TABLE

# 记录事务的算法,官网建议设置该参数使用 XXHASH64 算法
transaction_write_set_extraction=XXHASH64

# MGR使用乐观锁,所以官网建议隔离级别是RC,减少锁粒度
transaction_isolation=READ-COMMITTED

# 预装插件
plugin_load_add='group_replication.so'

# 相当于此GROUP的名字,是UUID值,不能和集群内其他GTID值的UUID混用,可用uuidgen来生成一个新的,
# 主要是用来区分整个内网里边的各个不同的GROUP,而且也是这个group内的GTID值的UUID
loose-group_replication_group_name='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'

# 是否随服务器启动而自动启动组复制,不建议直接启动,怕故障恢复时有扰乱数据准确性的特殊情况
loose-group_replication_start_on_boot=off

# 本地MGR的IP地址和端口,host:port,是MGR的端口,不是数据库的端口
loose-group_replication_local_address='172.19.0.11:33011'

# 需要接受本MGR实例控制的服务器IP地址和端口,是MGR的端口,不是数据库的端口
loose-group_replication_group_seeds='172.19.0.11:33011,172.19.0.12:33012,172.19.0.13:33013'

# 开启引导模式,添加组成员,用于第一次搭建MGR或重建MGR的时候使用,只需要在集群内的其中一台开启,
loose-group_replication_bootstrap_group=OFF

# 是否启动单主模式,如果启动,则本实例是主库,提供读写,其他实例仅提供读,如果为off就是多主模式了
loose-group_replication_single_primary_mode=ON

# 多主模式下,强制检查每一个实例是否允许该操作,如果不是多主,可以关闭
loose-group_replication_enforce_update_everywhere_checks=OFF

report_host=172.19.0.11
report_port=3306
配置文件 mysql-02.cnf
[client]
# 默认字符集
default-character-set=utf8mb4

[mysqld]
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default_authentication_plugin=mysql_native_password

# 最大连接数
max_connections=4096

# 日志到期自动删除
binlog_expire_logs_seconds=604800

# 对于组复制,数据必须存储在 InnoDB 事务性存储引擎,因此禁用以下引擎
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"

# server-id必须是唯一的
server-id=12

# 开启GTID,必须开启
gtid_mode=on

# 强制GTID的一致性
enforce_gtid_consistency=on

# binlog校验规则,8.0.21以后不需要设置
binlog_checksum=NONE

# binlog格式,MGR要求必须是ROW
binlog_format=row

# 生成各 Binlog 文件的前缀
log_bin=mysql-bin

# 因为集群会在故障恢复时互相检查binlog的数据, 所以需要记录下集群内其他服务器发过来已经执行过的binlog,按GTID来区分是否执行过.
log_slave_updates=on

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
master_info_repository=TABLE

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
relay_log_info_repository=TABLE

# 记录事务的算法,官网建议设置该参数使用 XXHASH64 算法
transaction_write_set_extraction=XXHASH64

# MGR使用乐观锁,所以官网建议隔离级别是RC,减少锁粒度
transaction_isolation=READ-COMMITTED

# 预装插件
plugin_load_add='group_replication.so'

# 相当于此GROUP的名字,是UUID值,不能和集群内其他GTID值的UUID混用,可用uuidgen来生成一个新的,
# 主要是用来区分整个内网里边的各个不同的GROUP,而且也是这个group内的GTID值的UUID
loose-group_replication_group_name='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'

# 是否随服务器启动而自动启动组复制,不建议直接启动,怕故障恢复时有扰乱数据准确性的特殊情况
loose-group_replication_start_on_boot=off

# 本地MGR的IP地址和端口,host:port,是MGR的端口,不是数据库的端口
loose-group_replication_local_address='172.19.0.12:33012'

# 需要接受本MGR实例控制的服务器IP地址和端口,是MGR的端口,不是数据库的端口
loose-group_replication_group_seeds='172.19.0.11:33011,172.19.0.12:33012,172.19.0.13:33013'

# 开启引导模式,添加组成员,用于第一次搭建MGR或重建MGR的时候使用,只需要在集群内的其中一台开启,
loose-group_replication_bootstrap_group=OFF

# 是否启动单主模式,如果启动,则本实例是主库,提供读写,其他实例仅提供读,如果为off就是多主模式了
loose-group_replication_single_primary_mode=ON

# 多主模式下,强制检查每一个实例是否允许该操作,如果不是多主,可以关闭
loose-group_replication_enforce_update_everywhere_checks=OFF

report_host=172.19.0.12
report_port=3306
配置文件 mysql-03.cnf
[client]
# 默认字符集
default-character-set=utf8mb4

[mysqld]
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default_authentication_plugin=mysql_native_password

# 最大连接数
max_connections=4096

# 日志到期自动删除
binlog_expire_logs_seconds=604800

# 对于组复制,数据必须存储在 InnoDB 事务性存储引擎,因此禁用以下引擎
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"

# server-id必须是唯一的
server-id=13

# 开启GTID,必须开启
gtid_mode=on

# 强制GTID的一致性
enforce_gtid_consistency=on

# binlog校验规则,8.0.21以后不需要设置
binlog_checksum=NONE

# binlog格式,MGR要求必须是ROW
binlog_format=row

# 生成各 Binlog 文件的前缀
log_bin=mysql-bin

# 因为集群会在故障恢复时互相检查binlog的数据, 所以需要记录下集群内其他服务器发过来已经执行过的binlog,按GTID来区分是否执行过.
log_slave_updates=on

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
master_info_repository=TABLE

# 基于安全的考虑,MGR集群要求复制模式要改成slave记录记录到表中,不然就报错
relay_log_info_repository=TABLE

# 记录事务的算法,官网建议设置该参数使用 XXHASH64 算法
transaction_write_set_extraction=XXHASH64

# MGR使用乐观锁,所以官网建议隔离级别是RC,减少锁粒度
transaction_isolation=READ-COMMITTED

# 预装插件
plugin_load_add='group_replication.so'

# 相当于此GROUP的名字,是UUID值,不能和集群内其他GTID值的UUID混用,可用uuidgen来生成一个新的,
# 主要是用来区分整个内网里边的各个不同的GROUP,而且也是这个group内的GTID值的UUID
loose-group_replication_group_name='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'

# 是否随服务器启动而自动启动组复制,不建议直接启动,怕故障恢复时有扰乱数据准确性的特殊情况
loose-group_replication_start_on_boot=off

# 本地MGR的IP地址和端口,host:port,是MGR的端口,不是数据库的端口
loose-group_replication_local_address='172.19.0.13:33013'

# 需要接受本MGR实例控制的服务器IP地址和端口,是MGR的端口,不是数据库的端口
loose-group_replication_group_seeds='172.19.0.11:33011,172.19.0.12:33012,172.19.0.13:33013'

# 开启引导模式,添加组成员,用于第一次搭建MGR或重建MGR的时候使用,只需要在集群内的其中一台开启,
loose-group_replication_bootstrap_group=OFF

# 是否启动单主模式,如果启动,则本实例是主库,提供读写,其他实例仅提供读,如果为off就是多主模式了
loose-group_replication_single_primary_mode=ON

# 多主模式下,强制检查每一个实例是否允许该操作,如果不是多主,可以关闭
loose-group_replication_enforce_update_everywhere_checks=OFF

report_host=172.19.0.13
report_port=3306
创建认证同步信息

进入mysql-01节点命令行,执行以下命令:

# 关闭日志
SET SQL_LOG_BIN=0;

# 创建用户
CREATE USER rpl_user@'%' IDENTIFIED BY 'mysql-888';

# 授权
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
FLUSH PRIVILEGES;

# 开启日志
SET SQL_LOG_BIN=1;

# 创建认证同步信息
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='mysql-888' FOR CHANNEL 'group_replication_recovery';

确认插件已经安装

mysql-01节点命令行,执行SHOW PLUGINS;确认已安装插件。
如果没有安装,可以执行INSTALL PLUGIN group_replication SONAME 'group_replication.so';安装。

mysql> SHOW PLUGINS;
+---------------------------------+----------+--------------------+----------------------+---------+
| Name                            | Status   | Type               | Library              | License |
+---------------------------------+----------+--------------------+----------------------+---------+
| binlog                          | ACTIVE   | STORAGE ENGINE     | NULL                 | GPL     |

(...)

| group_replication               | ACTIVE   | GROUP REPLICATION  | group_replication.so | GPL     |
+----------------------------+----------+--------------------+----------------------+-------------+
主节点引导组

mysql-01节点命令行,执行以下命令:

# 启动引导,注意,只有这个节点开启引导,其他两台都请忽略这一步
SET GLOBAL group_replication_bootstrap_group=ON;
# 开始同步
START GROUP_REPLICATION;
# 关闭引导
SET GLOBAL group_replication_bootstrap_group=OFF;

# 查看集群状态
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 7a2a180c-affc-11ec-a14f-0242ac13000b | 172.19.0.11 |        3306 | ONLINE       | PRIMARY     | 8.0.20         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
1 row in set (0.02 sec)
节点加入

mysql-02mysql-03节点命令行,执行以下命令:

# 关闭日志
SET SQL_LOG_BIN=0;

# 创建用户
CREATE USER rpl_user@'%' IDENTIFIED BY 'mysql-888';

# 授权
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
FLUSH PRIVILEGES;

# 开启日志
SET SQL_LOG_BIN=1;

# 创建认证同步信息
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='mysql-888' FOR CHANNEL 'group_replication_recovery';

# 开始同步
START GROUP_REPLICATION;

# 查看集群状态
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 7a2a180c-affc-11ec-a14f-0242ac13000b | 172.19.0.11 |        3306 | ONLINE       | PRIMARY     | 8.0.20         |
| group_replication_applier | a133b299-affe-11ec-8db8-0242ac13000d | 172.19.0.13 |        3306 | ONLINE       | SECONDARY   | 8.0.20         |
| group_replication_applier | d3042efc-affc-11ec-a414-0242ac13000c | 172.19.0.12 |        3306 | ONLINE       | SECONDARY   | 8.0.20         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)

数据同步测试

由于是搭建的单主模式,所以只能在mysql-01节点插入数据,然后数据会自动同步到mysql-02mysql-03节点中。
进入mysql-01节点命令行:

CREATE DATABASE test;
USE test;
CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
INSERT INTO t1 VALUES (1, 'Luis');

再进入mysql-02或者mysql-03节点命令行查询:

mysql> select * from test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
2 rows in set (0.01 sec)
开启远程访问
use mysql;
update user set host='%' where user ='root';
FLUSH PRIVILEGES;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;

select user,host,grant_priv,super_priv,authentication_string,password_last_changed from mysql.user;
模拟主节点挂掉测试

我们把主节点服务停掉,执行命令:docker stop mysql-01,然后进入mysql-02节点,查询集群状态

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | a133b299-affe-11ec-8db8-0242ac13000d | 172.19.0.13 |        3306 | ONLINE       | PRIMARY     | 8.0.20         |
| group_replication_applier | d3042efc-affc-11ec-a414-0242ac13000c | 172.19.0.12 |        3306 | ONLINE       | SECONDARY   | 8.0.20         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
2 rows in set (0.01 sec)

可以看到mysql-03节点已经升级为主节点。这时候我们去mysql-03节点插入一条数据。

use test;
INSERT INTO t1 VALUES (1, 'Jack');

我们执行docker start mysql-01mysql-01节点启动起来再来,然后进入命令行,执行同步命令,看看集群状态:

# 开启同步
mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (5.98 sec)

# 查询当前集群状态
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
| group_replication_applier | 7a2a180c-affc-11ec-a14f-0242ac13000b | 172.19.0.11 |        3306 | ONLINE       | SECONDARY   | 8.0.20         |
| group_replication_applier | a133b299-affe-11ec-8db8-0242ac13000d | 172.19.0.13 |        3306 | ONLINE       | PRIMARY     | 8.0.20         |
| group_replication_applier | d3042efc-affc-11ec-a414-0242ac13000c | 172.19.0.12 |        3306 | ONLINE       | SECONDARY   | 8.0.20         |
+---------------------------+--------------------------------------+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)

目前,节点mysql-01已经成功加入集群。

mysql> select * from test.t1;
+----+-------+
| c1 | c2    |
+----+-------+
|  1 | Luis  |
|  2 | Jack  |
+----+-------+
2 rows in set (0.01 sec)

数据也已经同步过来了。

小结

MySQL官方提供的组复制组件已经是可用状态,但是还是有不少限制。例如:表必须是InnoDB 存储引擎,必须有主键或者唯一键,对网络要求比较高,最多只能支持9个节点等。具体可以见官网参考资料链接。

参考资料
MySQL :: MySQL 8.0 参考手册 :: 18 组复制

MySQL :: MySQL 8.0 参考手册 :: 18.3 要求和限制

mysql8.0初探:(二)MySQL Group Replication-MGR集群简介_ping_note的技术博客_51CTO博客

【DB宝18】在Docker中安装使用MySQL高可用之MGR – 小麦苗DBA宝典 (xmmup.com)

MySQL MGR 节点管理及故障切换 - 墨天轮 (modb.pro)

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

推荐阅读更多精彩内容