今日頭條Go建千億級微服務(wù)的實踐

今日頭條當(dāng)前后端服務(wù)超過80%的流量是跑在 Go 構(gòu)建的服務(wù)上末早。微服務(wù)數(shù)量超過100個啊片,高峰 QPS 超過700萬赎婚,日處理請求量超過3000億刘绣,是業(yè)內(nèi)最大規(guī)模的 Go 應(yīng)用。

Go 構(gòu)建微服務(wù)的歷程

在2015年之前挣输,頭條的主要編程語言是 Python 以及部分 C++纬凤。隨著業(yè)務(wù)和流量的快速增長,服務(wù)端的壓力越來越大撩嚼,隨之而來問題頻出停士。Python 的解釋性語言特性以及其落后的多進(jìn)程服務(wù)模型受到了巨大的挑戰(zhàn)挖帘。此外,當(dāng)時的服務(wù)端架構(gòu)是一個典型的單體架構(gòu)恋技,耦合嚴(yán)重拇舀,部分獨立功能也急需從單體架構(gòu)中拆出來。

為什么選擇 Go 語言猖任?

Go 語言相對其它語言具有幾點天然的優(yōu)勢:

語法簡單你稚,上手快

性能高,編譯快朱躺,開發(fā)效率也不低

原生支持并發(fā)刁赖,協(xié)程模型是非常優(yōu)秀的服務(wù)端模型,同時也適合網(wǎng)絡(luò)調(diào)用

部署方便长搀,編譯包小宇弛,幾乎無依賴

當(dāng)時 Go 的1.4版本已經(jīng)發(fā)布,我曾在 Go 處于1.1版本的時候源请,開始使用 Go 語言開發(fā)后端組件枪芒,并且使用 Go 構(gòu)建過超大流量的后端服務(wù),因此對 Go 語言本身的穩(wěn)定性比較有信心谁尸。再加上頭條后端整體服務(wù)化的架構(gòu)改造舅踪,所以決定使用 Go 語言構(gòu)建今日頭條后端的微服務(wù)架構(gòu)。

2015年6月良蛮,今日頭條開始使用 Go 語言重構(gòu)后端的 Feed 流服務(wù)抽碌,期間一邊重構(gòu),一邊迭代現(xiàn)有業(yè)務(wù)决瞳,同時還進(jìn)行服務(wù)拆分货徙,直到2016年6月,F(xiàn)eed 流后端服務(wù)幾乎全部遷移到 Go皮胡。由于期間業(yè)務(wù)增長較快痴颊,夾雜服務(wù)拆分,因此沒有橫向?qū)Ρ戎貥?gòu)前后的各項指標(biāo)屡贺。但實際上切換到 Go 語言之后蠢棱,服務(wù)整體的穩(wěn)定性和性能都大幅提高。

微服務(wù)架構(gòu)

對于復(fù)雜的服務(wù)間調(diào)用烹笔,我們抽象出五元組的概念:(From, FromCluster, To, ToCluster, Method)裳扯。每一個五元組唯一定義了一類的RPC調(diào)用。以五元組為單元谤职,我們構(gòu)建了一整套微服務(wù)架構(gòu)饰豺。

我們使用 Go 語言研發(fā)了內(nèi)部的微服務(wù)框架 kite,協(xié)議上完全兼容 Thrift允蜈。以五元組為基礎(chǔ)單元冤吨,我們在 kite 框架上集成了服務(wù)注冊和發(fā)現(xiàn)蒿柳,分布式負(fù)載均衡,超時和熔斷管理漩蟆,服務(wù)降級垒探,Method 級別的指標(biāo)監(jiān)控,分布式調(diào)用鏈追蹤等功能怠李。目前統(tǒng)一使用 kite 框架開發(fā)內(nèi)部 Go 語言的服務(wù)圾叼,整體架構(gòu)支持無限制水平擴(kuò)展。

關(guān)于 kite 框架和微服務(wù)架構(gòu)實現(xiàn)細(xì)節(jié)后續(xù)有機(jī)會會專門分享捺癞,這里主要分享下我們在使用 Go 構(gòu)建大規(guī)模微服務(wù)架構(gòu)中夷蚊,Go 語言本身給我們帶來了哪些便利以及實踐過程中我們?nèi)〉玫慕?jīng)驗。內(nèi)容主要包括并發(fā)髓介,性能惕鼓,監(jiān)控以及對Go語言使用的一些體會。

