Redis 源碼閱讀 ——— 網(wǎng)絡(luò)模塊

Redis 源碼閱讀 ——— 網(wǎng)絡(luò)模塊

概述

redis 是cs架構(gòu),網(wǎng)絡(luò)采用epoll 模型,單線程處理每個(gè)請(qǐng)求。
很多同學(xué)對(duì)單線程有些疑問(wèn)吝沫,簡(jiǎn)單的解釋一下 redis 單線程的意思,redis 服務(wù)端雖說(shuō)是單線程递礼,但是可以同時(shí) 持有很多connection惨险,每個(gè)connection 都可以同時(shí)發(fā)請(qǐng)求,只不過(guò)在 redis 服務(wù)端脊髓,一個(gè)一個(gè)的處理每個(gè)connection 發(fā)過(guò)來(lái)的request辫愉, 通俗點(diǎn)說(shuō)就是,很多請(qǐng)求都能發(fā)過(guò)來(lái)供炼,redis 會(huì)存下來(lái)(其實(shí)是存在每個(gè)connection socket 內(nèi)核緩沖區(qū))一屋,一個(gè)一個(gè)處理。

為什么單線程處理效率如此之高袋哼?

  1. 幾乎所有的操作全部是內(nèi)存操作冀墨,內(nèi)存操作非常快(如果有一些系統(tǒng)調(diào)用涛贯,磁盤操作诽嘉,單線程不會(huì)快的)
  2. 單線程避免了使用鎖(memcache 使用了多線程,因?yàn)槎嗔随i之類的弟翘,也沒(méi)比redis快多少)

EPOLL 介紹

如果想讀懂 redis 網(wǎng)絡(luò)相關(guān)的代碼虫腋,必須先搞清楚 epoll 的使用,epoll 說(shuō)白了就是監(jiān)聽 fd(file descriptor稀余,操作 fd 其實(shí)就是操作socket)悦冀,每當(dāng) fd 上面有消息的時(shí)候(比如 可讀,可寫 消息等)睛琳,就會(huì)得到通知盒蟆,這樣就可以處理了。epoll 主要好處是可以同時(shí)監(jiān)聽多個(gè) fd(可以持有多個(gè) client 連接)师骗,epoll 只有在 持有很多連接历等,并且每個(gè)連接都不是特別活躍的時(shí)候 效率才高,其他的情況辟癌,不見得比 poll,select 高寒屯。

epoll 使用只需要三步:

  1. int epoll_create(int size);
    創(chuàng)建一個(gè)epoll的句柄,size用來(lái)告訴內(nèi)核這個(gè)監(jiān)聽的數(shù)目一共有多大黍少。這個(gè)參數(shù)不同于select()中的第一個(gè)參數(shù)寡夹,給出最大監(jiān)聽的fd+1的值处面。需要注意的是,當(dāng)創(chuàng)建好epoll句柄后要出,它就是會(huì)占用一個(gè)fd值鸳君,在linux下如果查看/proc/進(jìn)程id/fd/,是能夠看到這個(gè)fd的患蹂,所以在使用完epoll后,必須調(diào)用close()關(guān)閉砸紊,否則可能導(dǎo)致fd被耗盡
  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    epoll的事件注冊(cè)函數(shù)传于,它不同與select()是在監(jiān)聽事件時(shí)告訴內(nèi)核要監(jiān)聽什么類型的事件,而是在這里先注冊(cè)要監(jiān)聽的事件類型醉顽。第一個(gè)參數(shù)是epoll_create()的返回值沼溜,第二個(gè)參數(shù)表示動(dòng)作,用三個(gè)宏來(lái)表示:
    • EPOLL_CTL_ADD:注冊(cè)新的fd到epfd中游添;

    • EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件

    • EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd系草;
      第三個(gè)參數(shù)是需要監(jiān)聽的fd,第四個(gè)參數(shù)是告訴內(nèi)核需要監(jiān)聽什么事唆涝,struct epoll_event結(jié)構(gòu)如下:
      typedef union epoll_data {
      void *ptr;
      int fd;
      __uint32_t u32;
      __uint64_t u64;
      } epoll_data_t;

      struct epoll_event {
      __uint32_t events; /* Epoll events /
      epoll_data_t data; /
      User data variable */
      };
      events可以是以下幾個(gè)宏的集合:
      EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀(包括對(duì)端SOCKET正常關(guān)閉)找都;
      EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫; EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來(lái))廊酣; EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤能耻; EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
      EPOLLET: 將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式亡驰,這是相對(duì)于水平觸發(fā)(Level Triggered)來(lái)說(shuō)的晓猛。
      EPOLLONESHOT:只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后凡辱,如果還需要繼續(xù)監(jiān)聽這個(gè)socket的話戒职,需要再次把這個(gè)socket加入到EPOLL隊(duì)列里

  3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    等待事件的產(chǎn)生,類似于select()調(diào)用透乾。參數(shù)events用來(lái)從內(nèi)核得到事件的集合洪燥,maxevents告之內(nèi)核這個(gè)events有多大,這個(gè) maxevents的值不能大于創(chuàng)建epoll_create()時(shí)的size续徽,參數(shù)timeout是超時(shí)時(shí)間(毫秒蚓曼,0會(huì)立即返回,-1將不確定钦扭,也有說(shuō)法說(shuō)是永久阻塞)纫版。該函數(shù)返回需要處理的事件數(shù)目,如返回0表示已超時(shí)客情。

