epoll驚群效應(yīng)深度剖析

前情提要

我們一個基于Nginx+uWSGI+python的服務(wù)最近在高峰期經(jīng)常會遇到負(fù)載高導(dǎo)致一些請求報錯的情況禁筏,在單機qps只有差不多2000-3000左右的時候內(nèi)核的cpu占用竟然高達(dá)超過20%枕屉,內(nèi)核每秒上下文切換超過200w次,分析之后發(fā)現(xiàn)是nginx+uwsgi引發(fā)了驚群效應(yīng)盔沫,導(dǎo)致性能急劇下降钠四,通過上鎖解決驚群問題之后服務(wù)恢復(fù)踱阿⊥赴眨基于這個排查過程,再加上我之前寫過的關(guān)于epoll的分析最后也把驚群效應(yīng)一筆帶過虏肾,當(dāng)時沒有寫完整廓啊,那咱這次就好好聊聊這個話題,我會先詳細(xì)分析一下驚群效應(yīng)產(chǎn)生的原因封豪,然后拿nginx和uwsgi出來討論一下他們各自對這種問題是如何處理的谴轮,他們的方案優(yōu)劣是怎樣的 本文關(guān)于源碼的分析分別基于:linux 2.6及4.5內(nèi)核、nginx1.8及1.16撑毛,uWSGI2.20

還不明白的朋友可以回頭看看這個Nginx源碼驚群視頻講解:Nginx驚群

1. 不使用epoll/select的情況下多進程是如何共享端口監(jiān)聽的?

不使用多路復(fù)用的情況下唧领,進程要接受tcp連接必然要調(diào)用accept并且被阻塞藻雌,直到有一條連接到達(dá),在這之前無法做別的事情斩个,也即是說單個進程一次只能處理一條連接胯杭,業(yè)務(wù)處理完成之后調(diào)用close關(guān)閉連接,然后繼續(xù)等待accept受啥,循環(huán)往復(fù)做个,這種情況下是無法實現(xiàn)高并發(fā)的,所以一般會使用多進程再來同時處理更多的連接滚局,多進程一般情況下有兩種模式

第一種是由一個主進程進行accept監(jiān)聽居暖,接受一個連接之后再fork出一個子進程,把連接丟給子進程去進行業(yè)務(wù)處理藤肢,然后主進程繼續(xù)監(jiān)聽太闺,這個是最簡單的模式,由于只有一個進程在使用accept進行監(jiān)聽嘁圈,不涉及多進程爭搶的問題省骂,當(dāng)tcp連接事件到達(dá)后也只會喚醒這個監(jiān)聽進程,自然也不存在驚群效應(yīng)

第二種形式是由主進程fork出一批子進程最住,子進程繼承了父進程的這個監(jiān)聽端口钞澳,大家共享,然后一起監(jiān)聽涨缚。這里面就涉及到當(dāng)多個進程在阻塞狀態(tài)中等待同一個端口事件時內(nèi)核的行為轧粟,接下來重點分析一下這個場景

// 進程調(diào)用accept時會進入inet_csk_accept,這是accept的核心所在

struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)

{

struct inet_connection_sock *icsk = inet_csk(sk);

struct sock *newsk;

int error;

lock_sock(sk);

/* We need to make sure that this socket is listening,

* and that it has something pending.

*/

error = -EINVAL;

? ? // 確認(rèn)socket處于監(jiān)聽狀態(tài)

if (sk->sk_state != TCP_LISTEN)

goto out_err;

/* Find already established connection */

? ? /*接下來要找到一個建立好的連接*/

if (reqsk_queue_empty(&icsk->icsk_accept_queue)) { // 如果sock的連接隊列是空

long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);

/* If this is a non blocking socket don't sleep */

error = -EAGAIN;

if (!timeo) // 如果設(shè)置了非阻塞模式則直接返回,err是喜聞樂見的-EAGAIN

goto out_err;

? ? ? ? // 如果處于阻塞模式逃延,則進入inet_csk_wait_for_connect览妖,進程將處于阻塞狀態(tài),直接到新到的連接喚醒

error = inet_csk_wait_for_connect(sk, timeo);

if (error)

goto out_err;

}

