服務器镰官,并發(fā),“事件驅(qū)動”的本質(zhì)

主呵吗货,是時候了泳唠。 -- 《秋日》

什么是服務器?

不就是提供“付費”宙搬、“免費”服務的高檔電腦嘛笨腥!

你提到服務?

存儲一個圖片勇垛,讀取一篇文字脖母,觀看一個動作片,計算一個賬戶存款闲孤,...

什么是并發(fā)谆级?

不如講一講什么是不并發(fā)。

我有一臺服務器讼积,1核CPU肥照,連接到互聯(lián)網(wǎng)提供服務。在09:00時刻勤众,突然有100個用戶同時要看服務器的數(shù)據(jù)舆绎,服務器怎么辦?

              +-------+      09:00
              |       | 
              | 服務器 | 
              |       |
              +-------+
                  |
                  |
      ----------------------------

                 互聯(lián)網(wǎng)

      ----------------------------
        |    |    |   ......   |
      客戶1 客戶2 客戶3        客戶100

服務器:

--> 讀取客戶1的請求们颜,  驗證客戶身份吕朵, 把數(shù)據(jù)發(fā)送給你猎醇, 用時1秒 [ 客戶2到100等待中 ]
--> 讀取客戶2的請求,  驗證客戶身份努溃, 把數(shù)據(jù)發(fā)送給你硫嘶, 用時1秒 [ 客戶3到100等待中 ]
--> 讀取客戶3的請求,  驗證客戶身份梧税, 把數(shù)據(jù)發(fā)送給你音半, 用時1秒 [ 客戶4到100等待中 ]
........................................................................  
--> 讀取客戶100的請求,驗證客戶身份贡蓖, 把數(shù)據(jù)發(fā)送給你, 用時1秒 []

這就是“不并發(fā)”煌茬,即“迭代”斥铺,也就是“循環(huán)”的意思。

迭代 == 循環(huán)

既然來了100個客戶坛善,那么一個一個的處理晾蜘,循環(huán)從客戶1一直到客戶100。處理完成客戶1才去處理客戶2眠屎,...剔交。這樣我們可以看出:

  • 客戶1從發(fā)出請求到收到響應,等待了1秒
  • 客戶2從發(fā)出請求到收到響應改衩,等待了2秒
  • 客戶3從發(fā)出請求到收到響應岖常,等待了3秒
  • ..................................
  • 客戶100從發(fā)出請求到收到響應,等待了100秒

這就是“不并發(fā)”的問題葫督,同時來100個客戶竭鞍,這些用戶會排起長長的隊伍,等待很長的時候橄镜,服務器才會去為他服務偎快。客戶可不喜歡這樣的地方洽胶。

把服務器比喻成一個KFC晒夹,那么“不并發(fā)”就意味著只提供一個服務員,來了100個客戶姊氓,當然要排個長長的隊伍了丐怯。

              +-------+      09:00
              |       | 
              |  KFC  | 
              |       |
              +-- S --+
                客戶1
                客戶2
                客戶3
                ......
                客戶100

那么,如何并發(fā)他膳?

這個問題太廣泛了响逢,需要先從 "CPU" "操作系統(tǒng)" "進程" 開始。

CPU 操作系統(tǒng) 進程

操作系統(tǒng)運行時棕孙,采用“搶占”的方式舔亭。當今絕大多數(shù)操作系統(tǒng)采用資源“搶占”些膨。資源就是CPU計算,內(nèi)存使用钦铺,磁盤讀寫订雾,...基于此設計,在單核CPU的環(huán)境里矛洞,可以同時運行多個進程洼哎。

操作系統(tǒng)會劃分時間片,并使用一個任務隊列沼本,把每個進程每個階段的任務分配一個時間片噩峦,比如1ms(實際小的多)。1ms運行進程的任務抽兆,沒有完成就掛起放入隊列末端识补,下一次再運行。然后操作系統(tǒng)運行任務隊列的下一個任務辫红。

