Go 為什么這么“快”

本文主要從 Go 調(diào)度器架構(gòu)層面上介紹了 G-P-M 模型,通過(guò)該模型怎樣實(shí)現(xiàn)少量?jī)?nèi)核線程支撐大量 Goroutine 的并發(fā)運(yùn)行舵稠。以及通過(guò) NetPoller、sysmon 等幫助 Go 程序減少線程阻塞,充分利用已有的計(jì)算資源哺徊,從而最大限度提高 Go 程序的運(yùn)行效率室琢。

本文主要介紹了 Go 程序?yàn)榱藢?shí)現(xiàn)極高的并發(fā)性能,其內(nèi)部調(diào)度器的實(shí)現(xiàn)架構(gòu)(G-P-M 模型)落追,以及為了最大限度利用計(jì)算資源盈滴,Go 調(diào)度器是如何處理線程阻塞的場(chǎng)景。

怎么讓我們的系統(tǒng)更快

隨著信息技術(shù)的迅速發(fā)展轿钠,單臺(tái)服務(wù)器處理能力越來(lái)越強(qiáng)巢钓,迫使編程模式由從前的串行模式升級(jí)到并發(fā)模型。

并發(fā)模型包含 IO 多路復(fù)用疗垛、多進(jìn)程以及多線程症汹,這幾種模型都各有優(yōu)劣,現(xiàn)代復(fù)雜的高并發(fā)架構(gòu)大多是幾種模型協(xié)同使用贷腕,不同場(chǎng)景應(yīng)用不同模型背镇,揚(yáng)長(zhǎng)避短,發(fā)揮服務(wù)器的最大性能泽裳。

而多線程瞒斩,因?yàn)槠漭p量和易用,成為并發(fā)編程中使用頻率最高的并發(fā)模型涮总,包括后衍生的協(xié)程等其他子產(chǎn)品济瓢,也都基于它。

并發(fā) ≠ 并行

并發(fā) (concurrency) 和 并行 ( parallelism) 是不同的妹卿。

在單個(gè) CPU 核上旺矾,線程通過(guò)時(shí)間片或者讓出控制權(quán)來(lái)實(shí)現(xiàn)任務(wù)切換,達(dá)到 "同時(shí)" 運(yùn)行多個(gè)任務(wù)的目的夺克,這就是所謂的并發(fā)箕宙。但實(shí)際上任何時(shí)刻都只有一個(gè)任務(wù)被執(zhí)行,其他任務(wù)通過(guò)某種算法來(lái)排隊(duì)铺纽。

多核 CPU 可以讓同一進(jìn)程內(nèi)的 "多個(gè)線程" 做到真正意義上的同時(shí)運(yùn)行柬帕,這才是并行。

進(jìn)程狡门、線程陷寝、協(xié)程

進(jìn)程:進(jìn)程是系統(tǒng)進(jìn)行資源分配的基本單位,有獨(dú)立的內(nèi)存空間其馏。

線程:線程是 CPU 調(diào)度和分派的基本單位凤跑,線程依附于進(jìn)程存在,每個(gè)線程會(huì)共享父進(jìn)程的資源叛复。

協(xié)程:協(xié)程是一種用戶態(tài)的輕量級(jí)線程仔引,協(xié)程的調(diào)度完全由用戶控制扔仓,協(xié)程間切換只需要保存任務(wù)的上下文,沒(méi)有內(nèi)核的開(kāi)銷咖耘。

線程上下文切換

由于中斷處理翘簇,多任務(wù)處理,用戶態(tài)切換等原因會(huì)導(dǎo)致 CPU 從一個(gè)線程切換到另一個(gè)線程儿倒,切換過(guò)程需要保存當(dāng)前進(jìn)程的狀態(tài)并恢復(fù)另一個(gè)進(jìn)程的狀態(tài)版保。

上下文切換的代價(jià)是高昂的,因?yàn)樵诤诵纳辖粨Q線程會(huì)花費(fèi)很多時(shí)間夫否。上下文切換的延遲取決于不同的因素找筝,大概在在 50 到 100 納秒之間】兜酰考慮到硬件平均在每個(gè)核心上每納秒執(zhí)行 12 條指令袖裕,那么一次上下文切換可能會(huì)花費(fèi) 600 到 1200 條指令的延遲時(shí)間。實(shí)際上溉瓶,上下文切換占用了大量程序執(zhí)行指令的時(shí)間急鳄。