? ? // 到這里揽祥,連接隊列會有至少一條可用連接用到返回

newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);

WARN_ON(newsk->sk_state == TCP_SYN_RECV);

out:

release_sock(sk);

return newsk;

out_err:

newsk = NULL;

*err = error;

goto out;

}

EXPORT_SYMBOL(inet_csk_accept);

// inet_csk_wait_for_connect會將進程掛起讽膏,直到被新到的連接喚醒

static int inet_csk_wait_for_connect(struct sock *sk, long timeo)

{

struct inet_connection_sock *icsk = inet_csk(sk);

DEFINE_WAIT(wait); // 定義一個等待節(jié)點,用于掛在socket監(jiān)聽隊列下

int err;

for (;;) {

? ? ? ? // 使用prepare_to_wait_exclusive確認(rèn)互斥等待拄丰,在一個事件到達(dá)后內(nèi)核只會喚醒等待隊列中的一個進程

prepare_to_wait_exclusive(sk_sleep(sk), &wait,

? TASK_INTERRUPTIBLE);

release_sock(sk);

if (reqsk_queue_empty(&icsk->icsk_accept_queue))

? ? ? ? ? ? // 再一次判斷隊列是否空府树,空則進入調(diào)度,此時當(dāng)前進程將被掛起

timeo = schedule_timeout(timeo);

lock_sock(sk);

err = 0;

if (!reqsk_queue_empty(&icsk->icsk_accept_queue))

break;

err = -EINVAL;

if (sk->sk_state != TCP_LISTEN)

break;

err = sock_intr_errno(timeo);

if (signal_pending(current))

break;

err = -EAGAIN;

if (!timeo)

break;

}

finish_wait(sk_sleep(sk), &wait);

return err;

}

我摘取了linux內(nèi)核中關(guān)于accept部分的核心代碼料按,Linux提供了accept4的系統(tǒng)調(diào)用奄侠,accept4最終將調(diào)用上述的inet_csk_accept,而inet_csk_accept最終調(diào)用inet_csk_wait_for_connect载矿,如果此時沒有連接可用垄潮,內(nèi)核會將當(dāng)前進程掛起,其中的點在于掛起進程使用的是prepare_to_wait_exclusive這個函數(shù)闷盔,不存在多進程喚醒

PS:如果對內(nèi)核原理沒有一定的基礎(chǔ)弯洗,可能不會知道prepare_to_wait_exclusive是什么東西,簡單來說linux的內(nèi)核對進程喚醒提供了兩種模式逢勾,一種是prepare_to_wait牡整,一種是prepare_to_wait_exclusive,exclusive即互斥溺拱,如果調(diào)用的是prepare_to_wait_exclusive逃贝,則在對一個等待隊列進程喚醒的時候,只會喚醒一個進程迫摔,而prepare_to_wait沒有設(shè)置互斥位沐扳,會將掛在這個等待隊列上的所有進程全部喚醒

綜上可知,在普通的多進程共享監(jiān)聽端口的情況下句占,內(nèi)核對一個新的連接事件的到達(dá)迫皱,只會喚醒其中一個進程

可直接看以上流程圖,父進程創(chuàng)建的監(jiān)聽socket fd1由fork出來的兩個子進程共享辖众,這時候子進程的兩個fd在內(nèi)核中是屬于同一個文件卓起,被記錄在open files table這個表中,接下來第4凹炸、5步戏阅,兩個子進程同時調(diào)用accept進行阻塞監(jiān)聽,兩個進程都會被掛起來啤它,內(nèi)核會在這個socket的等待隊列wait queue鏈表中將兩個PID記錄下來以便喚醒奕筐;在第8步中一個連接事件到達(dá)舱痘,內(nèi)核將對應(yīng)socket下的等待隊列取出來,對于tcp連接事件而言离赫,內(nèi)核對一個連接事件只會喚醒一個進程芭逝,取出wait queue鏈表的第一個節(jié)點,將對應(yīng)的進程喚醒渊胸,此時PID1進程的accept成功取到連接并返回用戶態(tài)旬盯,PID2沒有被喚醒