Redis 中 epoll 的使用

Redis epoll 封裝介紹

redis 跟 網(wǎng)絡(luò)相關(guān)的代碼寫的比較簡(jiǎn)潔其弊,主要就兩處

  1. 不同操作系統(tǒng)的 epoll 代碼癞己,都在 ae_epoll.c ae_evport.c ae_kqueue.c ae_select.c 中, linux 使用 ae_epoll.c , mac 使用 ae_kqueue.c
  2. 對(duì) epoll 代碼的封裝在 ae.c 中
    • aeCreateEventLoop 是對(duì) epoll_create 的封裝
    • aeCreateFileEvent 是對(duì) epoll_ctl 的封裝梭伐,同時(shí)會(huì)將rfileProc痹雅, wfileProc 兩個(gè)處理消息的回調(diào)函數(shù)一起封裝
    • aeProcessEvents 是對(duì) epoll_wait 的封裝
    • aeMain 是一個(gè)死循環(huán),不停的調(diào)用 aeProcessEvents, redis 就是在這里不停的收到 client 的 request糊识, 并且一個(gè)一個(gè)處理

aeCreateEventLoop:

創(chuàng)建 aeEventLoop 結(jié)構(gòu)體

/* File event structure */
typedef struct aeFileEvent {
    int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */
    aeFileProc *rfileProc;
    aeFileProc *wfileProc;
    void *clientData;
} aeFileEvent;

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
    aeBeforeSleepProc *aftersleep;
} aeEventLoop;

這個(gè)結(jié)構(gòu)體中主要就是 events, fired 兩個(gè)aeFileEvent類型變量绩社,aeFileEvent 中 rfileProc, wfileProc 是兩個(gè)回調(diào)函數(shù)赂苗, 分別處理讀時(shí)間愉耙, 寫時(shí)間, events 是aeCreateFileEvent 函數(shù)調(diào)用時(shí) 為其賦值拌滋,fird 是 監(jiān)聽到有消息來(lái)的時(shí)候 為其賦值朴沿,在 ae_epoll.c 中 aeApiPoll 函數(shù)。

總結(jié)一下败砂, 服務(wù)啟動(dòng) aeCreateEventLoop 創(chuàng)建 aeEventLoop 類型的變量赌渣, 將需要監(jiān)控的 fd, 通過(guò) aeCreateFileEvent 監(jiān)聽(同時(shí)將 賦值 rfileProc, wfileProc 回調(diào)函數(shù))昌犹, aeProcessEvents 監(jiān)聽到有消息需要處理的時(shí)候坚芜, 會(huì)使用 rfileProc, wfileProc 回調(diào)函數(shù)處理消息。所以祭隔,讀 Redis 網(wǎng)絡(luò)相關(guān)代碼 货岭,其實(shí)只是看 aeCreateFileEvent(監(jiān)聽fd,設(shè)置對(duì)fd的回調(diào)函數(shù)) 在哪些地方被調(diào)用就可以了疾渴。