如果存在跨核上下文切換(Cross-Core Context Switch),可能會(huì)導(dǎo)致 CPU 緩存失效(CPU 從緩存訪問(wèn)數(shù)據(jù)的成本大約 3 到 40 個(gè)時(shí)鐘周期堰酿,從主存訪問(wèn)數(shù)據(jù)的成本大約 100 到 300 個(gè)時(shí)鐘周期)疾宏,這種場(chǎng)景的切換成本會(huì)更加昂貴。

Golang 為并發(fā)而生

Golang 從 2009 年正式發(fā)布以來(lái)触创,依靠其極高運(yùn)行速度和高效的開(kāi)發(fā)效率坎藐,迅速占據(jù)市場(chǎng)份額。Golang 從語(yǔ)言級(jí)別支持并發(fā)哼绑,通過(guò)輕量級(jí)協(xié)程 Goroutine 來(lái)實(shí)現(xiàn)程序并發(fā)運(yùn)行岩馍。

Goroutine 非常輕量,主要體現(xiàn)在以下兩個(gè)方面:

上下文切換代價(jià)卸逗: Goroutine 上下文切換只涉及到三個(gè)寄存器(PC / SP / DX)的值修改;而對(duì)比線程的上下文切換則需要涉及模式切換(從用戶態(tài)切換到內(nèi)核態(tài))蛀恩、以及 16 個(gè)寄存器、PC茂浮、SP…等寄存器的刷新;

內(nèi)存占用少:線程椝唬空間通常是 2M,Goroutine 椣浚空間最小 2K;

Golang 程序中可以輕松支持10w 級(jí)別的 Goroutine 運(yùn)行顽馋,而線程數(shù)量達(dá)到 1k 時(shí),內(nèi)存占用就已經(jīng)達(dá)到 2G幌羞。

Go 調(diào)度器實(shí)現(xiàn)機(jī)制:

Go 程序通過(guò)調(diào)度器來(lái)調(diào)度Goroutine 在內(nèi)核線程上執(zhí)行寸谜,但是 Goroutine 并不直接綁定 OS 線程 M - Machine運(yùn)行,而是由 Goroutine Scheduler 中的 P - Processor (邏輯處理器)來(lái)作獲取內(nèi)核線程資源的『中介』新翎。

Go 調(diào)度器模型我們通常叫做G-P-M 模型程帕,他包括 4 個(gè)重要結(jié)構(gòu)住练,分別是G地啰、P愁拭、M、Sched:

G:Goroutine亏吝,每個(gè) Goroutine 對(duì)應(yīng)一個(gè) G 結(jié)構(gòu)體岭埠,G 存儲(chǔ) Goroutine 的運(yùn)行堆棧、狀態(tài)以及任務(wù)函數(shù)蔚鸥,可重用惜论。

G 并非執(zhí)行體,每個(gè) G 需要綁定到 P 才能被調(diào)度執(zhí)行止喷。

P: Processor馆类,表示邏輯處理器,對(duì) G 來(lái)說(shuō)弹谁,P 相當(dāng)于 CPU 核乾巧,G 只有綁定到 P 才能被調(diào)度。對(duì) M 來(lái)說(shuō)预愤,P 提供了相關(guān)的執(zhí)行環(huán)境(Context)沟于,如內(nèi)存分配狀態(tài)(mcache),任務(wù)隊(duì)列(G)等植康。

P 的數(shù)量決定了系統(tǒng)內(nèi)最大可并行的 G 的數(shù)量(前提:物理 CPU 核數(shù) >= P 的數(shù)量)旷太。

P 的數(shù)量由用戶設(shè)置的 GoMAXPROCS 決定,但是不論 GoMAXPROCS 設(shè)置為多大销睁,P 的數(shù)量最大為 256供璧。

M: Machine,OS 內(nèi)核線程抽象冻记,代表著真正執(zhí)行計(jì)算的資源嗜傅,在綁定有效的 P 后,進(jìn)入 schedule 循環(huán);而 schedule 循環(huán)的機(jī)制大致是從 Global 隊(duì)列檩赢、P 的 Local 隊(duì)列以及 wait 隊(duì)列中獲取吕嘀。

M 的數(shù)量是不定的,由 Go Runtime 調(diào)整贞瞒,為了防止創(chuàng)建過(guò)多 OS 線程導(dǎo)致系統(tǒng)調(diào)度不過(guò)來(lái)偶房,目前默認(rèn)最大限制為 10000 個(gè)。

M 并不保留 G 狀態(tài)军浆,這是 G 可以跨 M 調(diào)度的基礎(chǔ)棕洋。

Sched:Go 調(diào)度器,它維護(hù)有存儲(chǔ) M 和 G 的隊(duì)列以及調(diào)度器的一些狀態(tài)信息等乒融。

