Node.js Core

設(shè)計(jì)高性能Web服務(wù)器的要點(diǎn)在于非阻塞I/O和事件驅(qū)動(dòng)

Node最大的特點(diǎn)是異步式I/O(非阻塞I/O)與事件緊密結(jié)合的編程模式曙痘,此模式與傳統(tǒng)同步式I/O線性的編程思維不同儡嘶,因?yàn)榭刂屏骱艽蟪潭纫揽渴录突卣{(diào)函數(shù)來(lái)組織溜族,一個(gè)邏輯要拆分為若干單元。

Node是JavaScript運(yùn)行時(shí)環(huán)境妨马,為JavaScript提供了一個(gè)異步I/O編程框架盅视。Node的指導(dǎo)思想:CPU執(zhí)行指令是非常快速的询刹,但I(xiàn)/O操作相對(duì)而言是極其緩慢的谜嫉。Node解決的是給CPU執(zhí)行的算法容易,I/O請(qǐng)求卻很頻繁的情況凹联。

同步/異步

當(dāng)請(qǐng)求到來(lái)時(shí)沐兰,相對(duì)于傳統(tǒng)的進(jìn)程或線程同步處理的方式。Node只在主線程中處理請(qǐng)求蔽挠,如果遇到I/O操作則以異步方式發(fā)起調(diào)用住闯,主線程立即返回,繼續(xù)處理之后的任務(wù)。由于異步比原,一次客戶端請(qǐng)求的處理方式由流式變?yōu)殡A段式插佛。而使用Node編寫(xiě)的JavaScript代碼都運(yùn)行在主線程中。

例如:假設(shè)一次客戶端請(qǐng)求分為三個(gè)階段

  1. 執(zhí)行函數(shù)A
  2. 一次I/O操作
  3. 執(zhí)行函數(shù)B

同步式處理

//同步式處理客戶端請(qǐng)求
function request(){
  $result = stageA();//執(zhí)行函數(shù)A
  $data = readfile();//讀取文件量窘,執(zhí)行一次I/O操作
  stageB($result, $data);//執(zhí)行函數(shù)B雇寇,將前兩部的結(jié)果作為參數(shù)。
}
同步式處理流程

同步式處理中每個(gè)請(qǐng)求使用一個(gè)線程或進(jìn)程處理绑改,一次請(qǐng)求處理完畢后線程被回收谢床。上圖同步式處理只顯示了兩個(gè)線程。如果客戶端更多厘线,線程數(shù)量會(huì)隨之增加识腿。

異步式處理

//異步式處理流程
function request(){
  var result = stageA();//執(zhí)行函數(shù)A
  // 發(fā)起異步讀取,此時(shí)主線程立即返回造壮,處理后續(xù)任務(wù)渡讼。
  readfileAsync(function(data){
    //在隨后的循環(huán)中執(zhí)行回調(diào)函數(shù)
    stageB(result, data);
  });
}
異步式處理

Node異步執(zhí)行中,Node使用一個(gè)主線程解決了所有問(wèn)題耳璧,異步處理流程中成箫,每一個(gè)方塊代表了一個(gè)階段任務(wù)的執(zhí)行。

Node高性能的來(lái)源得益于異步的運(yùn)行方式旨枯,如何理解異步對(duì)性能的性能的提升了蹬昌。打個(gè)比方目前出入車(chē)輛管理規(guī)定,外地來(lái)的車(chē)輛進(jìn)京需要辦理進(jìn)京證攀隔,而辦進(jìn)京證需等待一定時(shí)間皂贩。如果每個(gè)人都自己跑去辦理,就好像開(kāi)啟多個(gè)線程同步處理昆汹,辦理窗口有限明刷,就得排隊(duì)。而把這件事委托給第三方满粗,就好比不開(kāi)啟線程或進(jìn)程辈末,將耗時(shí)的I/O請(qǐng)求委托給操作系統(tǒng)。這種情況下人們從辦證的任務(wù)中解放出來(lái)映皆,因而能繼續(xù)做其他事情挤聘。若來(lái)了一個(gè)任務(wù),交給第三方去處理捅彻,則第三方就有一個(gè)接單隊(duì)列檬洞,只需要拿到所有的單,去辦理地點(diǎn)逐個(gè)辦理即可沟饥。


