net/http請(qǐng)求服務(wù)端處理源碼分析

http服務(wù)端是從*http.Server.ListenAndServe()開(kāi)始的。Server數(shù)據(jù)結(jié)構(gòu)定義了Handler字段将硝,聲明了http服務(wù)器如何處理(w,*r)

type Server struct {
    Handler Handle
}

Handler接口定義:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Handler可以自定義實(shí)現(xiàn),或是由上層框架實(shí)現(xiàn)腐芍。服務(wù)端監(jiān)聽(tīng)工作忧吟,已由net/http標(biāo)準(zhǔn)庫(kù)完成养筒。

首先是外層的監(jiān)聽(tīng)函數(shù)Serve(l)用來(lái)監(jiān)聽(tīng)tcp連接曾撤,有連接建立時(shí)accepted,開(kāi)一個(gè)協(xié)程處理請(qǐng)求晕粪。服務(wù)器用一個(gè)線(xiàn)程處理多個(gè)客戶(hù)端請(qǐng)求挤悉,是典型的異步應(yīng)用場(chǎng)景。由于go語(yǔ)言支持協(xié)程巫湘,代替了這樣的應(yīng)用場(chǎng)景装悲,go serve(ctx)簡(jiǎn)單的一行代碼,就完成了復(fù)雜的異步調(diào)用完成的功能尚氛。并且業(yè)務(wù)邏輯不分節(jié)诀诊,不需要關(guān)心線(xiàn)程池的調(diào)參。

協(xié)程中執(zhí)行的閉包阅嘶,主要由以下三塊邏輯:

  • 從TCP連接的buf緩沖中讀取請(qǐng)求字節(jié)流属瓣,并構(gòu)建Request請(qǐng)求。
  • 調(diào)用http.Server.Handler處理請(qǐng)求讯柔。
  • fnishRequest刷新TCP連接的緩存抡蛙,完成http響應(yīng)。

Serve(l)獲取連接

sync.Once

Java的兩種實(shí)現(xiàn)

舉一個(gè)例子魂迄,JDK7中的concurrentHashmap在構(gòu)建segment時(shí)粗截,會(huì)保證構(gòu)建唯一的Segment。是通過(guò)CAS無(wú)鎖操作實(shí)現(xiàn)的:volatile判空捣炬,線(xiàn)程中構(gòu)建熊昌,CAS賦值。常規(guī)的方法是添加的全局的volatile bool湿酸,在構(gòu)造函數(shù)的最后一行利用volatile禁止重排序的語(yǔ)義浴捆,標(biāo)記構(gòu)造已完成。并且要對(duì)構(gòu)造語(yǔ)句的集合加鎖稿械,保證其原子性。

//解釋java的思想冲粤,用go語(yǔ)言實(shí)現(xiàn)
var done bool = false
func setup(){
    a = "hello"
    done = true
}
func doprint(){
  if !done{
    setup()
  }
  print(a)
}

而go中也有類(lèi)似的功能美莫,sync.Once,通過(guò)Mutex實(shí)現(xiàn)梯捕。功能為保證閉包全局的唯一一次執(zhí)行厢呵。

var once sync.Once
once.Do()

lintener

在TCP監(jiān)聽(tīng)中,監(jiān)聽(tīng)線(xiàn)程只有一個(gè)listener傀顾,用sync.Once包裹襟铭。第個(gè)Serve(l)方法只管理一個(gè)監(jiān)聽(tīng)器。當(dāng)接受到并建立連接之用,為每個(gè)連接單開(kāi)一個(gè)協(xié)程處理寒砖。并調(diào)用trackListener方法赐劣,將其加入到Server.listeners。如果*Server正在執(zhí)行關(guān)閉哩都,則不進(jìn)行添加魁兼。

Serve(l)方法結(jié)束之前,將連接移除漠嵌。

s.shuttingDown()是一個(gè)標(biāo)志位方法咐汞,如果將其置為true,所有新建連接儒鹿、處理請(qǐng)求等操作化撕,在執(zhí)行之前都會(huì)檢查這個(gè)標(biāo)志位。類(lèi)似于java的volatile并發(fā)讀可見(jiàn)性語(yǔ)義约炎。s.shutdown標(biāo)志位采用了atomic.LoadInt32(&s.inShutdown)的方式確保其并發(fā)讀一致性植阴。

Context配置