并發(fā)

Go 作為一門新興的編程語言唐础,最大特點就在于它是原生支持并發(fā)的箱歧。和傳統(tǒng)基于 OS 線程和進(jìn)程實現(xiàn)不同,Go 語言的并發(fā)是基于用戶態(tài)的并發(fā)一膨,這種并發(fā)方式就變得非常輕量呀邢,能夠輕松運行幾萬甚至是幾十萬的并發(fā)邏輯。因此使用 Go 開發(fā)的服務(wù)端應(yīng)用采用的就是“協(xié)程模型”豹绪,每一個請求由獨立的協(xié)程處理完成驼鹅。

比進(jìn)程線程模型高出幾個數(shù)量級的并發(fā)能力,而相對基于事件回調(diào)的服務(wù)端模型森篷,Go 開發(fā)思路更加符合人的邏輯處理思維,因此即使使用 Go 開發(fā)大型的項目豺型,也很容易維護(hù)仲智。

并發(fā)模型

Go 的并發(fā)屬于 CSP 并發(fā)模型的一種實現(xiàn),CSP 并發(fā)模型的核心概念是:“不要通過共享內(nèi)存來通信姻氨,而應(yīng)該通過通信來共享內(nèi)存”钓辆。這在 Go 語言中的實現(xiàn)就是 Goroutine 和 Channel。在1978發(fā)表的 CSP 論文中有一段使用 CSP 思路解決問題的描述肴焊。

“Problem: To print in ascending order all primes less than 10000. Use an array of processes, SIEVE, in which each process inputs a prime from its predecessor and prints it. The process then inputs an ascending stream of numbers from its predecessor and passes them on to its successor, suppressing any that are multiples of the original prime.”

要找出10000以內(nèi)所有的素數(shù)前联,這里使用的方法是篩法,即從2開始每找到一個素數(shù)就標(biāo)記所有能被該素數(shù)整除的所有數(shù)娶眷。直到?jīng)]有可標(biāo)記的數(shù)似嗤,剩下的就都是素數(shù)。下面以找出10以內(nèi)所有素數(shù)為例届宠,借用 CSP 方式解決這個問題烁落。

從上圖中可以看出乘粒,每一行過濾使用獨立的并發(fā)處理程序,上下相鄰的并發(fā)處理程序傳遞數(shù)據(jù)實現(xiàn)通信伤塌。通過4個并發(fā)處理程序得出10以內(nèi)的素數(shù)表灯萍,對應(yīng)的 Go 實現(xiàn)代碼如下:

這個例子體現(xiàn)使用 Go 語言開發(fā)的兩個特點:

Go 語言的并發(fā)很簡單,并且通過提高并發(fā)可以提高處理效率每聪。

協(xié)程之間可以通過通信的方式來共享變量旦棉。

并發(fā)控制

當(dāng)并發(fā)成為語言的原生特性之后,在實踐過程中就會頻繁地使用并發(fā)來處理邏輯問題药薯,尤其是涉及到網(wǎng)絡(luò)I/O的過程绑洛,例如 RPC 調(diào)用,數(shù)據(jù)庫訪問等果善。下圖是一個微服務(wù)處理請求的抽象描述:

當(dāng) Request 到達(dá) GW 之后诊笤,GW 需要整合下游5個服務(wù)的結(jié)果來響應(yīng)本次的請求,假定對下游5個服務(wù)的調(diào)用不存在互相的數(shù)據(jù)依賴問題巾陕。那么這里會同時發(fā)起5個 RPC 請求讨跟,然后等待5個請求的返回結(jié)果。為避免長時間的等待鄙煤,這里會引入等待超時的概念晾匠。超時事件發(fā)生后,為了避免資源泄漏梯刚,會發(fā)送事件給正在并發(fā)處理的請求凉馆。在實踐過程中,得出兩種抽象的模型亡资。

1. Wait

2. Cancel