調(diào)度器循環(huán)的機(jī)制大致是從各種隊(duì)列掰盘、P 的本地隊(duì)列中獲取 G摄悯,切換到 G 的執(zhí)行棧上并執(zhí)行 G 的函數(shù),調(diào)用 Goexit 做清理工作并回到 M愧捕,如此反復(fù)奢驯。

理解 M、P次绘、G 三者的關(guān)系瘪阁,可以通過(guò)經(jīng)典的地鼠推車搬磚的模型來(lái)說(shuō)明其三者關(guān)系:

地鼠(Gopher)的工作任務(wù)是:工地上有若干磚頭,地鼠借助小車把磚頭運(yùn)送到火種上去燒制邮偎。M 就可以看作圖中的地鼠管跺,P 就是小車,G 就是小車?yán)镅b的磚禾进。

弄清楚了它們?nèi)叩年P(guān)系豁跑,下面我們就開(kāi)始重點(diǎn)聊地鼠是如何在搬運(yùn)磚塊的。

Processor(P):

根據(jù)用戶設(shè)置的 GoMAXPROCS 值來(lái)創(chuàng)建一批小車(P)泻云。

Goroutine(G):

通過(guò) Go 關(guān)鍵字就是用來(lái)創(chuàng)建一個(gè) Goroutine艇拍,也就相當(dāng)于制造一塊磚(G),然后將這塊磚(G)放入當(dāng)前這輛小車(P)中壶愤。

Machine (M):

地鼠(M)不能通過(guò)外部創(chuàng)建出來(lái)淑倾,只能磚(G)太多了,地鼠(M)又太少了征椒,實(shí)在忙不過(guò)來(lái)娇哆,剛好還有空閑的小車(P)沒(méi)有使用,那就從別處再借些地鼠(M)過(guò)來(lái)直到把小車(P)用完為止勃救。

這里有一個(gè)地鼠(M)不夠用碍讨,從別處借地鼠(M)的過(guò)程,這個(gè)過(guò)程就是創(chuàng)建一個(gè)內(nèi)核線程(M)蒙秒。

需要注意的是:地鼠(M) 如果沒(méi)有小車(P)是沒(méi)辦法運(yùn)磚的勃黍,小車(P)的數(shù)量決定了能夠干活的地鼠(M)數(shù)量,在 Go 程序里面對(duì)應(yīng)的是活動(dòng)線程數(shù);

在 Go 程序里我們通過(guò)下面的圖示來(lái)展示 G-P-M 模型:

P 代表可以“并行”運(yùn)行的邏輯處理器晕讲,每個(gè) P 都被分配到一個(gè)系統(tǒng)線程 M覆获,G 代表 Go 協(xié)程。

Go 調(diào)度器中有兩個(gè)不同的運(yùn)行隊(duì)列:全局運(yùn)行隊(duì)列(GRQ)和本地運(yùn)行隊(duì)列(LRQ)瓢省。

每個(gè) P 都有一個(gè) LRQ弄息,用于管理分配給在 P 的上下文中執(zhí)行的 Goroutines,這些 Goroutine 輪流被和 P 綁定的 M 進(jìn)行上下文切換勤婚。GRQ 適用于尚未分配給 P 的 Goroutines摹量。

從上圖可以看出,G 的數(shù)量可以遠(yuǎn)遠(yuǎn)大于 M 的數(shù)量,換句話說(shuō)缨称,Go 程序可以利用少量的內(nèi)核級(jí)線程來(lái)支撐大量 Goroutine 的并發(fā)凝果。多個(gè) Goroutine 通過(guò)用戶級(jí)別的上下文切換來(lái)共享內(nèi)核線程 M 的計(jì)算資源,但對(duì)于操作系統(tǒng)來(lái)說(shuō)并沒(méi)有線程上下文切換產(chǎn)生的性能損耗睦尽。

為了更加充分利用線程的計(jì)算資源器净,Go 調(diào)度器采取了以下幾種調(diào)度策略:

任務(wù)竊取(work-stealing)

我們知道,現(xiàn)實(shí)情況有的 Goroutine 運(yùn)行的快骂删,有的慢掌动,那么勢(shì)必肯定會(huì)帶來(lái)的問(wèn)題就是四啰,忙的忙死宁玫,閑的閑死,Go 肯定不允許摸魚(yú)的 P 存在柑晒,勢(shì)必要充分利用好計(jì)算資源欧瘪。

