golang構(gòu)建http服務(wù)

前面有介紹beego web框架刻像, 其實(shí)很多框架都是在 最簡(jiǎn)單的http服務(wù)上做擴(kuò)展的的粟瞬,基本上都是遵循h(huán)ttp協(xié)議,將底層的封裝好混巧,我們使用web框架只要寫(xiě)業(yè)務(wù)邏輯枪向,填代碼就可以了,不用關(guān)心底層實(shí)現(xiàn)咧党。

下面??實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的http服務(wù)

package main

import (
    "fmt"
    "net/http"
)

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}

func main() {
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe("127.0.0.0:8000", nil)
}

上面只使用了 go的內(nèi)置包 net/http

HTTP

除去細(xì)節(jié)秘蛔,理解HTTP構(gòu)建的網(wǎng)絡(luò)應(yīng)用只要關(guān)注兩個(gè)端--客戶端(client)和服務(wù)端(server),兩個(gè)端的交互來(lái)自client的request傍衡,以及server端的response深员。所謂的http服務(wù)器,主要在于如何接受client的request蛙埂,并向client返回response倦畅。

接收request的過(guò)程中,最重要的莫過(guò)于路由(router)绣的,即實(shí)現(xiàn)一個(gè)Multiplexer器叠赐。Go中既可以使用內(nèi)置的mutilplexer--DefaultServeMux,也可以自定義。Multiplexer路由的目的就是為了找到處理器函數(shù)(hander),后者將對(duì)request進(jìn)行處理屡江,同時(shí)構(gòu)建response芭概。

簡(jiǎn)單總結(jié)就是這個(gè)流程:

client -> Request -> Multiplexer(router)->handler ->Response -> client

因此,理解go中的http服務(wù)盼理,最重要的就是要理解Multiplexerhander谈山,Golang中的Multiplexer基于ServerMux結(jié)構(gòu),同時(shí)也實(shí)現(xiàn)了Handler接口

  • handler函數(shù):具有func(w http.ResponseWriter, r *http.Requests)簽名的函數(shù)
  • handler處理器(函數(shù)):經(jīng)過(guò)HanderFunc結(jié)構(gòu)包裝的handler函數(shù)宏怔,它實(shí)現(xiàn)了ServeHTTP接口方法的函數(shù)奏路。調(diào)用handler處理器的ServeHTTP方法時(shí),即調(diào)用handler函數(shù)本身臊诊。
  • handler對(duì)象:實(shí)現(xiàn)了Hander接口ServeHTTP方法的結(jié)構(gòu)鸽粉。

handler函數(shù)和handler對(duì)象的差別在于, 一個(gè)是函數(shù)抓艳,另一個(gè)是結(jié)構(gòu)體触机,他們都有實(shí)現(xiàn)了serverHTTP方法,很多情況下,他們是類似的儡首。

Golang的http處理流程可以用下面一張圖表示片任,后面內(nèi)容是針對(duì)圖進(jìn)行說(shuō)明:


Http包的三個(gè)關(guān)鍵類型

  1. Handler接口
  2. ServeMux接口
  3. HandlerFunc適配器
  4. Server

Handler

Golang沒(méi)有繼承,類多態(tài)的方法可以通過(guò)接口實(shí)現(xiàn)蔬胯。所謂接口則是定義聲明了函數(shù)簽名对供,任何結(jié)構(gòu)只要實(shí)現(xiàn)了與接口函數(shù)簽名相同的方法,就等同于實(shí)現(xiàn)了接口氛濒。go的http服務(wù)都是基于handler進(jìn)行處理的产场。


// A Handler responds to an HTTP request.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished; it
// is not valid to use the ResponseWriter or read from the
// Request.Body after or concurrently with the completion of the
// ServeHTTP call.
//
// Depending on the HTTP client software, HTTP protocol version, and
// any intermediaries between the client and the Go server, it may not
// be possible to read from the Request.Body after writing to the
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
// Except for reading the body, handlers should not modify the
// provided Request.
//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
// and either closes the network connection or sends an HTTP/2
// RST_STREAM, depending on the HTTP protocol. To abort a handler so
// the client sees an interrupted response but the server doesn't log
// an error, panic with the value ErrAbortHandler.
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)   //路由具體實(shí)現(xiàn)
}