比如有個進程凭涂,他的任務是打開一個文件,然后讀取100個字符贴妻,并把文件寫入10個字符切油。

操作系統(tǒng)會運行進程,先打開文件名惩,如果這時候時間片時間到了澎胡,掛起進程,放入隊列后面娩鹉,運行下一個進程滤馍。

當操作系統(tǒng)根據(jù)任務隊列的前進,又一次到達這個進程底循,操作系統(tǒng)讀取100個字符巢株,時間到,掛起進程熙涤,放入隊列后面阁苞,運行下一個進程。

... 如此重復 ...

時間片祠挫?

事實上那槽,操作系統(tǒng)被聰明的設計,即便是單核CPU等舔,也可以同時運行多個進程骚灸。操作系統(tǒng)經(jīng)常同時運行多個進程,比如 Photoshop, Firefox, Vim, ... 他們是同時運行的慌植,而且能“同時”工作甚牲。

對于進程义郑,操作系統(tǒng)不會把 CPU 和內(nèi)存一直放在某個進程中。如果這樣丈钙,當有個進程耗費時間特別長時非驮,其他的進程就罷工了,也就無法同時運行多進程了雏赦。

所以劫笙,操作系統(tǒng)會給每個進程一個時間片,即運行進程中任務的時間上限星岗,到達時間后會掛起進程填大,放入任務隊列后面,直到下一次任務隊列取出這個進程任務俏橘。

任務隊列栋盹?

操作系統(tǒng)使用一個線性的隊列,管理ta自己的工作流程敷矫。操作系統(tǒng)不停地取出任務,運行汉额,取出任務曹仗,運行,...

從編程的角度觀看蠕搜,就好比是一個數(shù)組怎茫。

IO的根本:內(nèi)核緩沖區(qū)

對磁盤寫需要花費大量時間,而對內(nèi)存寫則小的多妓灌。
一個高效的做法是:在內(nèi)核區(qū)域開辟一塊內(nèi)存轨蛤,用來放置讀取和寫入的內(nèi)容。

比如

程序員在9:00寫入100個字符虫埂,這些字符被復制到內(nèi)核緩沖區(qū)中祥山,這是屬于內(nèi)核的一塊內(nèi)存。

在9:10寫入20個字符掉伏,這些字符也被復制到內(nèi)核緩沖區(qū)中缝呕。

在某個時間,比如9:30斧散,操作系統(tǒng)把內(nèi)核緩沖區(qū)中寫入的所有內(nèi)容排序供常,然后一次性寫入到磁盤。

從9:00到9:30之間可能寫入了幾百次內(nèi)核緩沖區(qū)鸡捐,但都是在內(nèi)存區(qū)域栈暇,速度會很快,而在9:30只進行了一次磁盤寫入箍镜。這樣把數(shù)百次的寫入操作集合成一次磁盤寫入源祈。從而減少磁盤寫入次數(shù)煎源。

----------+----------------+----------------+----------+---------------> 時間線

     [100個字符]        [20個字符]           ...

     9:00 |寫入         9:10 |寫入       9:xx|寫入
          v                 v              v 
    +-----------------------------------------------------------------+
    |                             內(nèi)核緩沖區(qū)                            |  
    +-----------------------------------------------------------------+       

                                                  9:30 | 寫入
                                                       v
    +-----------------------------------------------------------------+
    |                               磁盤                               |
    +-----------------------------------------------------------------+

實際的計算機其運行速度非常高,9:00~9:30只不過是我們的人為假設新博,計算機這段時間間隔大概只有1分鐘薪夕,或者更低,而在這1分鐘內(nèi)赫悄,可能已經(jīng)運行了成百上千次不同的寫入原献。

緩沖?

緩沖是一塊內(nèi)存埂淮,里面放著亂糟糟的東西姑隅。內(nèi)存由小格子組成,每個小格子代表一位倔撞,可以放置一個0或者1讲仰。8個小格子稱為1個字節(jié),1024個小格子稱為1千個字節(jié)痪蝇,二進制都是用2的倍數(shù)表示鄙陡,所以2進制的1千是1024。