其實在linux 2.6之前的版本中,accept也會全量喚醒wait queue中的所有進程翎猛,同樣造成了驚群效應(yīng)胖翰,在2.6中增加了互斥標(biāo)志,修復(fù)了這個問題

分享更多關(guān)于 Linux后端開發(fā)網(wǎng)絡(luò)底層原理知識學(xué)習(xí)提升 點擊 學(xué)習(xí)資料 獲取切厘,完善技術(shù)棧萨咳,內(nèi)容知識點包括Linux,Nginx疫稿,ZeroMQ培他,MySQL,Redis遗座,線程池舀凛,MongoDB,ZK员萍,Linux內(nèi)核腾降,CDN拣度,P2P碎绎,epoll,Docker抗果,TCP/IP筋帖,協(xié)程,DPDK等等冤馏。

2. epoll下共享監(jiān)聽端口的行為

接下來看看在使用epoll的情況下日麸,也存在多個進程一起監(jiān)聽端口的情況,最經(jīng)典的例如nginx逮光,多個worker會一起監(jiān)聽同一個端口代箭,而在它1.11版本之前,使用的是上述第一節(jié)講的方式涕刚,master進程創(chuàng)建一個監(jiān)聽端口之后嗡综,通過fork的方式,讓woker進程繼承這個端口杜漠,然后放到epoll里面去進行監(jiān)聽极景,現(xiàn)在我們重點來看一下在這個場景下(epoll+accept)內(nèi)核的行為是怎樣的

與直接accept不同察净,epoll需要先調(diào)用epoll_create在內(nèi)核中創(chuàng)建一個epoll文件,如下圖

epoll會創(chuàng)建一個匿名的inode節(jié)點盼樟,這個節(jié)點指向的是一個epoll主結(jié)構(gòu)氢卡,這個結(jié)構(gòu)中有兩個核心字段,一個是紅黑樹晨缴,用戶態(tài)需要監(jiān)聽的文件都會掛在這個紅黑樹下面译秦,實現(xiàn)lgn的查找、插入喜庞、更新的復(fù)雜度诀浪;另外一個是rdlist,即文件事件的就緒隊列延都,指向的是一個鏈表雷猪,事件產(chǎn)生的時候,epoll會把對應(yīng)的epitem(即紅黑樹上的節(jié)點)插入到這個鏈表中晰房,當(dāng)向用戶態(tài)返回的時候求摇,只需要遍歷這個就緒鏈表即可,而不需要像select那樣遍歷所有文件殊者,不過本文的重點是分析epoll的阻塞及喚醒過程与境,epoll本身的主結(jié)構(gòu)簡單帶過,實際上這個結(jié)構(gòu)是比較復(fù)雜猖吴。

接下來看如何把要監(jiān)聽的socket fd掛在epoll上摔刁,這個過程調(diào)用的是epoll_ctl,將fd向內(nèi)核傳遞海蔽,內(nèi)核實際上會做兩個事情

將fd掛在紅黑樹中

調(diào)用文件設(shè)備驅(qū)動的poll回調(diào)指針(這是重點)

epoll/select等這些模型要實現(xiàn)多路復(fù)用共屈,實際上主要就是依賴于:將進程掛在對應(yīng)的fd的等待隊列上,這樣當(dāng)這個fd的事情產(chǎn)生的時候党窜,設(shè)備驅(qū)動就會將這個隊列上的進程喚醒拗引,如果進程不依賴epoll,毫無疑問他無法將自己同時掛在多個fd的隊列上幌衣,epoll幫他干了這個事情矾削,而干這個事情的一個核心步驟,是調(diào)用對應(yīng)fd驅(qū)動設(shè)備提供的poll方法

