(二)HBase集群啟動流程分析

在安裝HBase的時候需要配置一些參數(shù),這些參數(shù)在HBase啟動的時候發(fā)揮著怎樣的作用颖系,如何影響的HBase的運行嗅剖,出現(xiàn)啟動錯誤時如何快速定位錯誤的位置。在解決這些問題之前嘁扼,先從源碼的角度分析一下HBase的啟動流程信粮,了解HBase是怎么啟動的。

啟動流程概述

啟動HBase會執(zhí)行start-hbase.sh偷拔,然后腳本會先執(zhí)行hbase-config.sh蒋院,做一系列的配置設(shè)置,包括常用路徑莲绰、regionservers和backup-masters列表、常用端口等姑丑。在hbase-config.sh中會執(zhí)行hbase-env.sh蛤签,主要對JAVA的環(huán)境參數(shù)、ssh,栅哀、pid路徑等進行配置震肮。start-hbase.sh最后會根據(jù)hbase.cluster.distributed來確定啟動模式,分為本地和分布式留拾。
分布式啟動HBase戳晌,使用hbase-daemons.sh逐步啟動zookeeper、master痴柔、regionserver沦偎、master-backup。具體會調(diào)用相關(guān)的腳本(如zookeeper會調(diào)用zookeerper.sh)來進行環(huán)境的配置和登錄到相關(guān)的機器節(jié)點上執(zhí)行hbase-daemon.sh咳蔚。
hbase-daemon.sh的職責(zé)就是啟動各個進程豪嚎,在啟動過程中會先做進程判斷,日志滾動等準(zhǔn)備谈火,最后執(zhí)行啟動命名侈询,逐步的啟動各個節(jié)點上的進程。在啟動過程中糯耍,會在屏幕中打印啟動信息扔字。
執(zhí)行流程圖:


具體啟動流程

接下來分析每一步的源碼囊嘉,了解整個執(zhí)行的過程。

start-hbase.sh

作為HBase啟動的入口革为,在執(zhí)行hbase-config.sh設(shè)置完運行的環(huán)境變量后扭粱,會執(zhí)行命令,獲取hbase.cluster.distributed的值來確定HBase的啟動模式篷角。

distMode=`$bin/hbase --config "$HBASE_CONF_DIR" org.apache.hadoop.hbase.util.HBaseConfTool hbase.cluster.distributed | head -n 1`

具體的代碼實現(xiàn):



hbase.cluster.distributed在hbase-site.xml中進行配置焊刹,默認值為false。false將啟動standalone模式恳蹲,true將啟動distributed模式虐块,在配置分布式的時候需要改成true。

if [ "$distMode" == 'false' ] 
then
  "$bin"/hbase-daemon.sh --config "${HBASE_CONF_DIR}" $commandToRun master $@
else
  "$bin"/hbase-daemons.sh --config "${HBASE_CONF_DIR}" $commandToRun zookeeper
  "$bin"/hbase-daemon.sh --config "${HBASE_CONF_DIR}" $commandToRun master 
  "$bin"/hbase-daemons.sh --config "${HBASE_CONF_DIR}" \
    --hosts "${HBASE_REGIONSERVERS}" $commandToRun regionserver
  "$bin"/hbase-daemons.sh --config "${HBASE_CONF_DIR}" \
    --hosts "${HBASE_BACKUP_MASTERS}" $commandToRun master-backup
fi

standalone模式將會在一個JVM中運行所有的HBase和zookeeper嘉蕾。distributed模式會逐漸啟動zookeeper贺奠、master、regionserver错忱、master-backup等進程和節(jié)點儡率。在啟動節(jié)點時,$commandToRun有兩種選擇以清,可以是start(默認)儿普,還可以是autorestart。

hbase-config.sh

由于啟動的腳本如hbase-daemon.sh掷倔,zookeerper.sh等都不是在一臺機器上運行眉孩,因此每個腳本在執(zhí)行前都會先執(zhí)行一下hbase-config.sh來配置運行時的環(huán)境變量。
在hbase-config.sh中首先會檢查hbase-config.sh是否是一個軟連接勒葱,找到hbase-config.sh的真實路徑浪汪。然后設(shè)置HBASE_HOME,判斷是否指定了hbase的配置文件路徑凛虽,如果沒有將采用默認的配置文件路徑死遭。
腳本會設(shè)置一些默認的參數(shù),包括HBASE_CONF_DIR凯旋、HBASE_REGIONSERVERS呀潭、HBASE_BACKUP_MASTERS、HBASE_THRIFT_JMX_OPTS等瓦阐,然后執(zhí)行hbase-env.sh配置HBase的AVA的運行環(huán)境和ssh參數(shù)等蜗侈。程序會判斷是否設(shè)置HBASE_REGIONSERVER_MLOCK為true,這個主要是判斷系統(tǒng)是否使用了mlock來鎖住內(nèi)存睡蟋,防止這段內(nèi)存被操作系統(tǒng)swap掉踏幻。這將阻止Linux 將這個內(nèi)存頁調(diào)度到交換空間(swap space),即使該程序已有一段時間沒有訪問這段空間戳杀。最后如果檢測到了JAVA_HOME后该面,程序?qū)^續(xù)運行夭苗。

