Tair

Tair

Tair介紹

Tair(Taobao Pair)是淘寶開發(fā)的分布式Key-Value存儲引擎
服務器端自動負載均衡
分為持久化和非持久化兩種方式存儲
非持久化:分布式緩存使用 Memcached(mdb)、Redis(rdb)
持久化:SQL-DB使用FireBird(fdb)
NoSQL-DB:使用Kyoto Cabinet(kdb)扛施、LevelDB(ldb)
Tair采用可插拔存儲引擎設計,以上這些存儲引擎可以很方便的替換屹篓,還可以引入新的存儲引擎比如:
MySQL

使用場景

分布式緩存
大多數(shù)使用場景
大訪問少量臨時數(shù)據(jù)的存儲(kb左右)
用于緩存疙渣,降低對后端數(shù)據(jù)庫的訪問壓力
session場景
高速訪問某些數(shù)據(jù)結構的應用和計算(rdb)
數(shù)據(jù)源存儲
快速讀取數(shù)據(jù)(fdb)
持續(xù)大數(shù)據(jù)量的存入讀取(ldb)堆巧,交易快照
高頻度的更新讀韧蟆(ldb),庫存

Tair整體架構分析

image-20200827171238078.png

一個Tair集群主要包括client谍肤、Config server和Dataserver 三個不同的應用啦租。
Client在初始化時,從Config server處獲取數(shù)據(jù)的分布信息荒揣,根據(jù)分布信息和相應的Data server交互完
成用戶的請求篷角。
Config Server通過和Data Server的心跳(HeartBeat)維護集群中可用的節(jié)點,并根據(jù)可用的節(jié)點系任,
構建數(shù)據(jù)的在集群中的分布信息
Data server負責數(shù)據(jù)的存儲恳蹲,并按照Config server的指示完成數(shù)據(jù)的復制和遷移工作。

Config Server

Config Server是單點俩滥,采用一主一備的方式保證可靠性嘉蕾。
管理所有的data server, 維護data server的狀態(tài)信息
用戶配置的桶數(shù)量、副本數(shù)举农、機房信息
數(shù)據(jù)分布的對照表
協(xié)調(diào)數(shù)據(jù)遷移荆针、管理進度,將數(shù)據(jù)遷移到負載較小的節(jié)點上
Client和ConfigServer的交互主要是為了獲取數(shù)據(jù)分布的對照表颁糟,當client獲取到對照表后航背,會cache這
張表,然后通過查這張表決定數(shù)據(jù)存儲的節(jié)點棱貌,所以不需要和configserver交互玖媚,這使得Tair對外的服
務不依賴configserver,所以它不是傳統(tǒng)意義上的中心節(jié)點
Config server維護的對照表有版本概念婚脱,由于集群變動或管理觸發(fā)今魔,構建新的對照表后,對照表的版本
號遞增障贸,并通過Data server的心跳错森,將新表同步給數(shù)據(jù)節(jié)點。
客戶端和Data server交互時涩维,Dataserver每次都把自己緩存的對照表版本號放入response結構中瓦阐,返
回給客戶端,客戶端將Data server的對照表版本號和自己緩存的對照表版本號比較踏幻,如果不相同该面,會
主動和Config server通信吆倦,請求新的對照表坐求。
Tair的Configserver使客戶端使用時候桥嗤,不需要配置數(shù)據(jù)節(jié)點列表泛领,也不需要處理節(jié)點的的狀態(tài)變化渊鞋,
這使得Tair對最終用戶來說使用和配置都很簡單锡宋。

Data Server

Data server負責數(shù)據(jù)的物理存儲执俩,并根據(jù)Configserver構建的對照表完成數(shù)據(jù)的復制和遷移工作役首。
Data server具備抽象的存儲引擎層衡奥,可以很方便地添加新存儲引擎矮固。Data server還有一個插件容器乏屯,
可以動態(tài)地加載/卸載插件。

image-20200827171356871.png