由于go沒(méi)有繼承,常用第一形參來(lái)代替被繼承的對(duì)象章钾。所以這樣go可以利用形參墙贱,實(shí)現(xiàn)“多繼承”〖總結(jié)golang的形參特性:

  • 第一形參包含一個(gè)父類(lèi)實(shí)例惨撇,代替繼承
  • 支持閉包傳遞,減少繼承使用
  • 引用傳遞修改結(jié)果府寒,代替返回值魁衙。由于Go本身支持多返回值,這個(gè)特性效果不明顯
  • 支持不定參株搔,切片打散傳入

TCP服務(wù)端剖淀,通過(guò)向ctx加入“http-serve”:*Server的KV對(duì)來(lái)標(biāo)注。

循還體內(nèi)持續(xù)監(jiān)聽(tīng)

調(diào)用l.Accept()阻塞等待監(jiān)聽(tīng)纤房,當(dāng)有連接建立纵隔,將返回net.Conn實(shí)例。

檢測(cè)doneChan通道炮姨,是一個(gè)結(jié)束監(jiān)聽(tīng)的開(kāi)關(guān)捌刮。使能之后遇到,處理完已連接請(qǐng)求之后舒岸,將立即結(jié)束監(jiān)聽(tīng)绅作。

TCP連接具有超時(shí)重試機(jī)制,邏輯為:

if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
//未連接成功蛾派,等待5ms 10ms 20ms...1s不斷重試俄认。

總結(jié)未正確得到TCP連接的情況:

  • doneChan開(kāi)關(guān)使能个少,終止程序。
  • 超時(shí)重試眯杏。
  • 其它錯(cuò)誤夜焦,終止程序

最后新服務(wù)包裹net.Conn代表的TCP連接 和 服務(wù)端實(shí)例*Server。并新建一個(gè)協(xié)程處理TCP連接的數(shù)據(jù)包請(qǐng)求役拴。

由于并發(fā)線(xiàn)程非常有限糊探,使用并發(fā)地方式,一個(gè)線(xiàn)程處理一個(gè)連接請(qǐng)求河闰,會(huì)極大限制服務(wù)器處理連接的數(shù)量(個(gè)人測(cè)試科平,簡(jiǎn)單業(yè)務(wù)邏輯,16G內(nèi)存最大約開(kāi)4000個(gè)線(xiàn)程)姜性。因而產(chǎn)生了異步式編程瞪慧,只用一個(gè)監(jiān)聽(tīng),處理更大量的連接請(qǐng)求部念,由于請(qǐng)求是IO密集型操作弃酌,通過(guò)異步編程可以有效提高其并發(fā)度。具體執(zhí)行實(shí)際仍交給線(xiàn)程池執(zhí)行儡炼。不過(guò)異步編程的缺點(diǎn)也十分明顯妓湘,程序邏輯要截成兩段,拿到異步結(jié)果之后的處理邏輯還要再寫(xiě)一段乌询,同時(shí)程序還需要用并發(fā)哈希臨時(shí)保存未完成的請(qǐng)求榜贴,以及異步結(jié)果提前初始化好對(duì)象,等待接收妹田。

有了協(xié)程和閉包唬党,go語(yǔ)言的處理連接請(qǐng)求方法結(jié)構(gòu)十分清晰,只有一行:

go c.serve(ctx)

在TCP層面鬼佣,為每個(gè)連接單獨(dú)創(chuàng)建協(xié)程處理驶拱。在讀請(qǐng)求的完整包并作http協(xié)議層解析,會(huì)使用對(duì)象池晶衷,減少GC壓力蓝纲,提高處理并發(fā)度。

c.serve(ctx)監(jiān)聽(tīng)連接

net.Conn裝飾

連接的數(shù)據(jù)結(jié)構(gòu):

type conn struct {
    server *Server
    rwc net.Conn
    remoteAddr string
    tlsState *tls.ConnectionState
    cancelCtx context.CancelFunc
    werr error
    
    r *connReader
    bufr *bufio.Reader
    bufw *bufio.Writer
    lastMethod string

    mu sync.Mutex
    hijackedv bool
}

HTTP連接共有三塊內(nèi)容:-

  • 上下層:上層Server服務(wù)端晌纫,下層的TCP連接 客戶(hù)端地址 TLS信息
  • 讀寫(xiě)緩沖區(qū)
  • 狀態(tài)信息:最后http請(qǐng)求方式驻龟,當(dāng)前請(qǐng)求,當(dāng)前狀態(tài)缸匪,是否被handler劫持。

注意到讀緩沖區(qū)有兩種數(shù)據(jù)結(jié)構(gòu) 常規(guī)的bufio.Reader和net/http包實(shí)現(xiàn)的connReader:

type connReader struct {
    conn *conn

    mu      sync.Mutex // guards following
    hasByte bool
    byteBuf [1]byte
    cond    *sync.Cond
    inRead  bool
    aborted bool  // set true before conn.rwc deadline is set to past
    remain  int64 // bytes remaining
}

除了bufio.Reader緩沖區(qū)之外类溢,net/http實(shí)現(xiàn)的connReader是net.Conn的包裹凌蔬,為bufio.Reader提供緩沖數(shù)據(jù)露懒。為何實(shí)現(xiàn)兩個(gè)io.Reader?后面的內(nèi)容會(huì)詳述砂心。

serve(ctx)方法懈词,首先在ctx中保存客戶(hù)端地址c.rwc.LocalAddr()。

ctx裝飾類(lèi)emptyCtx cancelCtx timerCtx

Context是一個(gè)可配置生命周期的辩诞,被多個(gè)協(xié)程并發(fā)訪(fǎng)問(wèn)的KV存儲(chǔ)結(jié)構(gòu)坎弯。可設(shè)置生命周期译暂,或主動(dòng)釋放(可以被GC)抠忘。

其接口為:

type Context interface {
    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}
}

對(duì)于連接的ctx采用如下裝飾,擴(kuò)充其原有的功能

  • emptyCtx int類(lèi)型而非struct{}外永,僅實(shí)現(xiàn)Context接口崎脉,供context.Background()調(diào)用創(chuàng)建空ctx。同樣功能的還有context.Todo()
  • valueCtx 用于ctx的PUT/GET操作
  • cancelCtx伯顶,帶有cancelFunc的ctx囚灼。
  • timerCtx,具有超時(shí)機(jī)制的ctx祭衩。

先看ctx最基本的Put Get操作灶体。

func WithValue(parent Context, key, val interface{}) Context {
    if key == nil {
        panic("nil key")
    }
    if !reflect.TypeOf(key).Comparable() {
        panic("key is not comparable")
    }
    return &valueCtx{parent, key, val}
}

type valueCtx struct {
    Context
    key, val interface{}
}

ctx添加的KV值,直接存在繼承類(lèi)的新字段中掐暮。反射Type有Comparable()方法蝎抽,可用來(lái)檢查類(lèi)型是否可以被用來(lái)作key。不同于哈希劫乱,加參數(shù)并不檢查key是否已存在织中。

獲取字段用遞歸查找其所有的KV。有一個(gè)即返回衷戈,不檢查全部狭吼。

到這里看似這個(gè)數(shù)據(jù)結(jié)構(gòu)和map[]功能并沒(méi)有區(qū)別,實(shí)現(xiàn)卻坡為復(fù)雜殖妇。單獨(dú)寫(xiě)一個(gè)這樣的數(shù)據(jù)結(jié)構(gòu)刁笙,同樣的KV對(duì)之間多了一層父子關(guān)系。也就是說(shuō)參數(shù)是像壓棧一樣被壓入的谦趣,當(dāng)需要一個(gè)元素出棧疲吸,所有后壓入的元素也會(huì)跟著出棧。由于KV對(duì)之間存在父子關(guān)系前鹅,所以允許相同的key存在摘悴。意義在于其concelFunc:

Calling the CancelFunc cancels the child and its children, removes the parent's reference to the child, and stops any associated timers. 
type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     chan struct{}         // created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}
type canceler interface {
    cancel(removeFromParent bool, err error)
    Done() <-chan struct{}
}

canceler接口的實(shí)現(xiàn)類(lèi)就是*cancelCtx and *timerCtx。

創(chuàng)建方法:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    c := newCancelCtx(parent)
    propagateCancel(parent, &c)
    return &c, func() { c.cancel(true, Canceled) }
}

其中Canceled是個(gè)構(gòu)建error對(duì)象舰绘,為打印的信息蹂喻。方法返回cancelCtx和取消ctx的閉包葱椭。propagateCancel方法功能:向上面的parent找一個(gè)cancelCtx,如果這個(gè)cancelCtx已經(jīng)取消口四,則當(dāng)前ctx也取消孵运。否則加入p.children[]添加關(guān)聯(lián),等待被取消蔓彩≈伪浚總之功能為,向上建立父子關(guān)系赤嚼,這種關(guān)系是cancelCtx之間的旷赖。

cancelCtx.Done()功能:加鎖創(chuàng)建cancelCtx.done通道
cancel.calcel()功能:關(guān)閉c.done通道,遞歸調(diào)用所有c.children.cancel()探膊,釋放c.children = nil