hbase-env.sh

hbase-env.sh中設(shè)置了HBase運行中的一些重要的JVM參數(shù),后續(xù)在進行HBase調(diào)優(yōu)時會用到這些參數(shù)隔缀。除此之外還有一些進程優(yōu)先級题造,SSH參數(shù)等。主要參數(shù)如下:
1猾瘸、JAVA_HOME
JAVA的JDK路徑界赔,需要java 1.7以上
2、HBASE_HEAPSIZE
堆的最大使用量牵触。默認情況下是JVM默認值
3淮悼、HBASE_OFFHEAPSIZE
如果打算使用堆緩存,可以設(shè)置該值揽思。例如袜腥,要分配8G的offheap,將值設(shè)置為“8G”钉汗。
4羹令、HBASE_OPTS
默認為"-XX:+UseConcMarkSweepGC"
使用CMS收集器對年老代進行垃圾收集,CMS收集器通過多線程并發(fā)進行垃圾回收损痰,盡量減少垃圾收集造成的停頓福侈。
5、PermSize設(shè)置非堆內(nèi)存初始值
僅僅是JDK7需要配置卢未,JDK8+后就不需要
6癌刽、HBASE_MASTER_OPTS、HBASE_REGIONSERVER_OPTS
配置非堆內(nèi)存尝丐,初始分配的堆內(nèi)存,大允許分配的堆內(nèi)存衡奥,按需分配
7爹袁、java回收機制,分別配置sever和client端
HBase會在啟動的時候講Java的一些配置打印到.out日志中

SERVER_GC_OPTS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
SERVER_GC_OPTS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<FILE-PATH>"
SERVER_GC_OPTS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<FILE-PATH> -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M"

8矮固、HBASE_MANAGES_ZK
是否由它自己的zookpeer來管理失息,一般使用單獨的zookeeper集群來管理hbase。
9档址、HBASE_PID_DIR
pid的路徑盹兢,默認/tmp目錄下,但/tmp中的文件易失守伸,導(dǎo)致找不到pid文件绎秒,最后配置一個其它穩(wěn)定的文件。
10尼摹、HBASE_NICENESS
守護進程的調(diào)度優(yōu)先級
11见芹、HBASE_IDENT_STRING
標(biāo)志hbase實例的字符串剂娄,默認情況下為當(dāng)前用戶,會在創(chuàng)建pid玄呛,log等文件時使用
12阅懦、HBASE_BACKUP_MASTERS
在backup-masters中配置配置bakcup的master,當(dāng)一臺master宕機后徘铝,zookeeper會在backup中選擇一個耳胎。默認backup-masters是不存在的,需要自己新建
13惕它、HBASE_SSH_OPTS
ssh配置怕午,如果ssh的端口不是22,可以進行設(shè)置HBASE_SSH_OPTS="-p xxx”怠缸,后面再啟動各個進程的時候會用到诗轻,如:

ssh $HBASE_SSH_OPTS $zookeeper $cmd 2>&1 | sed "s/^/$zookeeper: /" &

14、HBASE_LOG_DIR
hbase的日志路徑

hbase-daemons.sh

hbase-daemons.sh比較簡單揭北,主要根據(jù)要啟動的進程扳炬,生成好遠程執(zhí)行命令remote_cmd,然后做分發(fā)搔体。

remote_cmd="cd ${HBASE_HOME}; $bin/hbase-daemon.sh --config ${HBASE_CONF_DIR} $@"
args="--hosts ${HBASE_REGIONSERVERS} --config ${HBASE_CONF_DIR} $remote_cmd"

command=$2
case $command in
  (zookeeper)
    exec "$bin/zookeepers.sh" $args
    ;;
  (master-backup)
    exec "$bin/master-backup.sh" $args
    ;;
  (*)
    exec "$bin/regionservers.sh" $args
    ;;
esac

zookeepers.sh