Tair的存儲引擎有一個抽象層蛤迎,只要滿足存儲引擎需要的接口替裆,便可以很方便地替換Tair底層的存儲引
擎辆童。比如你可以很方便地將bdb把鉴、tc甚至MySQL作為Tair的存儲引擎庭砍,而同時使用Tair的分布方式怠缸、同步
等特性揭北。
Tair默認包含兩個存儲引擎:mdb和fdb搔体。
mdb是一個高效的緩存存儲引擎嫉柴,它有著和memcached類似的內(nèi)存管理方式奉呛。mdb支持使用share
memory(tmpfs)瞧壮,這使得我們在重啟Tair數(shù)據(jù)節(jié)點的進程時不會導致數(shù)據(jù)的丟失咆槽,從而使升級對應
用來說更平滑,不會導致命中率的較大波動蛾娶。
fdb是一個簡單高效的持久化存儲引擎蛔琅,使用樹的方式根據(jù)數(shù)據(jù)key的hash值索引數(shù)據(jù)罗售,加快查找速
度钩述。索引文件和數(shù)據(jù)文件分離牙勘,盡量保持索引文件在內(nèi)存中谜悟,以便減小IO開銷北秽。使用空閑空間池管理被
刪除的空間贺氓。

Tair的安裝與使用

環(huán)境準備

GIt

yum install git
yum update -y nss curl libcurl

svn

yum install subversion

Libtool

yum install libtool

boost-devel

yum install boost-devel

zlib

yum install zlib-devel

C++

yum install gcc-c++

項目源碼下載和編譯

tbsys庫和tbnet庫下載(tair底層依賴tbsys庫和tbnet庫)

#git源碼下載
git clone https://github.com/kayaklee/tb-common-util.git
#設置環(huán)境變量
mkdir /var/tblib
export TBLIB_ROOT="/var/tblib"
#因為tbnet和tbsys在兩個不同的目錄辙培,但它們的源碼文件里頭文件的互相引用卻沒有加絕對或相對路徑扬蕊,
將兩個目錄的源碼加入到C++環(huán)境變量中即可尾抑。否則編譯時會出現(xiàn):“fatal error:tysys.h: No such
file or directory”的錯誤再愈。
CPLUS_INCLUDE_PATH=/home/tair/tb-common-util/tbsys/src:/home/tair/tb-commonutil/
tbnet/src
export CPLUS_INCLUDE_PATH
#修改tbsys代碼
cd ~/tb-common-util/trunk/tbsys/src
下載的代碼有個錯誤:具體是tbsys/src/tblog.cpp中323行代碼:需要將CLogger::CLogger&
CLogger::getLogger()改為CLogger& CLogger::getLogger()
#編譯tbsys和tbnet
cd tb-common-utils
./build.sh
注:安裝成功后,TBLIB_ROOT所指示的目錄下會有include和lib兩個目錄垂睬。
tair下載
#git源碼下載
git clone https://github.com/alibaba/tair.git
#安裝依賴
yum install -y openssl-devel libcurl-devel
#編譯
./bootstrap.sh
#檢測和生成 Makefile
./configure
#編譯和安裝到目標目錄
make -j
make install
注:默認安裝位置是 ~/tair_bin
Tair配置和啟動

下面以MDB引擎為例配置一個最小化的Tair集群(1 * ConfigServer + 1 * DataServer)