計算機啟動后躏啰,內(nèi)存中的每個小格子都是有值的趁矾,我沒有深入研究過初始是什么值。但是我們可以假定是0.

現(xiàn)在给僵,需要一塊緩沖毫捣,那就是從內(nèi)存中拿出一塊沒有使用的區(qū)域,里邊是很多小格子帝际,每個小格子放置了0或者1蔓同。小格子中肯定有0或者1,不可能是空白的蹲诀。

既然小格子都是0 1斑粱,那么這塊內(nèi)存中就有一些不確定的數(shù)值。這些數(shù)值在開始是無用的數(shù)據(jù)脯爪。這塊內(nèi)存可能剛才被某個進程使用過珊佣,存儲了一些用戶的賬號密碼,然后你的這塊內(nèi)存還放著這些數(shù)據(jù)披粟。但是這些數(shù)據(jù)對你現(xiàn)在當前的進程是沒用的咒锻。

使用賦值可以覆蓋掉原有的格子中的值。小格子被新的0 1填充守屉,獲得一個新的數(shù)據(jù)惑艇。

看到這里你也應該明白了,如果我們申請了長度是64個小格子的內(nèi)存,也就是可以放置64個0 1的內(nèi)存滨巴,64個小格子是8個字節(jié)思灌,可以放置8個ASCII字符,4個JavaScript字符(16位)恭取。

如果我們在小格子里只填充了32個泰偿,那么剩下的32個是一些混亂的數(shù)據(jù),我們不需要蜈垮,所以我們需要精確定位要使用的小格子數(shù)耗跛。

也就是4個字節(jié)。我們填充4個字節(jié)的數(shù)據(jù)攒发,然后操作這塊緩沖的時候调塌,也只操作4個字節(jié)的(讀出到另一塊內(nèi)存,或者寫入其他文件)惠猿。剩下的4個字節(jié)就不要去動羔砾,那些是混亂的數(shù)據(jù)。

Buffer 對象就是Node.js對緩沖的一個對象表示偶妖,通過提供的函數(shù) API 我們可以操作緩沖姜凄。包括申請一塊內(nèi)存做緩沖,填充這塊緩沖趾访,操作這塊緩沖的數(shù)據(jù)(里邊的小格子)态秧。