任何結(jié)構(gòu)體,只要實(shí)現(xiàn)了ServeHTTP方法舞竿,這個(gè)結(jié)構(gòu)就可以稱之為handler對(duì)象京景。ServeMux會(huì)使用handler并調(diào)用其ServeHTTP方法處理請(qǐng)求并返回響應(yīng)。
所有請(qǐng)求的處理器骗奖、路由ServeMux都滿足該接口确徙。

ServeMux

了解了Handler之后,再看ServeMux重归。
HTTP請(qǐng)求的多路轉(zhuǎn)接器(路由)米愿,它負(fù)責(zé)將每一個(gè)接收到的請(qǐng)求的URL與一個(gè)注冊(cè)模式的列表進(jìn)行匹配,并調(diào)用和URL最匹配的模式的處理器鼻吮。它內(nèi)部用一個(gè)map來(lái)保存所有處理器Handler。
ServeMux源碼很簡(jiǎn)單:

type ServeMux struct {
    mu sync.RWMutex   //鎖较鼓,由于請(qǐng)求涉及到并發(fā)處理椎木,因此這里需要一個(gè)鎖機(jī)制
    m  map[string]muxEntry  // 路由規(guī)則,一個(gè)string對(duì)應(yīng)一個(gè)mux實(shí)體博烂,這里的string就是注冊(cè)的路由表達(dá)式
    hosts bool // 是否在任意的規(guī)則中帶有host信息
} 
type muxEntry struct {
    h        Handler // 這個(gè)路由表達(dá)式對(duì)應(yīng)哪個(gè)handler
    pattern  string  //匹配字符串
}

ServeMux結(jié)構(gòu)中最重要的字段為m香椎,這是一個(gè)map,key是一些url模式禽篱,value是一個(gè)muxEntry結(jié)構(gòu)畜伐,后者里定義存儲(chǔ)了具體的url模式和handler。

當(dāng)然躺率,所謂的ServeMux也實(shí)現(xiàn)了ServeHTTP接口玛界,也算是一個(gè)handler,不過(guò)ServeMux的ServeHTTP方法不是用來(lái)處理request和respone悼吱,而是用來(lái)找到路由注冊(cè)的handler慎框。

  • http包有一個(gè)包級(jí)別變量DefaultServeMux,表示默認(rèn)路由:var DefaultServeMux = NewServeMux()后添,使用包級(jí)別的http.Handle()笨枯、http.HandleFunc()方法注冊(cè)處理器時(shí)都是注冊(cè)到該路由中。
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
  • ServeMux結(jié)構(gòu)體有ServeHTTP()方法(滿足Handler接口),主要用于間接調(diào)用它所保存的muxEntry中保存的Handler處理器的ServeHTTP()方法馅精。

HandlerFunc適配器

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
  • 自行定義的處理函數(shù)轉(zhuǎn)換為Handler類型就是HandlerFunc調(diào)用之后的結(jié)果严嗜,這個(gè)類型默認(rèn)就實(shí)現(xiàn)了ServeHTTP這個(gè)接口,即我們調(diào)用了HandlerFunc(f),強(qiáng)制類型轉(zhuǎn)換f成為HandlerFunc類型洲敢,這樣f就擁有了ServeHTTP方法漫玄。

Server

除了ServeMux和Handler,還有一個(gè)結(jié)構(gòu)Server需要了解沦疾。從http.ListenAndServe的源碼可以看出称近,它創(chuàng)建了一個(gè)server對(duì)象,并調(diào)用server對(duì)象的ListenAndServe方法:

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

查看server的結(jié)構(gòu)如下:

type Server struct {
    Addr         string        
    Handler      Handler       
    ReadTimeout  time.Duration 
    WriteTimeout time.Duration 
    TLSConfig    *tls.Config   

    MaxHeaderBytes int

    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)

    ConnState func(net.Conn, ConnState)
    ErrorLog *log.Logger
    disableKeepAlives int32     nextProtoOnce     sync.Once 
    nextProtoErr      error     
}

server結(jié)構(gòu)存儲(chǔ)了服務(wù)器處理請(qǐng)求常見(jiàn)的字段哮塞。其中Handler字段也保留Hander接口刨秆。如果Server接口沒(méi)有提供Handler結(jié)構(gòu)對(duì)象,那么會(huì)使用DefaultServeMux做Multiplexer忆畅。