操作系統(tǒng)中一個(gè)杰出的設(shè)計(jì)是線程添怔,操作系統(tǒng)把CPU處理時(shí)間分片后劃分出許多短暫的時(shí)間片湾戳,在時(shí)間T1執(zhí)行一個(gè)線程的指令,到時(shí)間T2再執(zhí)行下一個(gè)線程的指令广料,各個(gè)線程輪流執(zhí)行砾脑,結(jié)果好像是所有線程都在并行前進(jìn)。這樣艾杏,編程時(shí)可以創(chuàng)建多個(gè)線程韧衣,在同一期間執(zhí)行,各個(gè)線程可以并行的完成不同的任務(wù)购桑。

同步與異步

在單線程中畅铭,計(jì)算機(jī)是一臺(tái)嚴(yán)格意義上的馮諾依曼式機(jī)器,一段代碼調(diào)用了另一段代碼時(shí)勃蜘,只能采用同步調(diào)用硕噩。簡(jiǎn)單倆說(shuō),必須等待這段代碼執(zhí)行完畢并返回結(jié)果后缭贡,調(diào)用方才能繼續(xù)向下執(zhí)行炉擅。

有了多線程的支持后,可以采用異步調(diào)用阳惹,也就是說(shuō)谍失,調(diào)用方和被調(diào)方可以屬于不同的線程,調(diào)用方啟動(dòng)被調(diào)方線程后莹汤,不等待對(duì)象返回結(jié)果就繼續(xù)執(zhí)行后續(xù)代碼快鱼。

計(jì)算中有些處理時(shí)比較耗時(shí)的,調(diào)用這種處理代碼時(shí)纲岭,調(diào)用方如果苦苦等待會(huì)嚴(yán)重影響程序的性能攒巍。

異步調(diào)用雖然原理并不復(fù)雜,但在使用中容易出現(xiàn)莫名其妙的問(wèn)題荒勇,特別是不同線程共享代碼或貢獻(xiàn)數(shù)據(jù)時(shí)容易出現(xiàn)問(wèn)題。因此闻坚,要設(shè)計(jì)一個(gè)安全高效的的編程方式需要比較多的設(shè)計(jì)經(jīng)驗(yàn)沽翔,因此最好不要濫用異步。


JavaScript的異步處理上窿凤,ES5的回調(diào)函數(shù)callback使我們陷入地獄仅偎,ES6的承諾promise使我們脫離魔障,ES7的異步等待async-await終于帶領(lǐng)我們走向了光明雳殊。其實(shí)async-awaitpromisegenerator的語(yǔ)法糖橘沥,是為了編碼時(shí)更加流暢,同時(shí)增強(qiáng)代碼的可讀性夯秃。async用來(lái)表示函數(shù)是異步的座咆,使用async定義的函數(shù)會(huì)返回一個(gè)promise對(duì)象痢艺,因此可使用then方法添加回調(diào)函數(shù)。await 可以理解為async wait的縮寫(xiě)介陶,await必須出現(xiàn)在async函數(shù)內(nèi)部堤舒,因此它是不能夠單獨(dú)使用的。await后面可以跟任何JS表達(dá)式哺呜,作用是用來(lái)等待promise對(duì)象的狀態(tài)被resolved舌缤。如果await異步等待的是promise對(duì)象則會(huì)造成異步函數(shù)停止執(zhí)行并且等待promise的解決,如果等待的是正常的表達(dá)式則會(huì)立即執(zhí)行某残。

非阻塞I/O

什么是阻塞(block)呢国撵?線程在執(zhí)行中若遇到磁盤(pán)讀寫(xiě)或網(wǎng)絡(luò)通信(統(tǒng)稱為I/O操作),通常要耗費(fèi)較長(zhǎng)的時(shí)間玻墅,此時(shí)操作系統(tǒng)會(huì)剝奪這個(gè)線程的CPU控制權(quán)介牙,使其暫停執(zhí)行,同時(shí)將資源讓渡給其他工作線程椭豫,這種線程調(diào)度的方式稱為阻塞耻瑟。

  • 傳統(tǒng)同步式I/O(Synchronous I/O)或阻塞式I/O(Blocking I/O)
  • 異步式I/O(Asynchronous I/O)或非阻塞式I/O(Non-blocking I/O)

當(dāng)I/O操作完畢后,操作系統(tǒng)將這個(gè)線程的阻塞狀態(tài)解除赏酥,恢復(fù)其對(duì)CPU的控制權(quán)喳整,令其繼續(xù)執(zhí)行。這種I/O模式即傳統(tǒng)的同步式I/O(Synchronous I/O)或阻塞式I/O(Blocking I/O)裸扶。