c.child的父子關(guān)系杠愧,并不是相鄰的valueCtx繼承關(guān)系,而是相鄰的cancelCtx之間的關(guān)系逞壁。

再來(lái)看看timerCtx:

type timerCtx struct {
    cancelCtx
    timer *time.Timer // Under cancelCtx.mu.

    deadline time.Time
}

可以配置截止日期流济。使用了time.AfterFunc(dur, func() )到期后,自動(dòng)清理腌闯。

AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns a Timer that can be used to cancel the call using its Stop method.

最后利用以上代碼绳瘟,直接實(shí)現(xiàn)最常用的為ctx添加超時(shí)機(jī)制。

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
    return WithDeadline(parent, time.Now().Add(timeout))
}

總結(jié):這種數(shù)據(jù)結(jié)構(gòu)為大量的KV配置信息姿骏,提供批量釋放的功能糖声。功能類(lèi)似于etcd為一組相似的配置信息,配置相同的租約分瘦。最終使用GC更為高效蘸泻,提高內(nèi)存利用率。

connReader bufio.Reader bufio.Writer

c.bufw是池化的嘲玫,且指定了c.bufw.buf的數(shù)組大小為4<<10悦施。并且根據(jù)size大小,分為bufioWriter2kPool和bufioWriter4kPool去团。

bufio.Reader的最小池大小為4M字節(jié)抡诞。

讀取并解析請(qǐng)求

完成以上配置之后循還獲取數(shù)據(jù),并解析為空的Response:

w, err := c.readRequest(ctx)

遵守RFC 7231 5.1.1作Expect校驗(yàn)

如果請(qǐng)求Header中有"Expect":"100-continue"則繼續(xù)土陪,否則關(guān)閉http連接昼汗。

For now we'll just obey RFC 7231 5.1.1 which says "A server that receives an Expect field-value other than 100-continue MAY respond with a 417 (Expectation Failed) status code to indicate that the unexpected expectation cannot be met."

結(jié)束請(qǐng)求的工作:

    w.Header().Set("Connection", "close")
    w.WriteHeader(StatusExpectationFailed) //返回417狀態(tài)碼
    w.finishRequest() //刷新緩存相關(guān)操作

注意到req.Body是包裹了Response的。

為req.Body添加hitEof的后續(xù)處理函數(shù)

先上body數(shù)據(jù)結(jié)構(gòu):

type body struct {
    src          io.Reader
    hdr          interface{}   // non-nil (Response or Request) value means read trailer
    r            *bufio.Reader // underlying wire-format reader for the trailer
    closing      bool          // is the connection to be closed after reading body?
    doEarlyClose bool          // whether Close should stop early

    mu         sync.Mutex // guards following, and calls to Read and Close
    sawEOF     bool
    closed     bool
    earlyClose bool   // Close called and we didn't read to the end of src
    onHitEOF   func() // if non-nil, func to call when EOF is Read
}

再看connReader的數(shù)據(jù)結(jié)構(gòu):

type connReader struct {
    conn *conn

    mu      sync.Mutex // guards following
    hasByte bool
    byteBuf [1]byte
    cond    *sync.Cond
    inRead  bool
    aborted bool  // set true before conn.rwc deadline is set to past
    remain  int64 // bytes remaining
}

先通過(guò)b.sawEOF判斷是否讀完了req.Body鬼雀。讀完req.Body之后顷窒,調(diào)用w.conn.r.startBackgroundRead函數(shù),需要加鎖源哩。

cr.inRead標(biāo)志位判斷body是否被并發(fā)讀蹋肮,原則上不支持并發(fā)讀出刷。

cr.aborted終止讀取,并設(shè)置連接截止時(shí)間為很久以前aLongTimeAgo坯辩,意圖終止連接。

startBackgroundRead方法僅讀取1個(gè)byte崩侠,忽略掉連接超時(shí)漆魔、網(wǎng)絡(luò)錯(cuò)誤、aborted==true的錯(cuò)誤却音,報(bào)錯(cuò)則刪除終止請(qǐng)求改抡,即向res.closeNotifyCh寫(xiě)入true。

調(diào)用handler處理請(qǐng)求

釋放ctx

調(diào)用cancelCtx的方法系瓢,釋放ctx及其子節(jié)點(diǎn)的空間阿纤。

重用連接的處理