創(chuàng)建HTTP服務(wù)

創(chuàng)建一個(gè)http服務(wù)衡未,大致需要經(jīng)歷兩個(gè)過(guò)程,首先需要注冊(cè)路由家凯,即提供url模式和handler函數(shù)的映射缓醋,其次就是實(shí)例化一個(gè)server對(duì)象,并開(kāi)啟對(duì)客戶端的監(jiān)聽(tīng)绊诲。

http.HandleFunc("/", indexHandler)
http.ListenAndServe("127.0.0.1:8000", nil)

或者

server := &Server{Addr: addr, Handler: handler}
server.ListenAndServe()

http注冊(cè)路由

? net/http包暴露的注冊(cè)路由的api很簡(jiǎn)單送粱,http.HandleFunc選取了DefaultServeMux作為multiplexer:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

? 實(shí)際上,DefaultServeMux是ServeMux的一個(gè)實(shí)例掂之。當(dāng)然http包也提供了NewServeMux方法創(chuàng)建一個(gè)ServeMux實(shí)例抗俄,默認(rèn)則創(chuàng)建一個(gè)DefaultServeMux:

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux {
    return new(ServeMux)
}

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

注意,go創(chuàng)建實(shí)例的過(guò)程中世舰,也可以使用指針?lè)绞蕉ⅲ?/p>

type Server struct{}
server := Server{}

和下面的一樣都可以創(chuàng)建Server的實(shí)例

var DefaultServer Server
var server = &DefalutServer

因此DefaultServeMux的HandleFunc(pattern,handler)方法實(shí)際是定義在ServeMux下的:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

上述代碼中,HandlerFunc是一個(gè)函數(shù)類型跟压。同時(shí)實(shí)現(xiàn)了Handler接口的ServeHTTP方法胰蝠。使用HandlerFunc類型包裝一下路由定義的indexHandler函數(shù),其目的就是為了讓這個(gè)函數(shù)也實(shí)現(xiàn)ServeHTTP方法震蒋,即轉(zhuǎn)變成一個(gè)handler處理器(函數(shù))茸塞。

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

一旦這樣做了,就意味著我們的indexHandler函數(shù)也有了ServeHTTP方法喷好。 此外翔横,ServeMux的Handle方法,將會(huì)對(duì)pattern和handler函數(shù)做一個(gè)map映射:

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }

    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {

        path := pattern
        if pattern[0] != '/' {
            path = pattern[strings.Index(pattern, "/"):]
        }
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

由此可見(jiàn)梗搅,Handle函數(shù)的主要目的在于把handler和pattern模式綁定到map[string]muxEntry的map上禾唁,其中muxEntry保存了更多pattern和handler的信息效览。Server的m字段就是map[string]muxEntry這樣一個(gè)map。

此時(shí)荡短,pattern和handler的路由注冊(cè)完成丐枉。

開(kāi)啟監(jiān)聽(tīng)

注冊(cè)好路由之后,啟動(dòng)web服務(wù)還需要開(kāi)啟服務(wù)器監(jiān)聽(tīng)掘托。http的ListenAndServer方法中可以看到創(chuàng)建了一個(gè)Server對(duì)象瘦锹,并調(diào)用了Server對(duì)象的同名方法:

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

func (srv Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(net.TCPListener)})
}