如果HBASE_MANAGES_ZK為true恨樟,表示hbase使用自帶的zookeeper集群,如果不是疚俱,需要去加載用戶自定義的配置劝术。

if [ "$HBASE_MANAGES_ZK" = "true" ]; then
  hosts=`"$bin"/hbase org.apache.hadoop.hbase.zookeeper.ZKServerTool | grep '^ZK host:' | sed 's,^ZK host:,,'`
    # 獲取執(zhí)行命令,從hbase-daemons.sh傳過來
    # remote_cmd="cd ${HBASE_HOME}; $bin/hbase-daemon.sh --config ${HBASE_CONF_DIR} $@"
  cmd=$"${@// /\\ }"
  for zookeeper in $hosts; do
    # 登錄到節(jié)點上
   ssh $HBASE_SSH_OPTS $zookeeper $cmd 2>&1 | sed "s/^/$zookeeper: /" &
   if [ "$HBASE_SLAVE_SLEEP" != "" ]; then
    # 等待
     sleep $HBASE_SLAVE_SLEEP
   fi
  done
fi

wait

org.apache.hadoop.hbase.zookeeper.ZKServerTool中通過readZKNodes獲取到zookeerper的節(jié)點地址呆奕。
其會在conf目錄下搜尋zoo.cfg文件养晋,加載zookeeper的配置,如果沒有則會去hbase-site.xml中加載zookeeper的配置梁钾,包括hbase.zookeeper.quorum绳泉,hbase.zookeeper.property.clientPort,hbase.zookeeper.property.dataDir等參數(shù)姆泻。因此zookeeper的信息可以在zoo.cfg中配置零酪,也可以在hbase-site.xml中進行配置。

master-backup.sh

在master-backup.sh中拇勃,首先會獲取到 backup-masters列表四苇,然后登陸到節(jié)點上去以backup的方式啟動master

HOSTLIST=$HBASE_BACKUP_MASTERS
# 獲取backup-masters列表,在hbase-env.sh中設(shè)置HBASE_BACKUP_MASTERS參數(shù)
if [ "$HOSTLIST" = "" ]; then
  if [ "$HBASE_BACKUP_MASTERS" = "" ]; then
    export HOSTLIST="${HBASE_CONF_DIR}/backup-masters"
  else
    export HOSTLIST="${HBASE_BACKUP_MASTERS}"
  fi
fi