重用連接有兩個(gè)條件:s.disableKeepAlive==0 并且s.inShutdown==0,通過(guò)在*Server服務(wù)端參數(shù)設(shè)置夷陋。

還可以在請(qǐng)求中配置重用連接欠拾,與上一個(gè)條件都達(dá)到連接才是可重用的。同時(shí)滿(mǎn)足的條件為:

  • 響應(yīng)頭"Connection: keep-alive"
  • 如果有響應(yīng)body骗绕,必須要寫(xiě)完藐窄。即請(qǐng)求方法非HEAD,且響應(yīng)contentLength非-1酬土,且寫(xiě)入body的類(lèi)型符合的情況下荆忍,響應(yīng)contentLength寫(xiě)入的字節(jié)數(shù)與resp.written相同
  • 沒(méi)有發(fā)生body.earlyClose錯(cuò)誤,也主是未讀完req.Body的情況
  • 沒(méi)有發(fā)生寫(xiě)入響應(yīng)錯(cuò)誤

所以綜上所述撤缴,重用連接共有1刹枉,2,3共計(jì)6個(gè)條件屈呕,一個(gè)是通過(guò)客戶(hù)端在Header中配置微宝,其中兩個(gè)是服務(wù)端的配置參數(shù),其余三個(gè)條件是確保沒(méi)有傳輸錯(cuò)誤凉袱。

配置idleTimeout

將Server.idelTimeout()配置到conn.SetReadDeadline()芥吟。如果因超時(shí)未完成讀取req.Body,或向連接寫(xiě)入response专甩。其處理邏輯定位至上文“重用連接的處理”钟鸵。

至此完成了一次http的請(qǐng)求處理流程。其中readRequest讀包并解析 finishRequest刷緩存寫(xiě)包在后面繼續(xù)更新涤躲。而serverHandler.ServeHTTP()是一上層mvc框架的復(fù)雜邏輯實(shí)現(xiàn)棺耍,單獨(dú)寫(xiě)一個(gè)集合更新。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末种樱,一起剝皮案震驚了整個(gè)濱河市蒙袍,隨后出現(xiàn)的幾起案子俊卤,更是在濱河造成了極大的恐慌,老刑警劉巖害幅,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件消恍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡以现,警方通過(guò)查閱死者的電腦和手機(jī)狠怨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邑遏,“玉大人佣赖,你說(shuō)我怎么就攤上這事〖呛校” “怎么了憎蛤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)纪吮。 經(jīng)常有香客問(wèn)我俩檬,道長(zhǎng),這世上最難降的妖魔是什么彬碱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任豆胸,我火速辦了婚禮,結(jié)果婚禮上巷疼,老公的妹妹穿的比我還像新娘晚胡。我一直安慰自己,他們只是感情好嚼沿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布估盘。 她就那樣靜靜地躺著,像睡著了一般骡尽。 火紅的嫁衣襯著肌膚如雪遣妥。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天攀细,我揣著相機(jī)與錄音箫踩,去河邊找鬼。 笑死谭贪,一個(gè)胖子當(dāng)著我的面吹牛境钟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俭识,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼慨削,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起缚态,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤磁椒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后玫芦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浆熔,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年桥帆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蘸拔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡环葵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宝冕,到底是詐尸還是另有隱情张遭,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布地梨,位于F島的核電站菊卷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏宝剖。R本人自食惡果不足惜洁闰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望万细。 院中可真熱鬧扑眉,春花似錦、人聲如沸赖钞。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雪营。三九已至弓千,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間献起,已是汗流浹背洋访。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谴餐,地道東北人姻政。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像总寒,于是被迫代替她去往敵國(guó)和親扶歪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,737評(píng)論 25 707
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料善镰? 從這篇文章中你...
    hw1212閱讀 12,699評(píng)論 2 59
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理妹萨,服務(wù)發(fā)現(xiàn),斷路器炫欺,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • 初夏已至乎完,暑熱又來(lái)。十年前品洛,初到單位树姨,住單身公寓,條件甚苦桥状。宿舍樓緊鄰內(nèi)河水溝帽揪,因河水污染嚴(yán)重,水質(zhì)發(fā)臭辅斟,蚊...
    沖虛道長(zhǎng)閱讀 234評(píng)論 0 0
  • 讀《山路》總是孤獨(dú)的转晰,憂(yōu)愁的,這里面有我不敢去想的衰老士飒,不敢深思的歸宿查邢。她有房子,可以看見(jiàn)星辰大海酵幕,可以暢聊高朋滿(mǎn)...
    星辰海底閱讀 437評(píng)論 0 0