#查看和設置系統(tǒng)tmpfs
#MDB 引擎默認使用共享內(nèi)存钳枕,所以需要查看并設置系統(tǒng)的tmpfs的大小
# 這里根據(jù)實際機器內(nèi)存情況配置么伯,必須大于Tair使用內(nèi)存的配置
vim /etc/fstab
tmpfs /dev/shm tmpfs defaults,size=1024M 0 0
#生效
mount -o remount /dev/shm
#查看tmpfs
cat /etc/fstab | grep /dev/shm
顯示tmpfs /dev/shm tmpfs rw,size=1G 0 0
# 定義配置文件 復制配置文件
cp etc/configserver.conf.default etc/configserver.conf
cp etc/group.conf.default etc/group.conf
etc/dataserver.conf.default etc/dataserver.conf
# configserver.conf
[public]
config_server=192.168.127.133:5198
#config_server=192.168.1.2:5198
#config_server=10.211.55.9:5198
#config_server=192.168.1.2:5198
dev_name=eno16777736
#group.conf
# data center A
_server_list=192.168.127.133:5191
#_server_list=192.168.1.2:5191
#_server_list=192.168.1.3:5191
#_server_list=192.168.1.4:5191
# data center B
#_server_list=192.168.2.1:5191
#_server_list=192.168.2.2:5191
#_server_list=192.168.2.3:5191
#_server_list=192.168.2.4:5191
#dataserver.conf
[public]
config_server=192.168.127.133:5198
#config_server=192.168.1.2:5198
dev_name=eno16777736
mdb_inst_shift=0
process_thread_num=4
io_thread_num=4
#這里 slab_mem_size控制MDB內(nèi)存池的總大小田柔,mdb_inst_shift 控制實例的個數(shù)硬爆,注意這里一個實例
必須大于512MB且小于64GB缀磕。
slab_mem_size=512
#修改 tair.sh啟動腳本
#在CentOS 7下袜蚕,安裝目錄下的 tair.sh 啟動腳本有一行代碼需要修改
tmpfs_size=`df -m |grep /dev/shm | awk '{print $2}'`
#啟動Tair實例
#啟動dataserver
tair_bin $ ./tair.sh start_ds
#啟動configserver
tair_bin $ ./tair.sh start_cs
#查看進程
ps -ef |grep tair
注:執(zhí)行后沒有兩行記錄顯示绢涡,說明啟動失敗凿傅,可查看logs中的log查看錯誤信息聪舒。
Tair測試
#客戶端讀寫測試
tair_bin $ ./sbin/tairclient -c 192.168.127.133:5198 -g group_1
TAIR> health
TAIR> put key value
TAIR> get key
TAIR> remove key
TAIR> get key
Tair停止
#停止dataserver
tair_bin $ ./tair.sh stop_ds
#停止configserver
tair_bin $ ./tair.sh stop_cs

Tair高可用和負載均衡

Tair的高可用和負載均衡箱残,主要通過對照表和數(shù)據(jù)遷移兩大功能進行支撐止吁。
對照表將數(shù)據(jù)分為若干個桶赏殃,并根據(jù)機器數(shù)量、機器位置進行負載均衡和副本放置榜揖,確保數(shù)據(jù)分布均
勻举哟,并且在多機房有數(shù)據(jù)副本。
在集群發(fā)生變化時潜叛,會重新計算對照表威兜,并進行數(shù)據(jù)遷移庐椒。

對照表

在Tair系統(tǒng)中约谈,采用對照表將數(shù)據(jù)均衡的分布在DataServer上
還能動態(tài)適應節(jié)點的擴容和縮容
Tair基于一致性Hash算法存儲數(shù)據(jù)泼橘,根據(jù)配置建立固定數(shù)量的桶(bucket)
桶為Hash環(huán)節(jié)點迈勋,hash(key) 順時針 設置桶
桶是負載均衡和數(shù)據(jù)遷移的基本單位
config server 根據(jù)一定的策略把每個桶指派到不同的data server上粪躬,
因為數(shù)據(jù)按照key做hash算法镰官,所以可以認為每個桶中的數(shù)據(jù)基本是平衡的泳唠,
保證了桶分布的均衡性, 就保證了數(shù)據(jù)分布的均衡性笨腥。
比如:

    bucket      data server
    0       192.168.127.132 192.168.127.134
    1       192.168.127.133 192.168.127.135
    2       192.168.127.132 192.168.127.134
    3       192.168.127.133 192.168.127.135
    4       192.168.127.132 192.168.127.134
    5       192.168.127.133 192.168.127.135