如何并發(fā)?

  1. 多進程 多線程

    對于大量占用CPU的程序腹缩,如果要給1千個人同時提供CPU使用,最理想的狀態(tài)就是提供1000個CPU空扎,每個CPU占用一個線程藏鹊。

    不過現(xiàn)實還沒有這么多核的服務器谜疤。如果我們沒錢怔接,那么我們只有4個核,充其量我們可以提供4個CPU服務侠畔。也就是同時并行4個線程撮慨。同時可以為4個客戶提供CPU計算服務竿痰。

    然而,大部分客戶在使用服務的時間中砌溺,需要CPU計算的時間比例較少影涉。

    當你需要CPU密集的服務時,C語言是最好的選擇规伐,過去貌似更多的選擇C++蟹倾,但是現(xiàn)在的流行表示,以C++為代表的面向?qū)ο笾粫殉绦蚋愕糜纺[難以擴展。許多人在對C C++的反思后鲜棠,仍然認為C才是最有價值的肌厨。比如版本管理系統(tǒng)Git,簡潔有序豁陆。比如Redis柑爸,快速簡潔。

    C能做的是盒音,用最簡單的代碼表達內(nèi)容表鳍,獲得最快速的CPU和內(nèi)存操作。

    多進程里逆,為每一個客戶啟動一個進程提供服務进胯。
    多線程,在一個進程內(nèi)為每一個客戶提供一個線程服務原押。

    多進程和多線程的過程是相似的胁镐,但是多線程的內(nèi)存開銷要比進程少,并且切換速度快一些诸衔,但是多線程編程會變復雜盯漂,并且會出現(xiàn)多個線程同時操作同一個數(shù)據(jù),從而引入鎖的問題笨农。

    當CPU密集時就缆,顯然需要多線程更好一些,每一個客戶連接對應一個線程谒亦。因為在這樣的系統(tǒng)里竭宰,每一個任務都沒有等待的機會,所有的內(nèi)容都一直在不停地運算份招,直到結(jié)束切揭。

    數(shù)據(jù)庫就是很好的代表。

    ---> 連接進入 ---> 領(lǐng)取一個線程 ---> 計算 ---> 返回 ---> 收回線程
    ---> 連接進入 ---> 領(lǐng)取一個線程 ---> 計算 ---> 返回 ---> 收回線程
    ---> 連接進入 ---> 領(lǐng)取一個線程 ---> 計算 ---> 返回 ---> 收回線程
    ...........................................................
    

    理想情況下锁摔,進入 1萬 個用戶廓旬,我們希望有 1萬 個線程在同時處理任務。顯然谐腰,事實上硬件還達不到孕豹。這就需要一些操作系統(tǒng)排隊。而一旦進入排隊十气,后續(xù)進入的客戶就會進入等待励背,他們會明顯的感受到延遲的存在。

    比如進入 1萬 個用戶砸西,很有可能 10 個在運行計算椅野,另外的在操作系統(tǒng)中排隊。

    這樣的服務需要多核速度很快的 CPU,并且服務吞吐量并不特別高竟闪。

  2. IO 多路復用

    與操作系統(tǒng)達成協(xié)議离福,同時監(jiān)測多個文件描述符(讀寫源頭),操作系統(tǒng)提交程序控制權(quán)的時候炼蛤,可以一次提交多個變動的描述符妖爷。從而可以一次控制多個源頭讀寫。

    最典型的就是Unix提供的 select, poll理朋。

    使用 select 模型的時候絮识,工作過程是這樣的:

    1. 首先打開多個文件描述符
    2. 交給操作系統(tǒng)處理數(shù)據(jù)讀寫
    3. 操作系統(tǒng)發(fā)現(xiàn)數(shù)據(jù)變動后,對這個標識符打上標簽嗽上,停止阻塞次舌,喚醒主程序
    4. 主程序遍歷文件描述符,發(fā)現(xiàn)有變化的兽愤,就對其運行一個小任務
    5. 全部運行完任務彼念,再次提供給操作系統(tǒng)
  3. 優(yōu)化的 IO 多路復用 epoll kqueue

    當操作系統(tǒng)發(fā)出通知后,我們使用一個小緩沖內(nèi)存浅萧,讀取數(shù)據(jù)中的一塊逐沙,并運行任務,然后交回操作系統(tǒng)洼畅。因為處理的數(shù)據(jù)量很小吩案,所以感覺上去像是沒有阻塞。

    交給操作系統(tǒng)后帝簇,操作系統(tǒng)就可以再次加入新變動的描述符用于下一次的任務徘郭。

    文件描述符:是數(shù)據(jù)可以讀寫的源頭表示,比如一個文件描述符丧肴,就是代表可以讀寫的文件残揉。一個套接字描述符,也是可以讀寫的闪湾,網(wǎng)絡進入出去的數(shù)據(jù)使用“套接字描述符”這個術(shù)語來表示冲甘。

    config fds[1, 2, 3, ...]         // 配置文件描述符绩卤,他們關(guān)聯(lián)了數(shù)據(jù)讀寫的源頭
    
    loop {                            // 循環(huán)運行
        change_fds = epoll wait fds   // 交給操作系統(tǒng)途样,并等待(睡眠)
        forEach change_fds {          // 當操作系統(tǒng)通知時,會把變動的描述符放入change_fds
            if fd === socket in
                read socket
            if fd === file a
                write file a
            if fd === socket out
                write socket
            ...
        }
    }
    
    1. 首先打開多個文件描述符濒憋,

    2. 交給操作系統(tǒng)處理數(shù)據(jù)讀寫

    3. 操作系統(tǒng)發(fā)現(xiàn)數(shù)據(jù)變動后何暇,把變動的標識符放入一個變動描述符隊列,停止阻塞凛驮,喚醒主程序

    4. 主程序遍歷變動的文件描述符裆站,對其運行一個小任務

    5. 全部運行完任務,再次提供給操作系統(tǒng)

我看不明白!

Apache 在以往的服務中提供多線程服務器模型宏胯,Nginx 提供IO多路復用的模型羽嫡。

流行的數(shù)據(jù)庫,像 Mysql肩袍,采用多線程模型杭棵,因為 ta 面對的是密集的數(shù)據(jù)操作。而應用服務器面對的是套接字的讀取寫入氛赐,等待魂爪,很多時候,客戶都是沒有數(shù)據(jù)可以收發(fā)的艰管。

每個平臺實現(xiàn)了不同的 IO 多路復用滓侍,Linux 采用 epoll,BSD 采用 kqueue牲芋,還有的沒有采用撩笆,停留在多線程。libev是libevent的新版本街图,采用了統(tǒng)一封裝浇衬,針對不同平臺使用不同的IO吞吐。而在上層的編碼中餐济,采用統(tǒng)一的函數(shù)庫耘擂。

Node.js,底層是 C 編寫的 libev 框架絮姆,libev 在 Linux醉冤,BSD Unix上分別是用 epoll kqueue 多路復用模型,這在編程的抽象層常被叫做事件驅(qū)動篙悯。事實上蚁阳,ta 是多路復用,同時監(jiān)測多個文件描述符鸽照,采用非阻塞讀寫螺捐,從而在單線程進行并發(fā)。

非阻塞矮燎?IO...

所謂阻塞定血,是進程會進入睡眠,從而不再提供服務诞外,直到讀寫的數(shù)據(jù)已經(jīng)被放到內(nèi)核緩沖區(qū)澜沟,操作系統(tǒng)內(nèi)核會再次喚醒進程。

普通文件峡谊,也就是操作系統(tǒng)磁盤的文件茫虽,一般沒有讀和寫的阻塞刊苍,一旦通過open打開后,會立刻有內(nèi)存的映射濒析。你可以讀這個文件到內(nèi)存正什,然后再寫入別的地方。這些操作不需要等待數(shù)據(jù)準備号杏,操作是直接運行的埠忘,時間花費在磁盤尋址和內(nèi)存復制,沒有數(shù)據(jù)準備的等待馒索。

網(wǎng)絡套接字數(shù)據(jù)被認為要到來時莹妒,會有一些等待期,被認為是阻塞绰上。比如網(wǎng)絡的數(shù)據(jù)要一條一條傳過來旨怠,期間要經(jīng)過漫長的光纖。

套接字是怎么利用多路復用無阻塞讀寫的蜈块?

首先套接字是個讀寫雙工模式的鉴腻。套接字的數(shù)據(jù)來源于網(wǎng)絡,并從網(wǎng)絡發(fā)布出去百揭。因為此爽哎,每一階段從網(wǎng)絡來的數(shù)據(jù)量非常小∑饕唬可以比作一個水龍頭课锌,雖然水(數(shù)據(jù))確實一直不停地從水龍頭中涌出,但是每一點的水量都是非常小的祈秕,CPU 內(nèi)存處理這點流量幾乎不費吹灰之力渺贤。

所以,程序可以不停地檢測到數(shù)據(jù)流入请毛,并且擠滿內(nèi)核緩沖區(qū)志鞍,然后wait完畢,操作系統(tǒng)內(nèi)核通知進程(這個時間非常的短方仿,CPU是很快的)固棚,進程讀走內(nèi)核緩沖區(qū)的內(nèi)容,并返還給操作系統(tǒng)控制權(quán)仙蚜。操作系統(tǒng)再次把內(nèi)核緩沖區(qū)寫滿此洲,然后通知進程,...鳍征,如此黍翎,周而復始面徽,直到?jīng)]有數(shù)據(jù)變動了艳丛,操作系統(tǒng)就一直wait匣掸,進程則一直睡眠,直到有新的數(shù)據(jù)變化氮双。