Wait和Cancel兩種并發(fā)控制方式澜共,在使用 Go 開發(fā)服務(wù)的時候到處都有體現(xiàn),只要使用了并發(fā)就會用到這兩種模式锥腻。在上面的例子中嗦董,GW 啟動5個協(xié)程發(fā)起5個并行的 RPC 調(diào)用之后,主協(xié)程就會進(jìn)入等待狀態(tài)瘦黑,需要等待這5次 RPC 調(diào)用的返回結(jié)果京革,這就是 Wait 模式。另一中 Cancel 模式幸斥,在5次 RPC 調(diào)用返回之前匹摇,已經(jīng)到達(dá)本次請求處理的總超時時間,這時候就需要 Cancel 所有未完成的 RPC 請求甲葬,提前結(jié)束協(xié)程廊勃。Wait 模式使用會比較廣泛一些,而對于 Cancel 模式主要體現(xiàn)在超時控制和資源回收经窖。

在 Go 語言中供搀,分別有 sync.WaitGroup 和 context.Context 來實現(xiàn)這兩種模式隅居。

超時控制

合理的超時控制在構(gòu)建可靠的大規(guī)模微服務(wù)架構(gòu)顯得非常重要,不合理的超時設(shè)置或者超時設(shè)置失效將會引起整個調(diào)用鏈上的服務(wù)雪崩葛虐。

圖中被依賴的服務(wù)G由于某種原因?qū)е马憫?yīng)比較慢胎源,因此上游服務(wù)的請求都會阻塞在服務(wù)G的調(diào)用上。如果此時上游服務(wù)沒有合理的超時控制屿脐,導(dǎo)致請求阻塞在服務(wù)G上無法釋放涕蚤,那么上游服務(wù)自身也會受到影響,進(jìn)一步影響到整個調(diào)用鏈上各個服務(wù)的诵。

在 Go 語言中万栅,Server 的模型是“協(xié)程模型”,即一個協(xié)程處理一個請求西疤。如果當(dāng)前請求處理過程因為依賴服務(wù)響應(yīng)慢阻塞烦粒,那么很容易會在短時間內(nèi)堆積起大量的協(xié)程。每個協(xié)程都會因為處理邏輯的不同而占用不同大小的內(nèi)存代赁,當(dāng)協(xié)程數(shù)據(jù)激增扰她,服務(wù)進(jìn)程很快就會消耗大量的內(nèi)存。

協(xié)程暴漲和內(nèi)存使用激增會加劇 Go 調(diào)度器和運行時 GC 的負(fù)擔(dān)芭碍,進(jìn)而再次影響服務(wù)的處理能力徒役,這種惡性循環(huán)會導(dǎo)致整個服務(wù)不可用。在使用 Go 開發(fā)微服務(wù)的過程中窖壕,曾多次出現(xiàn)過類似的問題忧勿,我們稱之為協(xié)程暴漲。

有沒有好的辦法來解決這個問題呢瞻讽?通常出現(xiàn)這種問題的原因是網(wǎng)絡(luò)調(diào)用阻塞過長鸳吸。即使在我們合理設(shè)置網(wǎng)絡(luò)超時之后,偶爾還是會出現(xiàn)超時限制不住的情況速勇,對 Go 語言中如何使用超時控制進(jìn)行分析层释,首先我們來看下一次網(wǎng)絡(luò)調(diào)用的過程。

第一步快集,建立 TCP 連接,通常會設(shè)置一個連接超時時間來保證建立連接的過程不會被無限阻塞廉白。

第二步个初,把序列化后的 Request 數(shù)據(jù)寫入到 Socket 中,為了確保寫數(shù)據(jù)的過程不會一直阻塞猴蹂,Go 語言提供了 SetWriteDeadline 的方法院溺,控制數(shù)據(jù)寫入 Socket 的超時時間。根據(jù) Request 的數(shù)據(jù)量大小磅轻,可能需要多次寫 Socket 的操作珍逸,并且為了提高效率會采用邊序列化邊寫入的方式逐虚。因此在 Thrift 庫的實現(xiàn)中每次寫 Socket 之前都會重新 Reset 超時時間。

第三步谆膳,從 Socket 中讀取返回的結(jié)果叭爱,和寫入一樣, Go 語言也提供了 SetReadDeadline 接口漱病,由于讀數(shù)據(jù)也存在讀取多次的情況买雾,因此同樣會在每次讀取數(shù)據(jù)之前 Reset 超時時間。