為了提高 Go 并行處理能力,調(diào)高整體處理效率匙赞,當(dāng)每個(gè) P 之間的 G 任務(wù)不均衡時(shí)佛掖,調(diào)度器允許從 GRQ,或者其他 P 的 LRQ 中獲取 G 執(zhí)行涌庭。

減少阻塞

如果正在執(zhí)行的 Goroutine 阻塞了線程 M 怎么辦?P 上 LRQ 中的 Goroutine 會(huì)獲取不到調(diào)度么?

在 Go 里面阻塞主要分為一下 4 種場(chǎng)景:

場(chǎng)景 1:由于原子芥被、互斥量或通道操作調(diào)用導(dǎo)致 Goroutine 阻塞,調(diào)度器將把當(dāng)前阻塞的 Goroutine 切換出去坐榆,重新調(diào)度 LRQ 上的其他 Goroutine;

場(chǎng)景 2:由于網(wǎng)絡(luò)請(qǐng)求和 IO 操作導(dǎo)致 Goroutine 阻塞拴魄,這種阻塞的情況下,我們的 G 和 M 又會(huì)怎么做呢?

Go 程序提供了網(wǎng)絡(luò)輪詢器(NetPoller)來(lái)處理網(wǎng)絡(luò)請(qǐng)求和 IO 操作的問(wèn)題席镀,其后臺(tái)通過(guò) kqueue(MacOS)匹中,epoll(Linux)或 iocp(Windows)來(lái)實(shí)現(xiàn) IO 多路復(fù)用。

通過(guò)使用 NetPoller 進(jìn)行網(wǎng)絡(luò)系統(tǒng)調(diào)用豪诲,調(diào)度器可以防止 Goroutine 在進(jìn)行這些系統(tǒng)調(diào)用時(shí)阻塞 M顶捷。這可以讓 M 執(zhí)行 P 的 LRQ 中其他的 Goroutines,而不需要?jiǎng)?chuàng)建新的 M屎篱。有助于減少操作系統(tǒng)上的調(diào)度負(fù)載服赎。

下圖展示它的工作原理:G1 正在 M 上執(zhí)行,還有 3 個(gè) Goroutine 在 LRQ 上等待執(zhí)行交播。網(wǎng)絡(luò)輪詢器空閑著重虑,什么都沒(méi)干。

接下來(lái)堪侯,G1 想要進(jìn)行網(wǎng)絡(luò)系統(tǒng)調(diào)用嚎尤,因此它被移動(dòng)到網(wǎng)絡(luò)輪詢器并且處理異步網(wǎng)絡(luò)系統(tǒng)調(diào)用。然后伍宦,M 可以從 LRQ 執(zhí)行另外的 Goroutine芽死。此時(shí)乏梁,G2 就被上下文切換到 M 上了。

最后,異步網(wǎng)絡(luò)系統(tǒng)調(diào)用由網(wǎng)絡(luò)輪詢器完成搓译,G1 被移回到 P 的 LRQ 中棉浸。一旦 G1 可以在 M 上進(jìn)行上下文切換,它負(fù)責(zé)的 Go 相關(guān)代碼就可以再次執(zhí)行落萎。這里的最大優(yōu)勢(shì)是,執(zhí)行網(wǎng)絡(luò)系統(tǒng)調(diào)用不需要額外的 M炭剪。網(wǎng)絡(luò)輪詢器使用系統(tǒng)線程练链,它時(shí)刻處理一個(gè)有效的事件循環(huán)。

這種調(diào)用方式看起來(lái)很復(fù)雜奴拦,值得慶幸的是媒鼓,Go 語(yǔ)言將該“復(fù)雜性”隱藏在 Runtime 中:Go 開(kāi)發(fā)者無(wú)需關(guān)注 socket 是否是 non-block 的,也無(wú)需親自注冊(cè)文件描述符的回調(diào)错妖,只需在每個(gè)連接對(duì)應(yīng)的 Goroutine 中以“block I/O”的方式對(duì)待 socket 處理即可绿鸣,實(shí)現(xiàn)了 goroutine-per-connection 簡(jiǎn)單的網(wǎng)絡(luò)編程模式(但是大量的 Goroutine 也會(huì)帶來(lái)額外的問(wèn)題,比如棧內(nèi)存增加和調(diào)度器負(fù)擔(dān)加重)暂氯。

用戶層眼中看到的 Goroutine 中的“block socket”潮模,實(shí)際上是通過(guò) Go runtime 中的 netpoller 通過(guò) Non-block socket + I/O 多路復(fù)用機(jī)制“模擬”出來(lái)的。Go 中的 net 庫(kù)正是按照這方式實(shí)現(xiàn)的痴施。