args=${@// /\\ }
args=${args/master-backup/master}

if [ -f $HOSTLIST ]; then
  for hmaster in `cat "$HOSTLIST"`; do
    # 登錄到節(jié)點上 啟動backup master
   ssh $HBASE_SSH_OPTS $hmaster $"$args --backup" \
     2>&1 | sed "s/^/$hmaster: /" &
   if [ "$HBASE_SLAVE_SLEEP" != "" ]; then
     sleep $HBASE_SLAVE_SLEEP
   fi
  done
fi 

啟動后zookeeper會自動選取一個master作為active方咆,其它的都是backup月腋。

regionservers.sh

首先獲取到regionservers列表,默認在conf/regionservers中進行設(shè)置,如果regionservers是默認的localhost罗售,則會在本地啟動regionserver辜窑。設(shè)置成distributed后,會在各個節(jié)點上啟動regionserver節(jié)點寨躁。$”${@// /\\ }“會將命令中將所有的\替換成為空格穆碎。

HOSTLIST=$HBASE_REGIONSERVERS
# 獲取regionservers
if [ "$HOSTLIST" = "" ]; then
  if [ "$HBASE_REGIONSERVERS" = "" ]; then
    export HOSTLIST="${HBASE_CONF_DIR}/regionservers"
  else
    export HOSTLIST="${HBASE_REGIONSERVERS}"
  fi
fi

regionservers=`cat "$HOSTLIST"`
# 本地模式
if [ "$regionservers" = "localhost" ]; then
  "$bin"/local-regionservers.sh start 1
else
  for regionserver in `cat "$HOSTLIST"`; do
    # 并行執(zhí)行
    if ${HBASE_SLAVE_PARALLEL:-true}; then
      ssh $HBASE_SSH_OPTS $regionserver $"${@// /\\ }" \
        2>&1 | sed "s/^/$regionserver: /" &
    else # run each command serially
      ssh $HBASE_SSH_OPTS $regionserver $"${@// /\\ }" \
        2>&1 | sed "s/^/$regionserver: /"
    fi
    if [ "$HBASE_SLAVE_SLEEP" != "" ]; then
      sleep $HBASE_SLAVE_SLEEP
    fi
  done
fi

hbase-daemon.sh

前面的腳本都是做的準(zhǔn)備工作,啟動各個節(jié)點最后都是由hbase-daemon.sh來完成的职恳。因此hbase-daemon.sh應(yīng)該是最重要的腳本所禀,負責(zé)啟動前的環(huán)境清理,日志滾動以及進程啟動等工作放钦。
hbase-daemon.sh支持start|stop|restart|autorestart|foreground_start色徘,5種啟動方式,可以單獨用來管理節(jié)點操禀,如啟動regionserver:hbase-daemon.sh start regionserver
經(jīng)過一系列的日志文件路徑設(shè)置后褂策,開始執(zhí)行start過程。

(start)
    check_before_start
    hbase_rotate_log $HBASE_LOGOUT
    hbase_rotate_log $HBASE_LOGGC
    # 啟動進程
    echo starting $command, logging to $HBASE_LOGOUT
    $thiscmd --config "${HBASE_CONF_DIR}" \
        foreground_start $command $args < /dev/null > ${HBASE_LOGOUT} 2>&1  &
    disown -h -r
    sleep 1; head "${HBASE_LOGOUT}"
  ;;

首先check_before_start檢查要啟動的進程是否存在颓屑,如果存在就會發(fā)出$command running as process cat $HBASE_PID. Stop it first的提示斤寂。看到這個提示揪惦,應(yīng)該到該機器節(jié)點上去查詢一下該進程的情況遍搞。

check_before_start(){
    #ckeck if the process is not running
    mkdir -p "$HBASE_PID_DIR"
    if [ -f $HBASE_PID ]; then
    # kill -0 pid 不發(fā)送任何信號,但是系統(tǒng)會進行錯誤檢查,檢查一個進程是否存在器腋,存在返回0溪猿;不存在返回1
      if kill -0 `cat $HBASE_PID` > /dev/null 2>&1; then
        echo $command running as process `cat $HBASE_PID`.  Stop it first.
        exit 1
      fi
    fi
}

接著進行日志的滾動,如果日志文件存在就進行1->5的滾動纫塌,因此我們能夠看到logs下有編號1到5的日志文件诊县。默認情況是不會輸出GC日志的,可以在hbase-env.sh中開啟SERVER_GC_OPTS措左,CLIENT_GC_OPTS配置翎冲,才會輸出GC日志。

hbase_rotate_log ()
{
    log=$1;
    num=5;
    if [ -n "$2" ]; then
    num=$2
    fi
    # 檢查日志文件是否存在媳荒,并做日志滾動
    if [ -f "$log" ]; then # rotate logs
      while [ $num -gt 1 ]; do
          prev=`expr $num - 1`
          [ -f "$log.$prev" ] && mv -f "$log.$prev" "$log.$num"
          num=$prev
      done
      mv -f "$log" "$log.$num";
    fi
}

隨后會使用foreground_start來啟動進程

(foreground_start)
    # 當(dāng)腳本收到SIGHUP SIGINT SIGTERM EXIT的信號時,trap命令執(zhí)行雙引號中的命令
    trap cleanAfterRun SIGHUP SIGINT SIGTERM EXIT
    if [ "$HBASE_NO_REDIRECT_LOG" != "" ]; then
        # NO REDIRECT
        echo "`date` Starting $command on `hostname`"
        echo "`ulimit -a`"
        # in case the parent shell gets the kill make sure to trap signals.
        # Only one will get called. Either the trap or the flow will go through.
        # 設(shè)置hbase節(jié)點進程執(zhí)行的優(yōu)先級
        nice -n $HBASE_NICENESS "$HBASE_HOME"/bin/hbase \
            --config "${HBASE_CONF_DIR}" \
            $command "$@" start &
    else
        echo "`date` Starting $command on `hostname`" >> ${HBASE_LOGLOG}
        echo "`ulimit -a`" >> "$HBASE_LOGLOG" 2>&1
        # in case the parent shell gets the kill make sure to trap signals.
        # Only one will get called. Either the trap or the flow will go through.
        nice -n $HBASE_NICENESS "$HBASE_HOME"/bin/hbase \
            --config "${HBASE_CONF_DIR}" \
            $command "$@" start >> ${HBASE_LOGOUT} 2>&1 &
    fi
    # Add to the command log file vital stats on our environment.
    hbase_pid=$!
    echo $hbase_pid > ${HBASE_PID}
    wait $hbase_pid
  ;;

當(dāng)腳本收到SIGHUP SIGINT SIGTERM EXIT的信號時驹饺,trap命令執(zhí)行cleanAfterRun钳枕,kill掉已經(jīng)存在的進程,然后告訴zk刪除有問題的節(jié)點赏壹。

# 當(dāng)運行遇到問題時進行清理
cleanAfterRun() {
  if [ -f ${HBASE_PID} ]; then
    # If the process is still running time to tear it down.
    kill -9 `cat ${HBASE_PID}` > /dev/null 2>&1
    rm -f ${HBASE_PID} > /dev/null 2>&1
  fi

  if [ -f ${HBASE_ZNODE_FILE} ]; then
    if [ "$command" = "master" ]; then
      HBASE_OPTS="$HBASE_OPTS $HBASE_MASTER_OPTS" $bin/hbase master clear > /dev/null 2>&1
    else
      #call ZK to delete the node
      # 告訴zk刪除有問題的節(jié)點
      ZNODE=`cat ${HBASE_ZNODE_FILE}`
      HBASE_OPTS="$HBASE_OPTS $HBASE_REGIONSERVER_OPTS" $bin/hbase zkcli delete ${ZNODE} > /dev/null 2>&1
    fi
    rm ${HBASE_ZNODE_FILE}
  fi
}

從代碼中可以看到鱼炒,最后啟動腳本使用hbase命令來啟動的,在hbase中將會進行一系列的設(shè)置如日志蝌借,lib庫昔瞧,執(zhí)行類等指蚁,最終會運行exec "$JAVA" -Dproc_$COMMAND -XX:OnOutOfMemoryError="kill -9 %p" $HEAP_SETTINGS $HBASE_OPTS $CLASS "$@"來啟動相應(yīng)的進程。執(zhí)行成功后自晰,可以ps -ef|grep 進程名來查看最后是執(zhí)行了什么命令來啟動進程的凝化,如查看HMaster如何啟動的ps -ef|grep HMaster,就會列出具體的進程信息酬荞。

/usr/java/jdk1.8.0_65/bin/java -Dproc_master -XX:OnOutOfMemoryError=kill -9 %p -Xmx5G -XX:+UseConcMarkSweepGC -XX:PermSize=128m -XX:MaxPermSize=128m -Dhbase.log.dir=/home/xuxphp/hbase/logs -Dhbase.log.file=hbase-xuxphp-master-xuxp022.log -Dhbase.home.dir=/home/xuxphp/hbase -Dhbase.id.str=xuxphp -Dhbase.root.logger=INFO,RFA -Djava.library.path=/home/xuxphp/hadoop/lib/native -Dhbase.security.logger=INFO,RFAS org.apache.hadoop.hbase.master.HMaster start

總結(jié)

HBase在啟動過程中搓劫,會先配置大量的參數(shù),重要的有JVM參數(shù)混巧,regionserver枪向,master-backup列表,路徑等咧党。分析一遍啟動的過程后秘蛔,明白了哪些參數(shù)在什么位置使用,起到了什么樣的作用傍衡,能夠為HBase的運維提供一個指導(dǎo)深员。最后自己也學(xué)到了很多shell編程的知識。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末聪舒,一起剝皮案震驚了整個濱河市辨液,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌箱残,老刑警劉巖滔迈,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異被辑,居然都是意外死亡燎悍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門盼理,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谈山,“玉大人,你說我怎么就攤上這事宏怔∽嗦罚” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵臊诊,是天一觀的道長鸽粉。 經(jīng)常有香客問我,道長抓艳,這世上最難降的妖魔是什么焰宣? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任嗡呼,我火速辦了婚禮扎即,結(jié)果婚禮上吃挑,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著对供,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笔宿。 梳的紋絲不亂的頭發(fā)上犁钟,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音泼橘,去河邊找鬼涝动。 笑死,一個胖子當(dāng)著我的面吹牛炬灭,可吹牛的內(nèi)容都是我干的醋粟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼重归,長吁一口氣:“原來是場噩夢啊……” “哼米愿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鼻吮,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤育苟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椎木,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體违柏,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年香椎,在試婚紗的時候發(fā)現(xiàn)自己被綠了漱竖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡畜伐,死狀恐怖馍惹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玛界,我是刑警寧澤万矾,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站慎框,受9級特大地震影響勤众,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲤脏,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猎醇,春花似錦窥突、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沦疾,卻和暖如春称近,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哮塞。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工刨秆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忆畅。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓衡未,卻偏偏與公主長得像,于是被迫代替她去往敵國和親家凯。 傳聞我的和親對象是個殘疾皇子缓醋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容