Tair支持自定義的備份數(shù)脖母,比如你可以設置數(shù)據(jù)備份為2谆级,以提高數(shù)據(jù)的可靠性脚仔。對照表可以很方便地支
持這個特性鲤脏。
第二列為主節(jié)點的信息猎醇,第三列為輔節(jié)點信息姑食。在Tair中音半,客戶端的讀寫請求都是和主節(jié)點交互贡蓖。
當有節(jié)點不可用時斥铺,如果是輔節(jié)點邻眷,那么configserver會重新為其指定一個輔節(jié)點肆饶,如果是持久化存
儲驯镊,還將復制數(shù)據(jù)到新的輔節(jié)點上板惑。如果是主節(jié)點冯乘,那么configserver首先將輔節(jié)點提升為主節(jié)點裆馒,對
外提供服務他膳,并指定一個新的輔節(jié)點棕孙,確保數(shù)據(jù)的備份數(shù)蟀俊。

對照表的初始化

第一次啟動ConfigServer會根據(jù)在線的DataServer生成對照表
ConfigServer會啟動一個線程(table_builder_thread),該線程會每秒檢查一次是否需要重新構造對照

Tair 提供了兩種生成對照表的策略:

負載均衡優(yōu)先

config server會盡量的把桶均勻的分布到各個data server上
一個桶的備份數(shù)據(jù)不能在同一臺主機上

位置安全優(yōu)先

一般我們通過控制 _pos_mask(Tair的一個配置項) 來使得不同的機房具有不同的位置信息
一個桶的備份數(shù)據(jù)不能都位于相同的一個位置(不在同一個機房)

數(shù)據(jù)遷移

當DataServer增加時,config server負責重新計算一張新的桶在data server上的分布表
將桶較為平均的分配到各個DataServer上
當DataServer發(fā)生故障時, config server負責重新計算一張新的桶在data server上的分布表,
將原來由故障機器服務的桶的訪問重新指派到其它的data server中
這時需要做數(shù)據(jù)遷移谨娜,具體流程如下:

1、設置當前正在遷移的桶ID
2烫映、DataServer寫入桶數(shù)據(jù)時,會寫入redolog
3噩峦、migrate_manager遷移內(nèi)存中的桶數(shù)據(jù)
4锭沟、migrate_manager遷移redolog數(shù)據(jù)
5、redolog數(shù)據(jù)遷移完成后识补,將桶標記為遷移完成
6族淮、將信息發(fā)送給ConfigServer用于同步對照表

image-20200827172539347.png

Tair存儲引擎

Tair的存儲引擎有一個抽象層(storage_manager),只要實現(xiàn)存儲引擎接口,便可以替換Tair的底層存
儲引擎。
可插拔存儲引擎---類似MySQL
Tair默認包含四種存儲引擎:mdb、fdb乍炉、kdb和ldb

mdb

數(shù)據(jù)存儲:Memcached
高效緩存存儲
使用share memory 巢株,重啟不會數(shù)據(jù)丟失
支持K-V存儲那槽、prefix操作
適用于:String緩存使用 (json)、大訪問少量的臨時數(shù)據(jù)存儲、Session分離

fdb

數(shù)據(jù)存儲:FireBird
高效的持久化SQL存儲
索引文件和數(shù)據(jù)文件分離------>mysql MyISam
使用Tree的方式根據(jù)key的hash值索引數(shù)據(jù) B tree
索引文件在內(nèi)存中
適用于:快速訪問較小的數(shù)據(jù)

kdb

數(shù)據(jù)存儲:Kyoto Cabinet
Cabinet開發(fā)的KV的持久化存儲

簡單的包含記錄的數(shù)據(jù)文件
存儲形式為hash表或B+Tree
適用于:簡單臨時存儲

ldb

數(shù)據(jù)存儲:LevelDB
是Google開發(fā)的高性能持久化KV存儲
可內(nèi)嵌mdb緩存 mdb+ldb
支持kv雏赦、prefix
支持批量操作
適用于:大數(shù)據(jù)量的存取(交易快照)、高頻度的更新(庫存)、離線大批量數(shù)據(jù)導入