Server的ListenAndServer方法中,會(huì)初始化監(jiān)聽(tīng)地址Addr闪盔,同時(shí)調(diào)用Listen方法設(shè)置監(jiān)聽(tīng)弯院。最后將監(jiān)聽(tīng)的TCP對(duì)象傳入Serve方法:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    ...

    baseCtx := context.Background()
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
    for {
        rw, e := l.Accept()
        ...
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

處理請(qǐng)求

監(jiān)聽(tīng)開(kāi)啟之后,一旦客戶端請(qǐng)求到底泪掀,go就開(kāi)啟一個(gè)協(xié)程處理請(qǐng)求听绳,主要邏輯都在server方法之中。

serve方法比較長(zhǎng)异赫,其主要職能就是椅挣,創(chuàng)建一個(gè)上下文對(duì)象,然后調(diào)用Listener的Accept方法用來(lái)獲取連接數(shù)據(jù)并使用newConn方法創(chuàng)建連接對(duì)象塔拳。最后使用goroutein協(xié)程的方式處理連接請(qǐng)求鼠证。因此每一個(gè)連接都開(kāi)啟了一個(gè)協(xié)程,請(qǐng)求的上下文都不同靠抑,同時(shí)又保證了go的高并發(fā)量九。serve也是一個(gè)長(zhǎng)長(zhǎng)的方法:

func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    defer func() {
        if err := recover(); err != nil {
            const size = 64 << 10
            buf := make([]byte, size)
            buf = buf[:runtime.Stack(buf, false)]
            c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
        }
        if !c.hijacked() {
            c.close()
            c.setState(c.rwc, StateClosed)
        }
    }()

    ...

    for {
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // If we read any bytes off the wire, we're active.
            c.setState(c.rwc, StateActive)
        }
        ...
        
        }
        
        ...
    
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()
        if !w.shouldReuseConnection() {
            if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                c.closeWriteAndWait()
            }
            return
        }
        c.setState(c.rwc, StateIdle)
    }
}

盡管serve很長(zhǎng),里面的結(jié)構(gòu)和邏輯還是很清晰的颂碧,使用defer定義了函數(shù)退出時(shí)娩鹉,連接關(guān)閉相關(guān)的處理。然后就是讀取連接的網(wǎng)絡(luò)數(shù)據(jù)稚伍,并處理讀取完畢時(shí)候的狀態(tài)。接下來(lái)就是調(diào)用serverHandler{c.server}.ServeHTTP(w,w.req)方法處理請(qǐng)求了戚宦。最后就是請(qǐng)求處理完畢的邏輯个曙。serverHandler是一個(gè)重要的結(jié)構(gòu),它僅有一個(gè)字段受楼,即Server結(jié)構(gòu)垦搬,同時(shí)它也實(shí)現(xiàn)了Hander接口方法ServeHTTP,同時(shí)它也實(shí)現(xiàn)了Handler接口方法ServeHTTP艳汽,并在該接口方法中做了一個(gè)重要的事情猴贰,初始化multiplexer路由多路復(fù)用器。如果server對(duì)象沒(méi)有制定Handler河狐,則使用默認(rèn)的DefaultServeMux作為路由Multiplexer米绕。并調(diào)用初始化Handler的ServeHTTP方法瑟捣。