場(chǎng)景 3:當(dāng)調(diào)用一些系統(tǒng)方法的時(shí)候擎厢,如果系統(tǒng)方法調(diào)用的時(shí)候發(fā)生阻塞,這種情況下晾剖,網(wǎng)絡(luò)輪詢器(NetPoller)無(wú)法使用锉矢,而進(jìn)行系統(tǒng)調(diào)用的 Goroutine 將阻塞當(dāng)前 M。

讓我們來(lái)看看同步系統(tǒng)調(diào)用(如文件 I/O)會(huì)導(dǎo)致 M 阻塞的情況:G1 將進(jìn)行同步系統(tǒng)調(diào)用以阻塞 M1齿尽。

調(diào)度器介入后:識(shí)別出 G1 已導(dǎo)致 M1 阻塞沽损,此時(shí),調(diào)度器將 M1 與 P 分離循头,同時(shí)也將 G1 帶走绵估。然后調(diào)度器引入新的 M2 來(lái)服務(wù) P。此時(shí)卡骂,可以從 LRQ 中選擇 G2 并在 M2 上進(jìn)行上下文切換国裳。

阻塞的系統(tǒng)調(diào)用完成后:G1 可以移回 LRQ 并再次由 P 執(zhí)行。如果這種情況再次發(fā)生全跨,M1 將被放在旁邊以備將來(lái)重復(fù)使用缝左。

場(chǎng)景 4:如果在 Goroutine 去執(zhí)行一個(gè) sleep 操作,導(dǎo)致 M 被阻塞了。

Go 程序后臺(tái)有一個(gè)監(jiān)控線程 sysmon渺杉,它監(jiān)控那些長(zhǎng)時(shí)間運(yùn)行的 G 任務(wù)然后設(shè)置可以強(qiáng)占的標(biāo)識(shí)符蛇数,別的 Goroutine 就可以搶先進(jìn)來(lái)執(zhí)行。

只要下次這個(gè) Goroutine 進(jìn)行函數(shù)調(diào)用是越,那么就會(huì)被強(qiáng)占耳舅,同時(shí)也會(huì)保護(hù)現(xiàn)場(chǎng),然后重新放入 P 的本地隊(duì)列里面等待下次執(zhí)行倚评。

小結(jié)

本文主要從 Go 調(diào)度器架構(gòu)層面上介紹了 G-P-M 模型浦徊,通過(guò)該模型怎樣實(shí)現(xiàn)少量?jī)?nèi)核線程支撐大量 Goroutine 的并發(fā)運(yùn)行。以及通過(guò) NetPoller天梧、sysmon 等幫助 Go 程序減少線程阻塞盔性,充分利用已有的計(jì)算資源,從而最大限度提高 Go 程序的運(yùn)行效率腿倚。

以上內(nèi)容都是我自己的一些感想纯出,分享出來(lái)歡迎大家指正蚯妇,順便求一波關(guān)注敷燎,有想法的伙伴可以評(píng)論或者私信我哦~

作者:joellwang

來(lái)源:騰訊技術(shù)工程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箩言,隨后出現(xiàn)的幾起案子硬贯,更是在濱河造成了極大的恐慌,老刑警劉巖陨收,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饭豹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡务漩,警方通過(guò)查閱死者的電腦和手機(jī)拄衰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饵骨,“玉大人翘悉,你說(shuō)我怎么就攤上這事【哟ィ” “怎么了妖混?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)轮洋。 經(jīng)常有香客問(wèn)我制市,道長(zhǎng),這世上最難降的妖魔是什么弊予? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任祥楣,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘误褪。我一直安慰自己床未,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布振坚。 她就那樣靜靜地躺著薇搁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渡八。 梳的紋絲不亂的頭發(fā)上啃洋,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音屎鳍,去河邊找鬼宏娄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛逮壁,可吹牛的內(nèi)容都是我干的孵坚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼窥淆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卖宠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起忧饭,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扛伍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后词裤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體刺洒,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年吼砂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逆航。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渔肩,死狀恐怖因俐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赖瞒,我是刑警寧澤女揭,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站栏饮,受9級(jí)特大地震影響吧兔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袍嬉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一境蔼、第九天 我趴在偏房一處隱蔽的房頂上張望灶平。 院中可真熱鬧,春花似錦箍土、人聲如沸逢享。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞒爬。三九已至,卻和暖如春沟堡,著一層夾襖步出監(jiān)牢的瞬間侧但,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工航罗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留禀横,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓粥血,卻偏偏與公主長(zhǎng)得像柏锄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子复亏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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