linux中豁护,對設(shè)備模型進行了一個規(guī)范的標(biāo)準(zhǔn)化哼凯,比如設(shè)備分為字符設(shè)備、塊設(shè)備楚里、網(wǎng)絡(luò)設(shè)備等断部,對于開發(fā)者而言,要給一個設(shè)備實現(xiàn)一個驅(qū)動程序就必須按照linux提供的規(guī)范來實現(xiàn)腻豌,其中對于跟用戶層交互這塊家坎,內(nèi)核要求開發(fā)者實現(xiàn)一個叫file_operations的結(jié)構(gòu)嘱能,這個結(jié)構(gòu)定義了一系列操作的回調(diào)指針,比如read虱疏、write等用戶熟知的操作惹骂,當(dāng)用戶調(diào)用read、write等方法時做瞪,最終內(nèi)核會回調(diào)到這個設(shè)備的file_operations.read对粪、file_operations.write方法,這個方法的具體邏輯需由驅(qū)動開發(fā)者實現(xiàn)装蓬,比如本文的accept調(diào)用著拭,實際上最終是調(diào)用了socket下面的file_operations.accept方法

綜上所述,如果一個設(shè)備要支持epoll/select的調(diào)用牍帚,他必須實現(xiàn)file_operations.poll方法儡遮,epoll在處理用戶層傳入的fd時,實際上最終是調(diào)用了這個方法暗赶,而這個方法linux同樣做了一系列規(guī)范鄙币,他要求開發(fā)者實現(xiàn)以下邏輯:

要求poll方法返回用戶感興趣的事情的標(biāo)志,比如當(dāng)前fd是否可讀蹂随、是否可寫等

如果poll傳入一個poll專用的等待隊列結(jié)構(gòu)體十嘿,那他將會調(diào)用這個結(jié)構(gòu)體,這個結(jié)構(gòu)體中會有一個叫poll_table的東西岳锁,里面有一個回調(diào)函數(shù)绩衷,poll方法最終會調(diào)用這個回調(diào),這個回調(diào)是由epoll來設(shè)定的激率,epoll在這個方法中實現(xiàn)的邏輯是:將當(dāng)前進程掛在這個fd的等待隊列上面

簡單來說咳燕,如果是進程自己調(diào)用accept,則協(xié)議棧驅(qū)動會親自把這個進程掛在等待隊列上柱搜,如果是epoll來調(diào)用迟郎,則會回調(diào)poll方法剥险,最終epoll親自將進程掛在這個等待隊列上面聪蘸,記住這個結(jié)論,這是引發(fā)accept驚群效應(yīng)的最根本原因 我們來看一下epoll是如何跟file_opreations->poll方法進行交互的表制,我畫了一個簡單的時序圖

如上圖健爬,當(dāng)用戶調(diào)用epoll_ctl的添加事件的時候,在第6步中么介,epoll會把當(dāng)前進程掛在fd的等待隊列下娜遵,但是默認(rèn)情況下這種掛載不會設(shè)置互斥標(biāo)志,意思著當(dāng)設(shè)備有事情產(chǎn)生進行等待隊列喚醒的時候壤短,如果當(dāng)前隊列有多個進程在等待设拟,則會全部喚醒

可想而知慨仿,在下面的epoll_wait調(diào)用中,如果多個進程將同一個fd添加到epoll中進行監(jiān)聽纳胧,當(dāng)事件到達(dá)的時候镰吆,這些進程將被一起喚醒

但是喚醒并不一定會向用戶態(tài)返回,因為喚醒之后epoll還要遍歷一次就緒列表跑慕,確認(rèn)有至少一個事件發(fā)生才會向用戶態(tài)返回

到此万皿,我們可以想象出epoll是如何造成accept驚群的:

當(dāng)多個進程共享同一個監(jiān)聽端口并且都使用epoll進行多路復(fù)用的監(jiān)聽時,epoll將這些進程都掛在同一個等待隊列下

當(dāng)事件產(chǎn)生時核行,socket的設(shè)備驅(qū)動都會嘗試將等待隊列的進行喚醒牢硅,但是由于掛載隊列的時候使用的是epoll的掛載方式,沒有設(shè)置互斥標(biāo)志(取代了accept自己掛載隊列的方式芝雪,如第一節(jié)所述)减余,所以這個隊列下的所有進程將全部被喚醒

喚醒之后此時這些進程還處于內(nèi)核態(tài),他們都會立刻檢查事件就緒列表惩系,確認(rèn)是否有事件發(fā)生佳励,對accept而言,accept->poll方法將會檢查在當(dāng)前的socket的tcp全連接列表中是否有可用連接蛆挫,如果是則返回可用事件標(biāo)志