異步式I/O(Asynchronous I/O)或非阻塞式I/O(Non-blocking I/O)則針對(duì)所有I/O操作不采用阻塞的策略框都。當(dāng)線程遇到I/O操作時(shí),不會(huì)阻塞的方式等待I/O操作的完成或數(shù)據(jù)的返回呵晨,而只是將I/O請(qǐng)求發(fā)送給操作系統(tǒng)魏保,繼續(xù)執(zhí)行下一條語(yǔ)句。當(dāng)操作系統(tǒng)完成I/O操作時(shí)摸屠,以事件的形式通知執(zhí)行I/O操作的線程谓罗,線程會(huì)在特定時(shí)候處理這個(gè)事件。為了處理異步I/O季二,線程必須有事件循環(huán)檩咱,不斷地檢查有沒(méi)有未處理的事件,依次予以處理胯舷。

阻塞模式下刻蚯,一個(gè)線程只能處理一項(xiàng)任務(wù),要想提高吞吐量必須通過(guò)多線程桑嘶。而非阻塞模式下炊汹,一個(gè)線程永遠(yuǎn)在執(zhí)行計(jì)算操作,這個(gè)線程所使用的CPU核心利用率永遠(yuǎn)是100%逃顶,I/O以事件的方式通知讨便。

為什么Node.js使用單線程充甚、非阻塞的事件編程模型?

在阻塞模式下器钟,多線程往往能提高系統(tǒng)吞吐量津坑,因?yàn)橐粋€(gè)線程阻塞時(shí)還有其他線程在工作,多線程可讓CPU資源不被阻塞中的線程浪費(fèi)傲霸。而在非阻塞模式下疆瑰,線程不會(huì)被I/O阻塞,永遠(yuǎn)在利用CPU昙啄。多線程帶來(lái)的好處僅僅是在多核CPU的情況下利用更多的核穆役,而Node.js的單線程也能帶來(lái)同樣的好處。這就是為什么Node.js使用單線程梳凛、非阻塞的事件編程模型耿币。

多線程同步式I/O
單線程異步式I/O

單線程事件驅(qū)動(dòng)的異步式I/O比傳統(tǒng)的多線程阻塞式I/O好在哪里呢?

簡(jiǎn)而言之韧拒,異步式I/O就是少了多線程的開(kāi)銷(xiāo)淹接。對(duì)操作系統(tǒng)來(lái)說(shuō),創(chuàng)建一個(gè)線程的代價(jià)是十分昂貴的叛溢,需要給它分配內(nèi)存塑悼、列入調(diào)度,同時(shí)在線程切換時(shí)還需執(zhí)行內(nèi)存換頁(yè)楷掉,CPU的緩存被清空厢蒜,切換回來(lái)時(shí)還要重新從內(nèi)存中讀取數(shù)據(jù),破壞了數(shù)據(jù)的局部性烹植。

同步異步

異步I/O

關(guān)于異步I/O典型的場(chǎng)景是AJAX調(diào)用斑鸦,其收到響應(yīng)在是發(fā)送AJAX結(jié)束之后輸出的。在調(diào)用AJAX后草雕,后續(xù)代碼時(shí)被立即執(zhí)行的巷屿,而收到響應(yīng)的執(zhí)行時(shí)間是不被預(yù)期的。我們只知道將在這個(gè)異步請(qǐng)求結(jié)束后執(zhí)行墩虹,但并不知道具體的時(shí)間點(diǎn)嘱巾。異步調(diào)用中對(duì)于結(jié)果值的捕獲是符合“Don't call me, I will call you.”的原則的败晴,這也是注重結(jié)果不關(guān)心過(guò)程的一種表現(xiàn)。

$.post(url, data, function(res){
  console.log('收到響應(yīng)');
});
console.log('發(fā)送AJAX結(jié)束');
AJAX調(diào)用

Node中異步I/O非常常見(jiàn)栽渴,以讀取文件為例尖坤。

var fs = require('fs');
fs.readFile(path, function(err,res){
  console.log('文件讀取完畢');
});
console.log('發(fā)起文件讀取');
異步I/O

Node.js為什么使用單線程?

Node.js保持了JS在瀏覽器中單線程的特點(diǎn)闲擦,在Node中JS與其余線程是無(wú)法共享任何狀態(tài)的慢味。單線程最大好處是不用像多線程編程那樣處處在意狀態(tài)的同步問(wèn)題场梆,這里沒(méi)有死鎖的存在,也沒(méi)有線程上下文交換帶來(lái)的性能上的開(kāi)銷(xiāo)纯路。