分析上面的過程可以發(fā)現(xiàn)影響一次 RPC 耗費的總時間的長短由三部分組成:連接超時杨帽,寫超時漓穿,讀超時。而且讀和寫超時可能存在多次注盈,這就導(dǎo)致超時限制不住情況的發(fā)生晃危。為了解決這個問題,在 kite 框架中引入了并發(fā)超時控制的概念老客,并將功能集成到 kite 框架的客戶端調(diào)用庫中僚饭。

并發(fā)超時控制模型如上圖所示,在模型中引入了“Concurrent Ctrl”模塊沿量,這個模塊屬于微服務(wù)熔斷功能的一部分浪慌,用于控制客戶端能夠發(fā)起的最大并發(fā)請求數(shù)。并發(fā)超時控制整體流程是這樣的

首先朴则,客戶端發(fā)起 RPC 請求权纤,經(jīng)過“Concurrent Ctrl”模塊判斷是否允許當(dāng)前請求發(fā)起。如果被允許發(fā)起 RPC 請求乌妒,此時啟動一個協(xié)程并執(zhí)行 RPC 調(diào)用汹想,同時初始化一個超時定時器。然后在主協(xié)程中同時監(jiān)聽 RPC 完成事件信號以及定時器信號撤蚊。如果 RPC 完成事件先到達(dá)古掏,則表示本次 RPC 成功,否則侦啸,當(dāng)定時器事件發(fā)生槽唾,表明本次 RPC 調(diào)用超時。這種模型確保了無論何種情況下光涂,一次 RPC 都不會超過預(yù)定義的時間庞萍,實現(xiàn)精準(zhǔn)控制超時。

Go 語言在1.7版本的標(biāo)準(zhǔn)庫引入了“context”忘闻,這個庫幾乎成為了并發(fā)控制和超時控制的標(biāo)準(zhǔn)做法钝计,隨后1.8版本中在多個舊的標(biāo)準(zhǔn)庫中增加對“context”的支持,其中包括“database/sql”包。

性能

Go 相對于傳統(tǒng) Web 服務(wù)端編程語言已經(jīng)具備非常大的性能優(yōu)勢私恬。但是很多時候因為使用方式不對债沮,或者服務(wù)對延遲要求很高,不得不使用一些性能分析工具去追查問題以及優(yōu)化服務(wù)性能本鸣。在 Go 語言工具鏈中自帶了多種性能分析工具疫衩,供開發(fā)者分析問題。

CPU 使用分析

內(nèi)部使用分析

查看協(xié)程棧

查看 GC 日志

Trace 分析工具

下圖是各種分析方法截圖

在使用 Go 語言開發(fā)的過程中永高,我們總結(jié)了一些寫出高性能 Go 服務(wù)的方法

注重鎖的使用隧土,盡量做到鎖變量而不要鎖過程

可以使用 CAS,則使用 CAS 操作

針對熱點代碼要做針對性優(yōu)化

不要忽略 GC 的影響命爬,尤其是高性能低延遲的服務(wù)

合理的對象復(fù)用可以取得非常好的優(yōu)化效果

盡量避免反射曹傀,在高性能服務(wù)中杜絕反射的使用

有些情況下可以嘗試調(diào)優(yōu)“GOGC”參數(shù)

新版本穩(wěn)定的前提下,盡量升級新的 Go 版本饲宛,因為舊版本永遠(yuǎn)不會變得更好

下面描述一個真實的線上服務(wù)性能優(yōu)化例子皆愉。

這是一個基礎(chǔ)存儲服務(wù),提供 SetData 和 GetDataByRange 兩個方法艇抠,分別實現(xiàn)批量存儲數(shù)據(jù)和按照時間區(qū)間批量獲取數(shù)據(jù)的功能幕庐。為了提高性能,存儲的方式是以用戶 ID 和一段時間作為 key家淤,時間區(qū)間內(nèi)的所有數(shù)據(jù)作為 value 存儲到 KV 數(shù)據(jù)庫中异剥。因此,當(dāng)需要增加新的存儲數(shù)據(jù)時候就需要先從數(shù)據(jù)庫中讀取數(shù)據(jù)絮重,拼接到對應(yīng)的時間區(qū)間內(nèi)再存到數(shù)據(jù)庫中冤寿。

對于讀取數(shù)據(jù)的請求,則會根據(jù)請求的時間區(qū)間計算對應(yīng)的 key 列表青伤,然后循環(huán)從數(shù)據(jù)庫中讀取數(shù)據(jù)督怜。