當(dāng)所有進程都被喚醒赃承,但是還沒有進行去真正做accept動作的時候,所有進行的事件檢查都認(rèn)為accept事件可用悴侵,所以這些進行都向用戶態(tài)返回

用戶態(tài)檢查到有accept事件可用瞧剖,這時他們將會真正調(diào)用accept函數(shù)進行連接的獲取

此時只會有一個進行能真正獲取連接,其他進行都會返回EAGAIN錯誤可免,使用strace -p PID命令可以跟蹤到這種錯誤

并不是所有進行都會返回用戶態(tài)抓于,關(guān)鍵點在于這些被喚醒的進行在檢查事件的過程中,如果已經(jīng)有進程成功accept到連接了浇借,這時別的事情將不會檢查到這個事情捉撮,從而他們會繼續(xù)休眠,不會返回用戶態(tài)

雖然不一定會返回用戶態(tài)妇垢,但也造成了內(nèi)核上下文切換的發(fā)生巾遭,其實也是驚群效應(yīng)的表現(xiàn)

3. 內(nèi)核解決了驚群效應(yīng)了嗎

根本原因在于epoll的默認(rèn)行為是對于多進程監(jiān)聽同一文件不會設(shè)置互斥,進而將所有進程喚醒闯估,后續(xù)的內(nèi)核版本主要提供了兩種解決方案

既然默認(rèn)不會設(shè)置互斥灼舍,那就加一個互斥功能好了:-),linux4.5內(nèi)核之后給epoll添加了一個EPOLLEXCLUSIVE的標(biāo)志位涨薪,如果設(shè)置了這個標(biāo)志位骑素,那epoll將進程掛到等待隊列時將會設(shè)置一下互斥標(biāo)志位,這時實現(xiàn)跟內(nèi)核原生accept一樣的特性刚夺,只會喚醒隊列中的一個進程

第二種方法:linux 3.9內(nèi)核之后給socket提供SO_REUSEPORT標(biāo)志献丑,這種方式解決得更徹底末捣,他允許不同進程的socket綁定到同一個端口,取代以往需要子進程共享socket監(jiān)聽的方式创橄,這時候塔粒,每個進程的監(jiān)聽socket將指向open_file_tables下的不同節(jié)點,也就是說不同進程是在自己的設(shè)備等待隊列下被掛起的筐摘,不存在共享fd的問題卒茬,也就不存在被同時喚醒的可能時,而內(nèi)核則在驅(qū)動中將設(shè)置了SO_REUSEPORT并且綁定同一端口的這些socket分到同一個group中咖熟,當(dāng)有tcp連接事件到達(dá)的時候圃酵,內(nèi)核將會對源IP+源端口取hash然后指定這個group中其中一個進程來接受連接,相當(dāng)于在內(nèi)核級別中實現(xiàn)了一個負(fù)載均衡

基于以上兩種方法馍管,其實epoll生態(tài)在目前來說不存在所謂的驚群效應(yīng)了郭赐,除非:你溢用epoll,比如多進程之間共享了同一個epfd(父進程創(chuàng)建epoll由多個子進程來調(diào)用)确沸,那就不能怪epoll了捌锭,因為這時候多個進程都被掛到這個epoll下,這種情況下罗捎,已經(jīng)不是僅僅是驚群效應(yīng)的問題了观谦,比如說,A進程在epoll掛了socket1的連接事件桨菜,B進程調(diào)用了epoll_wait豁状,由于屬于同一個epfd,當(dāng)socket1產(chǎn)生事件的時候倒得,進程B也會被喚醒泻红,而更嚴(yán)重的事情在于,在B的空間下并不存在socket1這個fd霞掺,從而把問題搞得很復(fù)雜谊路。總結(jié):千萬不要在多線程/多進程之間共享epfd

4. Nginx是如何解決驚群效應(yīng)的