Redis 關(guān)鍵代碼

  1. initServer(server.c )無(wú)關(guān)代碼刪除:

    void initServer(void) {
      server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);  
      listenToPort(server.port,server.ipfd,&server.ipfd_count);
      for (j = 0; j < server.ipfd_count; j++) {
      if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
                acceptTcpHandler,NULL) == AE_ERR)
                {
                    serverPanic(
                        "Unrecoverable error creating server.ipfd file event.");
                }
        }
        
        這段代碼在默認(rèn)開啟redis-server 的情況下千贯,server.ipfd 代表的fd 是 6379 打開的socket, 在6379監(jiān)聽到的消息搞坝,都調(diào)用 acceptTcpHandler 函數(shù)
    
  2. acceptTcpHandler(networking.c)無(wú)關(guān)代碼刪除
    void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    while(max--) {
    cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
    acceptCommonHandler(cfd,0,cip);
    }
    }
    static void acceptCommonHandler(int fd, int flags, char *ip) {
    client *c;
    c = createClient(fd)
    }
    client *createClient(int fd) {
    aeCreateFileEvent(server.el,fd,AE_READABLE,
    readQueryFromClient, c) == AE_ERR)
    }
    這段代碼很清晰的表明了搔谴, 對(duì)于6379 過(guò)來(lái)的請(qǐng)求,全部 使用acceptTcpHandler 函數(shù)生成一個(gè)新的fd桩撮, 在同時(shí)將這個(gè)fd 放在 eventloop 中監(jiān)聽敦第,并且 使用 readQueryFromClient 來(lái)處理

  3. readQueryFromClient(networking.c) 無(wú)關(guān)代碼刪除
    void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    nread = read(fd, c->querybuf+qblen, readlen);
    processInputBuffer(c);
    }
    readQueryFromClient 就是把請(qǐng)求內(nèi)容讀出來(lái), 在調(diào)用 processInputBuffer 處理店量, processInputBuffer 就是 redis 里面各種業(yè)務(wù)邏輯了芜果,不在介紹

Epoll 總結(jié)

redis 網(wǎng)絡(luò)相關(guān)代碼其實(shí)就是一句話 使用 epoll 處理每一個(gè)請(qǐng)求,也沒(méi)什么好學(xué)習(xí)的融师。右钾。。。舀射。窘茁。。

Redis 如何處理TCP 粘包脆烟,拆包

粘包山林,拆包介紹

tcp是面向流的, 所以tcp對(duì)數(shù)據(jù)內(nèi)容毫無(wú)感知,收到就放在緩沖區(qū)里面等待用戶讀取邢羔,所以從server端讀出來(lái)的數(shù)據(jù)驼抹,可能是按照發(fā)送順序(tcp 保證不亂不丟)的任何內(nèi)容, 這樣 server 端如果無(wú)法識(shí)別出來(lái)一個(gè)完整的數(shù)據(jù)就出錯(cuò)了拜鹤。解決辦法有兩種

  • 特定分隔符砂蔽,比如 http 的一個(gè) request 是以 \r\n\r\n 結(jié)尾的,在服務(wù)端就可以一直讀到這個(gè)特定的 \r\n\r\n 署惯,通過(guò)這種方式可以區(qū)分出來(lái)一個(gè) request 的數(shù)據(jù)
  • 指定長(zhǎng)度, 比如在 前4個(gè)字節(jié)中 存放這條消息的長(zhǎng)度镣隶,這樣就知道就可以通過(guò) read 函數(shù)正確的讀出數(shù)據(jù)极谊。

特定字符的辦法優(yōu)勢(shì)是,不用浪費(fèi)空間存長(zhǎng)度安岂,但是現(xiàn)在的計(jì)算機(jī)環(huán)境通城岵可以忽略這個(gè)浪費(fèi),劣勢(shì)是 每個(gè)字符都需要判斷才能保證正確域那。效率低咙边。

指定長(zhǎng)度的辦法是浪費(fèi)空間存長(zhǎng)度,但是效率高次员,所以基本上可以說(shuō)任何時(shí)候都采用第二種方法

Redis 如何處理