type serverHandler struct {
    srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

這里DefaultServeMux的ServeHTTP方法其實(shí)也是定義在ServeMux結(jié)構(gòu)中的,相關(guān)代碼如下:

func (mux *ServeMux) (w ResponseWriter, r Request) {
    if r.RequestURI == "" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    if r.Method != "CONNECT" {
        if p := cleanPath(r.URL.Path); p != r.URL.Path {
            _, pattern = mux.handler(r.Host, p)
            url := *r.URL
            url.Path = p
            return RedirectHandler(url.String(), StatusMovedPermanently), pattern
        }
    }
    return mux.handler(r.Host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    // Host-specific pattern takes precedence over generic ones
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    var n = 0
    for k, v := range mux.m {
        if !pathMatch(k, path) {
        continue
    }
    if h == nil || len(k) > n {
        n = len(k)
        h = v.h
        pattern = v.pattern
        }
    }
    return
}

mux的ServeHTTP方法通過(guò)調(diào)用其Handler方法尋找注冊(cè)到路由上的handler函數(shù)栅干,并調(diào)用該函數(shù)的ServeHTTP方法迈套。

mux的Handler方法對(duì)URL簡(jiǎn)單的處理,然后調(diào)用handler方法碱鳞,后者會(huì)創(chuàng)建一個(gè)鎖桑李,同時(shí)調(diào)用match方法返回一個(gè)handler和pattern。

在match方法中窿给,mux的m字段是map[string]muxEntry贵白,后者存儲(chǔ)了pattern和handler處理器函數(shù),因此通過(guò)迭代m尋找出注冊(cè)路由的pattern模式與實(shí)際url匹配的handler函數(shù)并返回崩泡。

返回的結(jié)構(gòu)一直傳遞到mux的ServeHTTP方法禁荒,接下來(lái)調(diào)用handler函數(shù)的ServeHTTP方法,即IndexHandler函數(shù)允华,然后把reponse寫(xiě)到http.RequestWirter對(duì)象返回給客戶端圈浇。

上述函數(shù)運(yùn)行結(jié)束即serverHandler{c.server}.ServeHTTP(w, w.req)運(yùn)行結(jié)束。接下來(lái)就是對(duì)請(qǐng)求處理完畢之后上希望和連接斷開(kāi)的相關(guān)邏輯靴寂。

至此磷蜀,Golang中一個(gè)完整的http服務(wù)介紹完畢,包括注冊(cè)路由百炬,開(kāi)啟監(jiān)聽(tīng)褐隆,處理連接,路由處理函數(shù)。

最后總結(jié)一下:


GO Http執(zhí)行流程

首先調(diào)用Http.HandleFunc
按順序做了幾件事:

  1. 調(diào)用了DefaultServeMux的HandleFunc

  2. 調(diào)用了DefaultServeMux的Handle

  3. 往DefaultServeMux的map[string]muxEntry中增加對(duì)應(yīng)的handler和路由規(guī)則

其次調(diào)用http.ListenAndServe(":9090", nil) - nil使用默認(rèn)路由器
按順序做了幾件事情:

  1. 實(shí)例化Server
  2. 調(diào)用Server的ListenAndServe()
  3. 調(diào)用net.Listen("tcp", addr)監(jiān)聽(tīng)端口
  4. 啟動(dòng)一個(gè)for循環(huán),在循環(huán)體中Accept請(qǐng)求
  5. 對(duì)每個(gè)請(qǐng)求實(shí)例化一個(gè)Conn馒稍,并且開(kāi)啟一個(gè)goroutine為這個(gè)請(qǐng)求進(jìn)行服務(wù)go c.serve()
  6. 讀取每個(gè)請(qǐng)求的內(nèi)容w, err := c.readRequest()
  7. 判斷handler是否為空齐蔽,如果沒(méi)有設(shè)置handler(這個(gè)例子就沒(méi)有設(shè)置handler),handler就設(shè)置為DefaultServeMux
  8. 調(diào)用handler的ServeHttp
  9. 在這個(gè)例子中厨疙,下面就進(jìn)入到DefaultServeMux.ServeHttp
  10. 根據(jù)request選擇handler,并且進(jìn)入到這個(gè)handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)
    11 選擇handler:

A 判斷是否有路由能滿足這個(gè)request(循環(huán)遍歷ServeMux的muxEntry)
B 如果有路由滿足,調(diào)用這個(gè)路由handler的ServeHTTP
C 如果沒(méi)有路由滿足缴守,調(diào)用NotFoundHandler的ServeHTTP

參考文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市镇辉,隨后出現(xiàn)的幾起案子屡穗,更是在濱河造成了極大的恐慌,老刑警劉巖忽肛,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件村砂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屹逛,警方通過(guò)查閱死者的電腦和手機(jī)础废,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門汛骂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人色迂,你說(shuō)我怎么就攤上這事香缺。” “怎么了歇僧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵图张,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我诈悍,道長(zhǎng)祸轮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任侥钳,我火速辦了婚禮适袜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舷夺。我一直安慰自己苦酱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布给猾。 她就那樣靜靜地躺著疫萤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪敢伸。 梳的紋絲不亂的頭發(fā)上扯饶,一...
    開(kāi)封第一講書(shū)人閱讀 52,166評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音池颈,去河邊找鬼尾序。 笑死,一個(gè)胖子當(dāng)著我的面吹牛躯砰,可吹牛的內(nèi)容都是我干的每币。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼琢歇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼脯爪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起矿微,我...
    開(kāi)封第一講書(shū)人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尚揣,沒(méi)想到半個(gè)月后涌矢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡快骗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年娜庇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了塔次。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡名秀,死狀恐怖励负,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匕得,我是刑警寧澤继榆,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站汁掠,受9級(jí)特大地震影響略吨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜考阱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一翠忠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乞榨,春花似錦秽之、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至态秧,卻和暖如春董虱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背申鱼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工愤诱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捐友。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓淫半,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親匣砖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子科吭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

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