每個循環(huán)階段碰酝,每個讀寫占用的時間和讀寫的數(shù)據(jù)量都是小塊的,幾乎可以看做瞬時戴差。完成后立刻交還給操作系統(tǒng)控制權(quán)送爸,等待操作系統(tǒng)內(nèi)核下一次的通知。

一個大文件如何在寫入讀取時不造成其他客戶等待暖释?

一個大型文件袭厂,可以使用一個游標記錄每次讀寫的位置,每次只讀寫一小塊球匕,然后記錄游標纹磺,停止讀寫,并返回到循環(huán)亮曹,進入等待橄杨。當操作系統(tǒng)下一次發(fā)出通知時,讀寫游標后面的一小塊照卦,并如此重復式矫,直到完全讀寫。這樣可以在最小的時間返還操作系統(tǒng)的控制權(quán)役耕,以此達到無阻塞采转。

我如何搭建我的超級服務器?

你需要運行一個CPU極度密集的業(yè)務瞬痘,并把ta投入到互聯(lián)網(wǎng)上提供服務氏义?

  1. 你需要一個服務器用來運行你的 CPU 密集型的業(yè)務,這臺服務器是用 C 編寫的图云,提供了高性能 CPU 和大量的內(nèi)存用來提供可靠的快速的服務惯悠。

    這個服務提供 TCP UDP 級別的服務,也就是說通過套接字與其他進程通信竣况。

    另外克婶,這個服務應該使用多線程,對每一個進入的請求提供一個線程丹泉,并使用線程池提升反應質(zhì)量情萤。并要有一個良好的請求隊列控制程序,以免請求過度摹恨,導致服務器崩潰筋岛。

    這些服務可以是自己編寫的,也可以是第三方提供的晒哄,比如 Mysql, Orcale, ... 也可以是復雜的散點計算睁宰,或者大數(shù)據(jù)分析肪获,... 他們通過套接字與下面的IO服務通信。

  2. 你需要一個服務器用來運行你的 IO 密集型的業(yè)務柒傻,一旦你的服務是面向網(wǎng)絡孝赫,那么就意味著你需要驗證成千上萬的客戶,這些業(yè)務內(nèi)容不需要大量的 CPU 計算红符,就算是循環(huán)也可能只在100次量級之內(nèi)青柄。這時候應該使用編寫更快速,更容易管理的語言预侯,比如 Node.js致开,Python,Ruby萎馅。

    這樣的語言編寫出來的程序喇喉,更容易擴展,和與他人合作校坑。因為是IO服務拣技,所以不存在計算的性能問題,存在的差別則是IO吞吐上耍目。

    基于 IO 服務器的演化膏斤,大致經(jīng)歷了多進程 -> 多線程 -> select poll多路復用 -> epoll kqueue 多路復用。現(xiàn)在最快速的 IO 服務器是采用 epoll kqueue 多路復用邪驮,比如 Nginx莫辨。Node.js 本身就是 epoll 的,其核心是基于 epoll 的 libev 事件驅(qū)動庫毅访。

    如果你打算使用 Python沮榜,Ruby,選用他們的 epoll kqueue 事件驅(qū)動庫喻粹,要比多線程庫快速并穩(wěn)定的多蟆融。

    說明:epoll 只能在Linux服務器使用,在 BSD Unix 則是采用了kqueue守呜,與 epoll 達到相似的目的型酥。libev 在底層對多個平臺進行了統(tǒng)一封裝。

    另外查乒,基于事件驅(qū)動的服務弥喉,也更容易水平擴展,搭建集群可以將服務器拓展至幾十個玛迄。

    福利:甚至對于拓展的管理程序由境,Nginx 就可以提供現(xiàn)成的服務。

  3. 把你的 IO 密集服務器和你的 CPU 密集服務器蓖议,通過內(nèi)部局域網(wǎng)進行連接通信(套接字通信)虏杰,這樣你就提供了一個 CPU 極度密集的互聯(lián)網(wǎng)服務讥蟆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘹屯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌从撼,老刑警劉巖州弟,帶你破解...
    沈念sama閱讀 212,185評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異低零,居然都是意外死亡婆翔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評論 3 385
  • 文/潘曉璐 我一進店門掏婶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啃奴,“玉大人,你說我怎么就攤上這事雄妥∽罾伲” “怎么了?”我有些...
    開封第一講書人閱讀 157,684評論 0 348
  • 文/不壞的土叔 我叫張陵老厌,是天一觀的道長瘟则。 經(jīng)常有香客問我,道長枝秤,這世上最難降的妖魔是什么醋拧? 我笑而不...
    開封第一講書人閱讀 56,564評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮淀弹,結(jié)果婚禮上丹壕,老公的妹妹穿的比我還像新娘。我一直安慰自己薇溃,他們只是感情好菌赖,可當我...
    茶點故事閱讀 65,681評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沐序,像睡著了一般盏袄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薄啥,一...
    開封第一講書人閱讀 49,874評論 1 290
  • 那天辕羽,我揣著相機與錄音,去河邊找鬼垄惧。 笑死刁愿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的到逊。 我是一名探鬼主播铣口,決...
    沈念sama閱讀 39,025評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼滤钱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脑题?” 一聲冷哼從身側(cè)響起件缸,我...
    開封第一講書人閱讀 37,761評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叔遂,沒想到半個月后他炊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,217評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡已艰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,545評論 2 327
  • 正文 我和宋清朗相戀三年痊末,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哩掺。...
    茶點故事閱讀 38,694評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡凿叠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嚼吞,到底是詐尸還是另有隱情盒件,我是刑警寧澤,帶...
    沈念sama閱讀 34,351評論 4 332
  • 正文 年R本政府宣布舱禽,位于F島的核電站履恩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呢蔫。R本人自食惡果不足惜切心,卻給世界環(huán)境...
    茶點故事閱讀 39,988評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望片吊。 院中可真熱鬧绽昏,春花似錦、人聲如沸俏脊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,778評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爷贫。三九已至认然,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漫萄,已是汗流浹背卷员。 一陣腳步聲響...
    開封第一講書人閱讀 32,007評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腾务,地道東北人毕骡。 一個月前我還...
    沈念sama閱讀 46,427評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親未巫。 傳聞我的和親對象是個殘疾皇子窿撬,可洞房花燭夜當晚...
    茶點故事閱讀 43,580評論 2 349

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

  • 從三月份找實習到現(xiàn)在,面了一些公司叙凡,掛了不少劈伴,但最終還是拿到小米、百度握爷、阿里跛璧、京東、新浪饼拍、CVTE赡模、樂視家的研發(fā)崗...
    時芥藍閱讀 42,214評論 11 349
  • 在服務器端程序開發(fā)領(lǐng)域田炭,性能問題一直是備受關(guān)注的重點师抄。業(yè)界有大量的框架、組件教硫、類庫都是以性能為賣點而廣為人知叨吮。然而...
    零一間閱讀 866評論 0 12
  • 在服務器端程序開發(fā)領(lǐng)域茶鉴,性能問題一直是備受關(guān)注的重點。業(yè)界有大量的框架景用、組件涵叮、類庫都是以性能為賣點而廣為人知。然而...
    dreamer_lk閱讀 1,001評論 0 17
  • word直接復制來了伞插,格式就不改了割粮。至于這門課怎么復習,只要平時實驗都認真完成媚污、報告認真寫舀瓢,平時分都很高;考試的話...
    Jozhn閱讀 4,531評論 0 8
  • 今天打羽毛球去了 運動后真的感覺很棒
    李小花兒_閱讀 139評論 0 0