同樣或油,單線程也有自身的弱點(diǎn),具體表現(xiàn)在

  • 無(wú)法利用多核CPU
  • 錯(cuò)誤會(huì)引起整個(gè)應(yīng)用退出驰唬,應(yīng)用的健壯性值得考驗(yàn)顶岸。
  • 大量計(jì)算占用CPU導(dǎo)致無(wú)法繼續(xù)調(diào)用異步I/O

像瀏覽器中的JS與UI公用一個(gè)線程一樣,JS長(zhǎng)時(shí)間執(zhí)行會(huì)導(dǎo)致UI的渲染和響應(yīng)被中斷叫编。在Node中辖佣,長(zhǎng)時(shí)間的CPU占用也會(huì)導(dǎo)致后續(xù)的異步I/O發(fā)不出調(diào)用,已完成的異步I/O的回調(diào)函數(shù)也會(huì)得不到及時(shí)執(zhí)行搓逾。

最早解決這種大計(jì)算問(wèn)題的方案是Google公司開(kāi)發(fā)的Gears卷谈,它啟用了一個(gè)完全能獨(dú)立的進(jìn)程,將需要計(jì)算的程序發(fā)送給這個(gè)進(jìn)程霞篡,在結(jié)果得出后世蔗,通過(guò)事件將結(jié)果傳遞回來(lái)。這個(gè)模型將計(jì)算分發(fā)到其他進(jìn)程上朗兵,以次來(lái)降低運(yùn)算造成阻塞的幾率污淋。

后臺(tái)H5制定了Web Workers的標(biāo)準(zhǔn),Google放棄了Gears矛市,全力支持Web Workers芙沥。Web Workers能夠創(chuàng)建工作線程來(lái)進(jìn)行計(jì)算,以解決JS大計(jì)算阻塞UI渲染的問(wèn)題浊吏。工作線程為了不阻塞主線程而昨,通過(guò)消息傳遞的方式來(lái)傳遞運(yùn)行結(jié)果,這也使得工作進(jìn)程不能訪問(wèn)主線程中的UI找田。

Node采用了與Web Workers相同的思路來(lái)解決單線程中大量計(jì)算的問(wèn)題(child_process)歌憨。子進(jìn)程的出現(xiàn),意味著Node可從容地應(yīng)對(duì)單線程在健壯性和無(wú)法利用多核CPU方面的問(wèn)題墩衙。通過(guò)將計(jì)算分發(fā)到各個(gè)子進(jìn)程务嫡,可將大量計(jì)算分解掉,然后在通過(guò)進(jìn)程之間的事件消息來(lái)傳遞結(jié)果漆改,這可以很好地保持應(yīng)用模型的簡(jiǎn)單和低依賴心铃。通過(guò)Master-Worker的管理方式,也可很好地管理各個(gè)工作進(jìn)程挫剑,以達(dá)到更高的健壯性去扣。

關(guān)于如何通過(guò)子進(jìn)程來(lái)充分利用硬件資源和提升應(yīng)用的健壯性,這是一個(gè)值得探究的話題樊破。

事件循環(huán)

Node.js所有的異步I/O操作在完成時(shí)都會(huì)發(fā)送一個(gè)事件到事件隊(duì)列愉棱。從開(kāi)發(fā)看來(lái)事件由EventEmitter對(duì)象提供唆铐。

Node.js在什么時(shí)候會(huì)進(jìn)入事件循環(huán)呢?Node.js程序由事件循環(huán)開(kāi)始到事件循環(huán)結(jié)束奔滑,所有的邏輯都是事件的回調(diào)函數(shù)艾岂,所以Node.js始終在事件循環(huán)中,程序入口就是事件循環(huán)第一個(gè)事件的回調(diào)函數(shù)朋其。事件的回調(diào)函數(shù)在執(zhí)行過(guò)程中王浴,可能會(huì)發(fā)出I/O請(qǐng)求或直接發(fā)射(emit)事件,執(zhí)行完畢后再返回事件循環(huán)令宿,事件循環(huán)會(huì)檢查事件隊(duì)列中有沒(méi)有未處理的事件叼耙,直至程序結(jié)束。

事件循環(huán)機(jī)制