nginx在1.11版本以上菩彬,已經(jīng)默認(rèn)打開了SO_REUSEPORT選項缠劝,解決了這個問題,應(yīng)用層不需要做特別的事情挤巡,而在這之前剩彬,nginx解決驚群的方式是加鎖酷麦,多個進程之間共享一個文件鎖矿卑,只有在搶到這個鎖的時候,這個進程才會將要監(jiān)聽的端口放到epoll中沃饶,當(dāng)epoll_wait返回之后母廷,nginx會調(diào)用accept把連接取出來轻黑,然后釋放文件鎖,讓別的進程去監(jiān)聽琴昆。這是一種折衷的辦法氓鄙,并沒有很完美,首先進程間爭搶鎖會有性能開耗(即使是非阻塞的鎖)业舍,中間可能會有小段時間沒有進程去獲取鎖抖拦,比如A進程拿到鎖,其他進程將會過一小段時間嘗試再去獲取鎖舷暮,而這小段時間里面如果請求量很大态罪,A僅接受一小部分請求就讓出鎖,則中間過程會有一些連接事件被hang住下面,總而言之复颈,升級nginx版本吧,不要再依靠這種模式了沥割!

5. uwsgi是如何解決驚群效應(yīng)的

[https://uwsgi-docs.readthedocs.io/en/latest/articles/SerializingAccept.html](Serializing accept(), AKA Thundering Herd, AKA the Zeeg Problem - uWSGI 2.0 documentation)

以上是uwsgi官方的說明耗啦,認(rèn)為uwsgi應(yīng)用一般不追求并發(fā)量,在實際上并不需要特殊關(guān)注驚群的問題机杜,同時也提供了一個-thunder-lock的選項帜讲,實現(xiàn)了一個鎖用于進程間爭搶accept,當(dāng)然了椒拗,在新版本中也支持了SO_REUSEPORT并且默認(rèn)打開舒帮,不過在實際運行中發(fā)現(xiàn),如果不打開這個鎖的情況下陡叠,驚群效應(yīng)對uwsgi導(dǎo)致的結(jié)果是:CPU傾斜

在所有進行都在監(jiān)聽的情況下玩郊,誰先從內(nèi)核層中返回用戶態(tài)并accept,誰就能成功拿到socket枉阵,而拿到socket之后通常會繼續(xù)將這個socket添加到epoll中用來監(jiān)聽收到數(shù)據(jù)的事件译红,如果一個進程在epoll中添加的socket越多,那么他被喚醒的概率越大兴溜,喚醒之后他會檢查accept侦厚,所以他成功accept的概率也越高,久而久之拙徽,你會看到總是有少數(shù)幾個worker在處理請求刨沦,而其他worker被餓死。這種情況后續(xù)在詳解.........

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膘怕,一起剝皮案震驚了整個濱河市想诅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖来破,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篮灼,死亡現(xiàn)場離奇詭異,居然都是意外死亡徘禁,警方通過查閱死者的電腦和手機诅诱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來送朱,“玉大人娘荡,你說我怎么就攤上這事∈徽樱” “怎么了它改?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長商乎。 經(jīng)常有香客問我央拖,道長,這世上最難降的妖魔是什么鹉戚? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任鲜戒,我火速辦了婚禮,結(jié)果婚禮上抹凳,老公的妹妹穿的比我還像新娘遏餐。我一直安慰自己,他們只是感情好赢底,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布失都。 她就那樣靜靜地躺著,像睡著了一般幸冻。 火紅的嫁衣襯著肌膚如雪粹庞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天洽损,我揣著相機與錄音庞溜,去河邊找鬼。 笑死碑定,一個胖子當(dāng)著我的面吹牛流码,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播延刘,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼漫试,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了碘赖?” 一聲冷哼從身側(cè)響起驾荣,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤外构,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后秘车,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體典勇,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡劫哼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年叮趴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片权烧。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡眯亦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出般码,到底是詐尸還是另有隱情妻率,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布板祝,位于F島的核電站宫静,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏券时。R本人自食惡果不足惜孤里,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望橘洞。 院中可真熱鬧捌袜,春花似錦、人聲如沸炸枣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽适肠。三九已至霍衫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侯养,已是汗流浹背慕淡。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沸毁,地道東北人峰髓。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像息尺,于是被迫代替她去往敵國和親携兵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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