set a 1 這條指令败许,按照 redis 協(xié)議,會(huì)翻譯成

*3
$3
set
$1
a
$1
1

*3 表示有3行數(shù)據(jù)淑蔚, $3 表示 有3個(gè)字符

屬于哪種方法讀者自己感受下市殷。

Reids 的聰明之處

在我沒(méi)讀redis代碼的時(shí)候,我一直認(rèn)為 從緩沖區(qū) 讀出來(lái)自己需要的長(zhǎng)度刹衫,處理好以后醋寝,在從緩沖區(qū)里繼續(xù)讀,看了redis 代碼以后带迟,我才發(fā)現(xiàn)自己 too yong, redis 是這樣做的(networking.c readQueryFromClient 函數(shù)音羞, 代碼有刪減)

    readlen = PROTO_IOBUF_LEN;
    qblen = sdslen(c->querybuf);
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
    nread = read(fd, c->querybuf+qblen, readlen);

define PROTO_IOBUF_LEN (102416) / Generic I/O buffer size */

redis 每次都讀緩沖區(qū)的大小,如果最后一條消息不完整仓犬,下次計(jì)算一下長(zhǎng)度嗅绰,繼續(xù)讀,因?yàn)檫@個(gè)騷操作(知道了其實(shí)就是常規(guī)操作),讓效率大大提升了办陷,不然每次使用 read 系統(tǒng)調(diào)用貌夕,非常影響性能,特別對(duì)于 redis 這種單線程模型程序影響就更大了民镜。

Redis如何處理 half connection

half connection 介紹

client -> server, 雖然我們都說(shuō)connection啡专,其實(shí) 就是client 開著一個(gè) fd, server 開著一個(gè) fd制圈,兩個(gè)fd之間可以互相通信们童,關(guān)閉的時(shí)候 一個(gè) fd 跟另外一個(gè) fd 說(shuō)我準(zhǔn)備關(guān)閉了(tcp 四次揮手), 不過(guò)如果有的極端情況(在大規(guī)模server端是常規(guī)情況),比如拔網(wǎng)線鲸鹦,關(guān)機(jī)慧库,網(wǎng)絡(luò)異常等原因(具體我也沒(méi)試驗(yàn)過(guò)), 可能發(fā)不出任何消息 就斷了,另外一個(gè) fd 就在那里傻傻的等著馋嗜,這就出現(xiàn)了 half connection 的情況齐板。

如何處理 half connection

  • 一般的處理辦法就是心跳檢查,服務(wù)端會(huì) 定時(shí)的 ping 客戶端葛菇,如果連續(xù)幾次 都 ping 不通甘磨,那么就會(huì)主動(dòng)斷開鏈接

為什么不使用 keep_alive 處理

Host Requirements RFC羅列有不使用它的三個(gè)理由:

  • 在短暫的故障期間,它們可能引起一個(gè)良好連接(good connection)被釋放(dropped)
  • 它們消費(fèi)了不必要的寬帶
  • 在以數(shù)據(jù)包計(jì)費(fèi)的互聯(lián)網(wǎng)上它們(額外)花費(fèi)金錢眯停。然而济舆,在許多的實(shí)現(xiàn)中提供了存活定時(shí)器。

這種說(shuō)法有它的道理莺债,但是并不能說(shuō)服我不使用 keep alive滋觉,最能說(shuō)服我的是在知乎上看過(guò)的一句話, keep_alive 只能保證 tcp 是正常的齐邦,但是不能保證 用戶程序是正常的椎侠,自己感受一下這些話。

Redis 如何處理