Node.js沒(méi)有顯式的事件循環(huán)粒没,它對(duì)開(kāi)發(fā)者不可見(jiàn)筛婉,由libev庫(kù)實(shí)現(xiàn)。libev支持多種類(lèi)型的事件癞松,如ev_io爽撒、ev_timer、ev_signal响蓉、ev_idle等硕勿,在Node.js中均被EventEmitter封裝。libev事件循環(huán)的每一次迭代枫甲,在Node.js中就是一次Tick源武,libev不斷檢查是否有活動(dòng)的、可供檢測(cè)的事件監(jiān)聽(tīng)器想幻,直至檢測(cè)不到時(shí)才退出事件循環(huán)粱栖,進(jìn)程結(jié)束。

異步I/O與事件驅(qū)動(dòng)

Node.js采用異步式I/O與事件驅(qū)動(dòng)的設(shè)計(jì)脏毯,對(duì)于高并發(fā)的解決方案闹究,傳統(tǒng)采用多線程模型,也就是為每個(gè)業(yè)務(wù)邏輯提供一個(gè)系統(tǒng)線程食店,通過(guò)系統(tǒng)線程切換來(lái)彌補(bǔ)同步式I/O調(diào)用時(shí)的時(shí)間開(kāi)銷(xiāo)渣淤。

Node.js采用單線程模型,對(duì)于所有I/O都采用異步式的請(qǐng)求方式吉嫩,避免頻繁的上下文切換价认。Node在執(zhí)行過(guò)程中會(huì)維護(hù)一個(gè)時(shí)間隊(duì)列,程序在執(zhí)行時(shí)進(jìn)入事件循環(huán)等待下一個(gè)事件到來(lái)自娩,每個(gè)異步式I/O請(qǐng)求完成后會(huì)被推送到事件隊(duì)列用踩,等待程序進(jìn)程進(jìn)行處理。


事件循環(huán)

Node.js的異步機(jī)制是基于事件的,所有磁盤(pán)I/O捶箱、網(wǎng)絡(luò)通信、數(shù)據(jù)庫(kù)查詢都以非阻塞的方式請(qǐng)求动漾,返回的結(jié)果由事件循環(huán)來(lái)處理丁屎。

Node.js進(jìn)程在同一時(shí)刻只會(huì)處理一個(gè)事件,完成后立即進(jìn)入事件循環(huán)檢查并處理后續(xù)的事件旱眯。其好處是CPU和內(nèi)存在同一時(shí)刻集中處理一件事晨川,同時(shí)盡可能讓耗時(shí)的I/O操作并行執(zhí)行。對(duì)于低速連接攻擊删豺,Node.js只是在事件隊(duì)列中增加請(qǐng)求共虑,等待操作系統(tǒng)的回應(yīng),因而不會(huì)有任何多線程開(kāi)銷(xiāo)呀页,很大程度可提高Web應(yīng)用的健壯性妈拌,防止惡意攻擊。

異步事件模式的弊端是不符合開(kāi)發(fā)者的常規(guī)線性思路蓬蝶,需要把一個(gè)完整的邏輯拆分為一個(gè)個(gè)事件尘分,增加了開(kāi)發(fā)和調(diào)試的難度。

Node技術(shù)架構(gòu)
Node基于libuv實(shí)現(xiàn)跨平臺(tái)的架構(gòu)
Node與PHP的架構(gòu)比較
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丸氛,一起剝皮案震驚了整個(gè)濱河市培愁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缓窜,老刑警劉巖定续,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異禾锤,居然都是意外死亡私股,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)时肿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)庇茫,“玉大人,你說(shuō)我怎么就攤上這事螃成〉┣” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵寸宏,是天一觀的道長(zhǎng)宁炫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)氮凝,這世上最難降的妖魔是什么羔巢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上竿秆,老公的妹妹穿的比我還像新娘启摄。我一直安慰自己,他們只是感情好幽钢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布歉备。 她就那樣靜靜地躺著,像睡著了一般匪燕。 火紅的嫁衣襯著肌膚如雪蕾羊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天帽驯,我揣著相機(jī)與錄音龟再,去河邊找鬼。 笑死尼变,一個(gè)胖子當(dāng)著我的面吹牛利凑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嫌术,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼截碴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蛉威?” 一聲冷哼從身側(cè)響起日丹,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚯嫌,沒(méi)想到半個(gè)月后哲虾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡择示,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年束凑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栅盲。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汪诉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谈秫,到底是詐尸還是另有隱情扒寄,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布拟烫,位于F島的核電站该编,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏硕淑。R本人自食惡果不足惜课竣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一嘉赎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧于樟,春花似錦公条、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至奢米,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纠永,已是汗流浹背鬓长。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尝江,地道東北人涉波。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像炭序,于是被迫代替她去往敵國(guó)和親啤覆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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