這種情況下,高峰期服務(wù)的接口響應(yīng)時間比較高狠角,嚴(yán)重影響服務(wù)的整體性能号杠。通過上述性能分析方法對于高峰期服務(wù)進(jìn)行分析之后,得出如下結(jié)論:

問題點:

GC 壓力大丰歌,占用 CPU 資源高

反序列化過程占用 CPU 較高

優(yōu)化思路:

GC 壓力主要是內(nèi)存的頻繁申請和釋放姨蟋,因此決定減少內(nèi)存和對象的申請

序列化當(dāng)時使用的是 Thrift 序列化方式,通過 Benchmark立帖,我們找到相對高效的 Msgpack 序列化方式眼溶。

分析服務(wù)接口功能可以發(fā)現(xiàn),數(shù)據(jù)解壓縮厘惦,反序列化這個過程是最頻繁的,這也符合性能分析得出來的結(jié)論。仔細(xì)分析解壓縮和反序列化的過程宵蕉,發(fā)現(xiàn)對于反序列化操作而言酝静,需要一個”io.Reader”的接口,而對于解壓縮羡玛,其本身就實現(xiàn)了”io.Reader“接口别智。在 Go 語言中,“io.Reader”的接口定義如下:

這個接口定義了 Read 方法稼稿,任何實現(xiàn)該接口的對象都可以從中讀取一定數(shù)量的字節(jié)數(shù)據(jù)薄榛。因此只需要一段比較小的內(nèi)存 Buffer 就可以實現(xiàn)從解壓縮到反序列化的過程,而不需要將所有數(shù)據(jù)解壓縮之后再進(jìn)行反序列化让歼,大量節(jié)省了內(nèi)存的使用敞恋。

為了避免頻繁的 Buffer 申請和釋放,使用“sync.Pool”實現(xiàn)了一個對象池谋右,達(dá)到對象復(fù)用的目的硬猫。

此外,對于獲取歷史數(shù)據(jù)接口改执,從原先的循環(huán)讀取多個 key 的數(shù)據(jù)啸蜜,優(yōu)化為從數(shù)據(jù)庫并發(fā)讀取各個 key 的數(shù)據(jù)。經(jīng)過這些優(yōu)化之后辈挂,服務(wù)的高峰 PCT99 從100ms降低到15ms衬横。

上述是一個比較典型的 Go 語言服務(wù)優(yōu)化案例。概括為兩點:

從業(yè)務(wù)層面上提高并發(fā)

減少內(nèi)存和對象的使用

優(yōu)化的過程中使用了 pprof 工具發(fā)現(xiàn)性能瓶頸點终蒂,然后發(fā)現(xiàn)“io.Reader”接口具備的 Pipeline 的數(shù)據(jù)處理方式蜂林,進(jìn)而整體優(yōu)化了整個服務(wù)的性能。

服務(wù)監(jiān)控

Go 語言的 runtime 包提供了多個接口供開發(fā)者獲取當(dāng)前進(jìn)程運行的狀態(tài)后豫。在 kite 框架中集成了協(xié)程數(shù)量悉尾,協(xié)程狀態(tài),GC 停頓時間挫酿,GC 頻率构眯,堆棧內(nèi)存使用量等監(jiān)控。實時采集每個當(dāng)前正在運行的服務(wù)的這些指標(biāo)早龟,分別針對各項指標(biāo)設(shè)置報警閾值惫霸,例如針對協(xié)程數(shù)量和 GC 停頓時間。另一方面葱弟,我們也在嘗試做一些運行時服務(wù)的堆棧和運行狀態(tài)的快照壹店,方便追查一些無法復(fù)現(xiàn)的進(jìn)程重啟的情況。

編程思維和工程性

相對于傳統(tǒng) Web 編程語言芝加,Go 在編程思維上的確帶來了許多的改變硅卢。每一個 Go 開發(fā)服務(wù)都是一個獨立的進(jìn)程,任何一個請求處理造成 Panic,都會讓整個進(jìn)程退出将塑,因此當(dāng)啟動一個協(xié)程的時候需要考慮是否需要使用 recover 方法脉顿,避免影響其它協(xié)程。對于 Web 服務(wù)端開發(fā)点寥,往往希望將一個請求處理的整個過程能夠串起來艾疟,這就非常依賴于 Thread Local 的變量,而在 Go 語言中并沒有這個概念敢辩,因此需要在函數(shù)調(diào)用的時候傳遞 context蔽莱。