rdb

數(shù)據(jù)存儲:Redis
高效緩存存儲
多種數(shù)據(jù)結構和計算
適用于:復雜數(shù)據(jù)結構存儲、商品屬性、粉絲列表、商品評論、消息隊列

數(shù)據(jù)存儲 持久化 數(shù)據(jù)格式 應用場景
mdb Memcached K-V存儲斧散、prefix操作 緩存使用箍镜、Session分離
fdb FireBird SQL、BTree 快速訪問較小的數(shù)據(jù)
kdb Kyoto Cabinet Hash、B+Tree 簡單臨時存儲
ldb LevelDB kv、prefix、batch 大數(shù)據(jù)量高頻度的存儲
rdb Redis List鄙陡、Set毫捣、Hash.... 復雜數(shù)據(jù)結構緩存

阿里使用:mdb、rdb珊佣、ldb

mdb的存儲結構(Memcached)

image-20200827173134242.png

mem_pool
用于共享內(nèi)存管理蒿辙,將內(nèi)存分為若干個page
page個數(shù)根據(jù)slab_mem_size設置泰偿,單位為MB
單個DataServer最多使用64G內(nèi)存
mem_cache
用于管理slab调塌,存放slab_manager列表
slab_manager管理item(數(shù)據(jù)塊)
slab個數(shù)為100,一個slab可存儲800kb
cache_hash_map
用于存儲hash表腹缩,根據(jù)key進行hash對應item數(shù)據(jù)
hash沖突,產(chǎn)生鏈表
mdb_area_stat
用于維護area(namespace)狀態(tài)脆粥,記錄了該area的數(shù)據(jù)鏈表和數(shù)據(jù)量限制(tablespace)

Tair相應API

key/value常見操作API

命令 操作 說明
get get key [area] 獲得key的值
put put key data [area]
[expired]
設置key和value
incr incr key [count]
[initValue] [area]
對key的值自增
decr decr key [count]
[area]
對key的值自減
batcheGet mget key1 ... keyn
area
批量獲得
expire expire key time 設置key的過期時間
prefixPut pput [area] pkey
skey value
根據(jù)前綴設置培慌,按照prefix計算hash,同一個prefix會存
儲在同一個hash表胁镐,形成鏈表
prefixGet pget [area] pkey
skey
根據(jù)前綴讀取

version

Tair中的每個數(shù)據(jù)都包含版本號,版本號在每次更新后都會遞增狞甚。這個特性有助于防止由于數(shù)據(jù)的并發(fā)
更新導致的問題十气,類似于樂觀鎖
比如离福,系統(tǒng)有一個value為“a”次舌,A和B同時get到這個value。
A執(zhí)行操作改為"b"
B執(zhí)行操作改為"c"
如果不控制,無論A和B誰先更新成功徘郭,它的更新都會被后到的更新覆蓋。
使用version解決
第一次put version為1
get version為1
A修改成功后 version增加1
B修改時本身version為1濒憋,小于服務器版本2
所以B修改不成功
在put時条辟,version傳0 可強制修改

struct mdb_item{
    uint64_t item_id;
    .....
    uint16_t version
}