server.c clientsCronHandleTimeout 函數(shù)

    if (server.maxidletime &&
        !(c->flags & CLIENT_SLAVE) &&    /* no timeout for slaves */
        !(c->flags & CLIENT_MASTER) &&   /* no timeout for masters */
        !(c->flags & CLIENT_BLOCKED) &&  /* no timeout for BLPOP */
        !(c->flags & CLIENT_PUBSUB) &&   /* no timeout for Pub/Sub clients */
        (now - c->lastinteraction > server.maxidletime))
    {
        serverLog(LL_VERBOSE,"Closing idle client");
        freeClient(c);
        return 1;
    } else if (c->flags & CLIENT_BLOCKED) {

......

通過(guò)代碼可以看出來(lái)侄旬,redis 根本就既沒(méi)有用 keep_alive , 也沒(méi)有用 ping肺蔚, 而是簡(jiǎn)單粗暴的通過(guò) client 最后一次訪問(wèn)server 的時(shí)間 條件來(lái)判斷,不管 這條連接是不是正常的儡羔,這樣同樣可以解決 half connection 問(wèn)題宣羊。

我知道 redis 肯定要處理 half connection 的問(wèn)題,所以我開始找 ping 相關(guān)代碼汰蜘,但是沒(méi)找到仇冯,后來(lái)就老老實(shí)實(shí)從定時(shí)相關(guān)代碼里面看,才找到族操。

第一反應(yīng)苛坚,覺(jué)得比較奇怪比被,為什么好的連接也給斷開了呢,是不是redis比較傻泼舱,能不能給他提個(gè)優(yōu)雅處理的pr等缀, 后來(lái)仔細(xì)想想,果然還是我自己 too yong, 作為服務(wù)端娇昙,連接資源還是比較寶貴的尺迂,如果長(zhǎng)時(shí)間不訪問(wèn)服務(wù)端斷開本來(lái)就是很合理的,而且如果用我開始覺(jué)得優(yōu)雅的心跳冒掌,簡(jiǎn)直就是災(zāi)難噪裕,因?yàn)?redis 是單線程的,心跳都是一次網(wǎng)絡(luò)交互股毫。膳音。。铃诬。

我看到過(guò)很多寫心跳處理 half connection 的代碼祭陷,原來(lái)一直覺(jué)得這就是最好的方法,學(xué)習(xí)了 redis 我才發(fā)現(xiàn)別有洞天趣席,而且我仔細(xì)思考了下颗胡,感覺(jué)大部分時(shí)候 redis 這種處理方法更科學(xué)。

感悟

都說(shuō) redis 代碼簡(jiǎn)潔吩坝,易讀,不過(guò)沒(méi)想到竟簡(jiǎn)潔如斯Q颇琛6で蕖! tcp 粘包 拆包的處理闸迷, half connection 的處理嵌纲,都讓我有一種別開生面的感覺(jué)。其實(shí)文章里只寫了有代表性的東西腥沽,時(shí)間有限無(wú)法一一列舉逮走,代碼組織,架構(gòu)都讓我覺(jué)得提升不少今阳, 讀 redis 真的是一種享受师溅,建議看過(guò)這篇文章的朋友都看一看 redis 代碼,不要覺(jué)得難盾舌,其實(shí)你發(fā)現(xiàn)比讀你同事的垃圾代碼 容易多了墓臭。。妖谴。窿锉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嗡载,更是在濱河造成了極大的恐慌窑多,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洼滚,死亡現(xiàn)場(chǎng)離奇詭異埂息,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)判沟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門耿芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人挪哄,你說(shuō)我怎么就攤上這事吧秕。” “怎么了迹炼?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵砸彬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我斯入,道長(zhǎng)砂碉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任刻两,我火速辦了婚禮增蹭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘磅摹。我一直安慰自己滋迈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布户誓。 她就那樣靜靜地躺著饼灿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帝美。 梳的紋絲不亂的頭發(fā)上碍彭,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音悼潭,去河邊找鬼庇忌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舰褪,可吹牛的內(nèi)容都是我干的漆枚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抵知,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼墙基!你這毒婦竟也來(lái)了软族?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤残制,失蹤者是張志新(化名)和其女友劉穎立砸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體初茶,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颗祝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恼布。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片螺戳。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖折汞,靈堂內(nèi)的尸體忽然破棺而出倔幼,到底是詐尸還是另有隱情,我是刑警寧澤爽待,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布损同,位于F島的核電站,受9級(jí)特大地震影響鸟款,放射性物質(zhì)發(fā)生泄漏膏燃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一何什、第九天 我趴在偏房一處隱蔽的房頂上張望组哩。 院中可真熱鬧,春花似錦处渣、人聲如沸禁炒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至暴备,卻和暖如春悠瞬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涯捻。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工浅妆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人障癌。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓凌外,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涛浙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子康辑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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