最后,使用 Go 開發(fā)的項目中戚长,并發(fā)是一種常態(tài)盗冷,因此就需要格外注意對共享資源的訪問,臨界區(qū)代碼邏輯的處理历葛,會增加更多的心智負(fù)擔(dān)正塌。這些編程思維上的差異,對于習(xí)慣了傳統(tǒng) Web 后端開發(fā)的開發(fā)者恤溶,需要一個轉(zhuǎn)變的過程乓诽。

關(guān)于工程性,也是 Go 語言不太所被提起的點咒程。實際上在 Go 官方網(wǎng)站關(guān)于為什么要開發(fā) Go 語言里面就提到鸠天,目前大多數(shù)語言當(dāng)代碼量變得巨大之后,對代碼本身的管理以及依賴分析變得異痴室觯苦難稠集,因此代碼本身成為了最麻煩的點,很多龐大的項目到最后都變得不敢去動它饥瓷。而 Go 語言不同剥纷,其本身設(shè)計語法簡單,類C的風(fēng)格呢铆,做一件事情不會有很多種方法晦鞋,甚至一些代碼風(fēng)格都被定義到 Go 編譯器的要求之內(nèi)。而且棺克,Go 語言標(biāo)準(zhǔn)庫自帶了源代碼的分析包悠垛,可以方便地將一個項目的代碼轉(zhuǎn)換成一顆 AST 樹。

下面以一張圖形象地表達(dá)下 Go 語言的工程性:

同樣是拼成一個正方形娜谊,Go 只有一種方式确买,每個單元都是一致。而 Python 拼接的方式可能可以多種多樣纱皆。

總結(jié)

今日頭條使用 Go 語言構(gòu)建了大規(guī)模的微服務(wù)架構(gòu)湾趾,本文結(jié)合 Go 語言特性著重講解了并發(fā)芭商,超時控制,性能等在構(gòu)建微服務(wù)中的實踐搀缠。事實上蓉坎,Go 語言不僅在服務(wù)性能上表現(xiàn)卓越,而且非常適合容器化部署胡嘿,我們很大一部分服務(wù)已經(jīng)運行于內(nèi)部的私有云平臺。結(jié)合微服務(wù)相關(guān)組件钳踊,我們正朝著 Cloud Native 架構(gòu)演進(jìn)衷敌。

更多技術(shù)實踐內(nèi)容可以關(guān)注今日頭條技術(shù)博客:http://techblog.toutiao.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拓瞪,隨后出現(xiàn)的幾起案子缴罗,更是在濱河造成了極大的恐慌,老刑警劉巖祭埂,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件面氓,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛆橡,警方通過查閱死者的電腦和手機(jī)舌界,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泰演,“玉大人呻拌,你說我怎么就攤上這事∧阑溃” “怎么了藐握?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垃喊。 經(jīng)常有香客問我猾普,道長,這世上最難降的妖魔是什么本谜? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任初家,我火速辦了婚禮,結(jié)果婚禮上耕突,老公的妹妹穿的比我還像新娘笤成。我一直安慰自己,他們只是感情好眷茁,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布炕泳。 她就那樣靜靜地躺著,像睡著了一般上祈。 火紅的嫁衣襯著肌膚如雪培遵。 梳的紋絲不亂的頭發(fā)上浙芙,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機(jī)與錄音籽腕,去河邊找鬼嗡呼。 笑死,一個胖子當(dāng)著我的面吹牛皇耗,可吹牛的內(nèi)容都是我干的南窗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼郎楼,長吁一口氣:“原來是場噩夢啊……” “哼万伤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呜袁,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤敌买,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阶界,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虹钮,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年膘融,在試婚紗的時候發(fā)現(xiàn)自己被綠了芙粱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡氧映,死狀恐怖宅倒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屯耸,我是刑警寧澤拐迁,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站疗绣,受9級特大地震影響线召,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜多矮,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一缓淹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧塔逃,春花似錦讯壶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至格粪,卻和暖如春躏吊,著一層夾襖步出監(jiān)牢的瞬間氛改,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工比伏, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留胜卤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓赁项,卻偏偏與公主長得像葛躏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子悠菜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356

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