Tair實現(xiàn)樂觀鎖
trylock(獲取版本)–> transaction –> unlock(版本檢查) — 正常—> version+1 commit
— 異常—> rollback
只要能獲取版本浇衬,就認為trylock成功
版本檢查則更新version,一般是加1螺捐;否則颠悬,異常處理矮燎,version不變,并開啟回滾
多事務同時獲得樂觀鎖時赔癌,unlock只能有一個成功诞外,其余的都應該失敗
version 初始化要大于1
Tair實現(xiàn)分布式鎖
利用 Tair 的 version 特性可以實現(xiàn)分布式鎖,由于 LDB 具有持久化功能灾票,當服務有出現(xiàn)宕機的情況峡谊,
也不會因此出現(xiàn)鎖丟失或者鎖不可釋放的情況。
如果KEY不存在的話刊苍,傳入一個固定的初始化VERSION(需要大于1)既们,Tair會在保存這個緩存的同時設
置這個緩存的VERSION為你傳入的 VERSION+1
KEY如果已經(jīng)存在,Tair會校驗你傳入的VERSION是否等于現(xiàn)在這個緩存的VERSION班缰,如果相等則允許
修改贤壁,否則將失敗。

    //獲得鎖
    public boolean lock(String lockKey) {
        //10 :version expiretime : 過期時間 秒
        ResultCode code = defaultTairManager.put(lockKey, defaultVlaue, 5,
        expiretime);
        if (ResultCode.SUCCESS.equals(code))
        return true;
        else
        return false;
    }
    //釋放鎖
    public Boolean unlock(String lockKey){
        ResultCode code = defaultTairManager.delete(lockKey);
        return ResultCode.SUCCESS.equals(code);
    }

Java Client

pom.xml

<!-- https://mvnrepository.com/artifact/com.taobao.tair/tair-client -->
    <dependency>
        <groupId>com.taobao.tair</groupId>
        <artifactId>tair-client</artifactId>
        <version>2.3.5</version>
    </dependency>

demo

    DefaultTairManager defaultTairManager = new DefaultTairManager();
    List<String> cs = new ArrayList<String>();
    cs.add("192.168.127.132:5198");
    defaultTairManager.setConfigServerList(cs);
    defaultTairManager.setGroupName("group_1");
    defaultTairManager.init();
    defaultTairManager.put(0,"name:001","zhangfei");
    Result<DataEntry> value= defaultTairManager.get(0,"name:001");
    System.out.println(value);
    //設置版本0 強制更新 過期時間2秒
    defaultTairManager.put(0,"name:002","zhaoy",0,2);
    Result<DataEntry> value2= defaultTairManager.get(0,"name:002");
    System.out.println(value2);

Tair
機房埠忘、負載均衡、數(shù)據(jù)遷移馒索、不停止服務
更好的可用性和負載均衡
Redis
多數(shù)據(jù)結構處理莹妒、計算
Tair+Redis

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绰上,隨后出現(xiàn)的幾起案子旨怠,更是在濱河造成了極大的恐慌,老刑警劉巖蜈块,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鉴腻,死亡現(xiàn)場離奇詭異,居然都是意外死亡百揭,警方通過查閱死者的電腦和手機爽哎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來器一,“玉大人课锌,你說我怎么就攤上這事∑盹酰” “怎么了渺贤?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長请毛。 經(jīng)常有香客問我志鞍,道長,這世上最難降的妖魔是什么方仿? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任固棚,我火速辦了婚禮统翩,結果婚禮上,老公的妹妹穿的比我還像新娘玻孟。我一直安慰自己唆缴,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布黍翎。 她就那樣靜靜地躺著面徽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匣掸。 梳的紋絲不亂的頭發(fā)上趟紊,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音碰酝,去河邊找鬼霎匈。 笑死,一個胖子當著我的面吹牛送爸,可吹牛的內(nèi)容都是我干的铛嘱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼袭厂,長吁一口氣:“原來是場噩夢啊……” “哼墨吓!你這毒婦竟也來了?” 一聲冷哼從身側響起纹磺,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤帖烘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后橄杨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秘症,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年式矫,在試婚紗的時候發(fā)現(xiàn)自己被綠了乡摹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡衷佃,死狀恐怖趟卸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情氏义,我是刑警寧澤锄列,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站惯悠,受9級特大地震影響邻邮,放射性物質發(fā)生泄漏。R本人自食惡果不足惜克婶,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一筒严、第九天 我趴在偏房一處隱蔽的房頂上張望丹泉。 院中可真熱鬧,春花似錦鸭蛙、人聲如沸摹恨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晒哄。三九已至,卻和暖如春肪获,著一層夾襖步出監(jiān)牢的瞬間寝凌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工孝赫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留较木,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓青柄,卻偏偏與公主長得像伐债,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子致开,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348