面試官:net/http庫知道嗎?能說說優(yōu)缺點(diǎn)嗎?

小白debug 2023-01-11 08:00

前言

哈嘍,大家后,我是asong;這幾天看了一下Go語言標(biāo)準(zhǔn)庫net/http的源碼,所以就來分享一下我的學(xué)習(xí)心得;為什么會(huì)突然想看http標(biāo)準(zhǔn)庫呢?因?yàn)樵诿嬖嚨臅r(shí)候面試官問我你知道Go語言的net/http庫嗎?他有什么有缺點(diǎn)嗎?因?yàn)槲覜]有看過這部分源碼,所以一首涼涼送給我;

廢話不多說,接下來請(qǐng)跟著我的腳步我們一起探索net/http

本文代碼基于:Go1.19.3

net/http庫的一個(gè)小demo

服務(wù)端:

import?(
?"fmt"
?"net/http"
)

func?getProfile(w?http.ResponseWriter,?r?*http.Request)?{
?fmt.Fprintln(w,?"asong")
}

func?main()?{
?http.HandleFunc("/profile",?getProfile)
?err?:=?http.ListenAndServe(":8080",?nil)
?if?err?!=?nil?{
??fmt.Printf("http?server?failed,?err:?%v\n",?err)
??return
?}
}

本地啟動(dòng)一個(gè)server端監(jiān)聽8080端口,并且提供路由/profile獲取個(gè)人信息;

客戶端:

import?(
?"fmt"
?"io/ioutil"
?"net/http"
)

func?main()?{
?resp,?err?:=?http.DefaultClient.Get("http://127.0.0.1:8080/profile")
?if?err?!=?nil?{
??fmt.Printf("get?failed,?err:%v\n",?err)
??return
?}
?defer?resp.Body.Close()
?body,?err?:=?ioutil.ReadAll(resp.Body)
?if?err?!=?nil?{
??fmt.Printf("read?from?resp.body?failed,?err:%v\n",?err)
??return
?}
?fmt.Println(string(body))
}

通過這樣一個(gè)簡(jiǎn)單的例子,我們可以知道客戶端我們主要使用http.Client{},服務(wù)端我們主要使用http.ListenAndServehttp.HandleFunc,所以我們就可以從這兩個(gè)包入手來分別看一看客戶端和服務(wù)端的代碼具體是怎么封裝;

客戶端實(shí)現(xiàn)

客戶端級(jí)別最高的抽象是net/http.Client{},具體結(jié)構(gòu)如下:

type?Client?struct?{
????Transport?RoundTripper
????CheckRedirect?func(req?*Request,?via?[]*Request)?error
????Jar?CookieJar
????Timeout?time.Duration
}
  • Transport:其類型是RoundTripper,RoundTrip代表一個(gè)本地事務(wù),RoundTripper接口的實(shí)現(xiàn)主要有三個(gè):Transport、http2Transport、fileTransport,其目的是支持更好的擴(kuò)展性;
  • CheckRedirect:用來做重定向
  • Jar:其類型是CookieJar,用來做cookie管理,CookieJar接口的實(shí)現(xiàn)Jar結(jié)構(gòu)體在源碼包net/http/cookiejar/jar.go

客戶端可以直接通過net/http.DefaultClient發(fā)起HTTP請(qǐng)求,也可以自己構(gòu)建新的net/http.Client實(shí)現(xiàn)自定義的HTTP事務(wù),多數(shù)情況下我們使用默認(rèn)的客戶端發(fā)出的請(qǐng)求就可以滿足需求;

我們畫一個(gè)UML圖,如下所示:

image-20221204160448320

了解HTTP客戶端的基本結(jié)構(gòu),我們接下來就開始分析客戶端的基本實(shí)現(xiàn);

構(gòu)建Request

net/http包的Request結(jié)構(gòu)體封裝好了HTTP請(qǐng)求所需的必要信息:

type?Request?struct?{
?Method?string
?URL?*url.URL
?Proto??????string?//?"HTTP/1.0"
?ProtoMajor?int????//?1
?ProtoMinor?int????//?0
?Header?Header
?Body?io.ReadCloser
?GetBody?func()?(io.ReadCloser,?error)
?ContentLength?int64
?removed?as?necessary?when?sending?and
?TransferEncoding?[]string
?Close?bool
?Host?string
?Form?url.Values
?PostForm?url.Values
?MultipartForm?*multipart.Form
?Trailer?Header
?RemoteAddr?string
?RequestURI?string
?TLS?*tls.ConnectionState
?Cancel?<-chan?struct{}
?Response?*Response
?ctx?context.Context
}

其中包含了HTTP請(qǐng)求的方法、URL、協(xié)議版本、協(xié)議頭以及請(qǐng)求體等字段,還包括了指向響應(yīng)的引用:Response;其提供了NewRequest()、NewRequestWithContext()兩個(gè)方法用來構(gòu)建請(qǐng)求,這個(gè)方法可以校驗(yàn)HTTP請(qǐng)求的字段并根據(jù)輸入的參數(shù)拼裝成新的請(qǐng)求結(jié)構(gòu)體,NewRequest()方法內(nèi)部也是調(diào)用的NewRequestWithContext,區(qū)別就是是否使用context來做goroutine上下文傳遞;接下來我們看一下NewRequestWithContext方法的具體實(shí)現(xiàn):

func?NewRequestWithContext(ctx?context.Context,?method,?url?string,?body?io.Reader)?(*Request,?error)?{
????//?默認(rèn)使用GET方法
?if?method?==?""?{
??method?=?"GET"
?}
????//?校驗(yàn)方法是否有效,也就是是否是GET、POST、PUT等
?if?!validMethod(method)?{
??return?nil,?fmt.Errorf("net/http:?invalid?method?%q",?method)
?}
????//?ctx必須要傳遞,NewRequest方法調(diào)用時(shí)會(huì)傳遞context.Background()
?if?ctx?==?nil?{
??return?nil,?errors.New("net/http:?nil?Context")
?}
????//?解析URL,解析Scheme、Host、Path等信息
?u,?err?:=?urlpkg.Parse(url)
?if?err?!=?nil?{
??return?nil,?err
?}
????//?body在下面會(huì)根據(jù)其類型包裝成io.ReadCloser類型
?rc,?ok?:=?body.(io.ReadCloser)
?if?!ok?&&?body?!=?nil?{
??rc?=?io.NopCloser(body)
?}
?//?The?host's?colon:port?should?be?normalized.?See?Issue?14836.
?u.Host?=?removeEmptyPort(u.Host)
?req?:=?&Request{
??ctx:????????ctx,
??Method:?????method,
??URL:????????u,
??Proto:??????"HTTP/1.1",
??ProtoMajor:?1,
??ProtoMinor:?1,
??Header:?????make(Header),
??Body:???????rc,
??Host:???????u.Host,
?}
?if?body?!=?nil?{
??switch?v?:=?body.(type)?{
??case?*bytes.Buffer:
???req.ContentLength?=?int64(v.Len())
???buf?:=?v.Bytes()
???req.GetBody?=?func()?(io.ReadCloser,?error)?{
????r?:=?bytes.NewReader(buf)
????return?io.NopCloser(r),?nil
???}
??case?*bytes.Reader:
???req.ContentLength?=?int64(v.Len())
???snapshot?:=?*v
???req.GetBody?=?func()?(io.ReadCloser,?error)?{
????r?:=?snapshot
????return?io.NopCloser(&r),?nil
???}
??case?*strings.Reader:
???req.ContentLength?=?int64(v.Len())
???snapshot?:=?*v
???req.GetBody?=?func()?(io.ReadCloser,?error)?{
????r?:=?snapshot
????return?io.NopCloser(&r),?nil
???}
??default:
??if?req.GetBody?!=?nil?&&?req.ContentLength?==?0?{
???req.Body?=?NoBody
???req.GetBody?=?func()?(io.ReadCloser,?error)?{?return?NoBody,?nil?}
??}
?}

?return?req,?nil
}

整個(gè)構(gòu)建請(qǐng)求過程中只有處理body的時(shí)候稍有一些復(fù)雜,我們需要根據(jù)body的類型使用不同的方法將其包裝成io.ReadCloser類型;

啟動(dòng)事務(wù)

構(gòu)建HTTP請(qǐng)求后,接下來我們需要開啟HTTP事務(wù)進(jìn)行請(qǐng)求并且等待遠(yuǎn)程響應(yīng),我們以net/http.Client.Do()方法為例子,我們看一下它的調(diào)用鏈路:

  • net/http.Client.Do()
  • net/http.Client.do()
  • net/http.Client.send()
  • net/http.Send()
  • net/http.Transport.RoundTrip()

RoundTrip()是RoundTripper類型中的一個(gè)的方法,net/http.Transport是其中的一個(gè)實(shí)現(xiàn),在net/http/transport.go文件中我們可以找到這個(gè)方法:

//?roundTrip?implements?a?RoundTripper?over?HTTP.
func?(t?*Transport)?roundTrip(req?*Request)?(*Response,?error)?{
?t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)
?ctx?:=?req.Context()
?trace?:=?httptrace.ContextClientTrace(ctx)
????//?省略前置檢查部分
????.....
?for?{
????????//?用來檢測(cè)ctx退出信號(hào)
??select?{
??case?<-ctx.Done():
???req.closeBody()
???return?nil,?ctx.Err()
??default:
??}

??//?獲取連接,這塊就是我們要看的重點(diǎn),Go語言通過連接池對(duì)資源進(jìn)行了復(fù)用;
??pconn,?err?:=?t.getConn(treq,?cm)
??if?err?!=?nil?{
???t.setReqCanceler(cancelKey,?nil)
???req.closeBody()
???return?nil,?err
??}

??var?resp?*Response
??if?pconn.alt?!=?nil?{
???//?HTTP/2?path.
???t.setReqCanceler(cancelKey,?nil)?//?not?cancelable?with?CancelRequest
???resp,?err?=?pconn.alt.RoundTrip(req)
??}?else?{
????????????//?開始處理響應(yīng)
???resp,?err?=?pconn.roundTrip(treq)
??}
??if?err?==?nil?{
???resp.Request?=?origReq
???return?resp,?nil
??}

??//?Rewind?the?body?if?we're?able?to.
??req,?err?=?rewindBody(req)
??if?err?!=?nil?{
???return?nil,?err
??}
?}
}

代碼一大堆,我們只要重點(diǎn)看兩部分即可:

  • net/http.Transport.getConn()獲取連接
  • net/http.persistConn.roundTrip()處理寫入HTTP請(qǐng)求并在select中等待響應(yīng)的返回;

獲取連接

func?(t?*Transport)?getConn(treq?*transportRequest,?cm?connectMethod)?(pc?*persistConn,?err?error)?{
?req?:=?treq.Request
?trace?:=?treq.trace
?ctx?:=?req.Context()
?if?trace?!=?nil?&&?trace.GetConn?!=?nil?{
??trace.GetConn(cm.addr())
?}

?w?:=?&wantConn{
??cm:?????????cm,
??key:????????cm.key(),
??ctx:????????ctx,
??ready:??????make(chan?struct{},?1),
??beforeDial:?testHookPrePendingDial,
??afterDial:??testHookPostPendingDial,
?}
?defer?func()?{
??if?err?!=?nil?{
???w.cancel(t,?err)
??}
?}()

?//?在隊(duì)列中有閑置連接,直接返回
?if?delivered?:=?t.queueForIdleConn(w);?delivered?{
??pc?:=?w.pc
??return?pc,?nil
?}

?cancelc?:=?make(chan?error,?1)
?t.setReqCanceler(treq.cancelKey,?func(err?error)?{?cancelc?<-?err?})

?//?放到隊(duì)列中等待建立新的連接
?t.queueForDial(w)

?//?阻塞等待連接
?select?{
?case?<-w.ready:
??return?w.pc,?w.err
?case?<-req.Cancel:
??return?nil,?errRequestCanceledConn
?case?<-req.Context().Done():
??return?nil,?req.Context().Err()
?case?err?:=?<-cancelc:
??if?err?==?errRequestCanceled?{
???err?=?errRequestCanceledConn
??}
??return?nil,?err
?}
}

因?yàn)檫B接的建議會(huì)消耗比較多的時(shí)間,帶來較大的開下,所以Go語言使用了連接池對(duì)資源進(jìn)行分配和復(fù)用,先調(diào)用net/http.Transport.queueForIdleConn()獲取等待閑置的連接,如果沒有獲取到在調(diào)用net/http.Transport.queueForDial在隊(duì)列中等待建立新的連接,通過select監(jiān)聽連接是否建立完畢,超時(shí)未獲取到連接會(huì)上剖錯(cuò)誤,我們繼續(xù)在queueForDial追蹤TCP連接的建立:

func?(t?*Transport)?queueForDial(w?*wantConn)?{
?w.beforeDial()
?if?t.MaxConnsPerHost?<=?0?{
??go?t.dialConnFor(w)
??return
?}

?t.connsPerHostMu.Lock()
?defer?t.connsPerHostMu.Unlock()

?if?n?:=?t.connsPerHost[w.key];?n???if?t.connsPerHost?==?nil?{
???t.connsPerHost?=?make(map[connectMethodKey]int)
??}
??t.connsPerHost[w.key]?=?n?+?1
??go?t.dialConnFor(w)
??return
?}
????....
}

我們會(huì)啟動(dòng)一個(gè)goroutine做tcp的建連,最終調(diào)用dialConn方法,在這個(gè)方法內(nèi)做持久化連接,調(diào)用net庫的dial方法進(jìn)行TCP連接:

func?(t?*Transport)?dialConn(ctx?context.Context,?cm?connectMethod)?(pconn?*persistConn,?err?error)?{
?pconn?=?&persistConn{
??t:?????????????t,
??cacheKey:??????cm.key(),
??reqch:?????????make(chan?requestAndChan,?1),
??writech:???????make(chan?writeRequest,?1),
??closech:???????make(chan?struct{}),
??writeErrCh:????make(chan?error,?1),
??writeLoopDone:?make(chan?struct{}),
?}

?conn,?err?:=?t.dial(ctx,?"tcp",?cm.addr())
?if?err?!=?nil?{
??return?nil,?err
?}
?pconn.conn?=?conn

?pconn.br?=?bufio.NewReaderSize(pconn,?t.readBufferSize())
?pconn.bw?=?bufio.NewWriterSize(persistConnWriter{pconn},?t.writeBufferSize())

?go?pconn.readLoop()
?go?pconn.writeLoop()
?return?pconn,?nil
}

在連接建立后,代碼中我們我們還看到分別啟動(dòng)了兩個(gè)goroutine,readLoop用于從tcp連接中讀取數(shù)據(jù),writeLoop用于從tcp連接中寫入數(shù)據(jù);

我們看一下writeLoop方法:

func?(pc?*persistConn)?writeLoop()?{
?defer?close(pc.writeLoopDone)
?for?{
??select?{
??case?wr?:=?<-pc.writech:
???startBytesWritten?:=?pc.nwrite
???err?:=?wr.req.Request.write(pc.bw,?pc.isProxy,?wr.req.extra,?pc.waitForContinue(wr.continueCh))
???if?bre,?ok?:=?err.(requestBodyReadError);?ok?{
????err?=?bre.error
????wr.req.setError(err)
???}
???if?err?==?nil?{
????err?=?pc.bw.Flush()
???}
???if?err?!=?nil?{
????if?pc.nwrite?==?startBytesWritten?{
?????err?=?nothingWrittenError{err}
????}
???}
???pc.writeErrCh?<-?err?//?to?the?body?reader,?which?might?recycle?us
???wr.ch?<-?err?????????//?to?the?roundTrip?function
???if?err?!=?nil?{
????pc.close(err)
????return
???}
??case?<-pc.closech:
???return
??}
?}
}

監(jiān)聽writech通道,所以的數(shù)據(jù)發(fā)送都是在這個(gè)循環(huán)中寫入的;

net/http.Transport{}中提供了連接池配置參數(shù),開發(fā)者可以自行定義:

type?Transport?struct?{
?......
????MaxIdleConns?int
????MaxIdleConnsPerHost?int
????MaxConnsPerHost?int
????IdleConnTimeout?time.Duration
????......
}

處理HTTP請(qǐng)求

net/http.persistConn.roundTrip()會(huì)處理HTTP請(qǐng)求,我們看其具體實(shí)現(xiàn):

func?(pc?*persistConn)?roundTrip(req?*transportRequest)?(resp?*Response,?err?error)?{
????......
?startBytesWritten?:=?pc.nwrite
?writeErrCh?:=?make(chan?error,?1)
????//writeLoop循環(huán)寫入
?pc.writech?<-?writeRequest{req,?writeErrCh,?continueCh}
????
????//?等待響應(yīng)數(shù)據(jù)
????resc?:=?make(chan?responseAndError)
????//?readLoop循環(huán)等待響應(yīng)
?pc.reqch?<-?requestAndChan{
??req:????????req.Request,
??cancelKey:??req.cancelKey,
??ch:?????????resc,
??addedGzip:??requestedGzip,
??continueCh:?continueCh,
??callerGone:?gone,
?}
????
?for?{
??select?{
??case?err?:=?<-writeErrCh:
??case?<-pcClosed:
???pcClosed?=?nil
???if?canceled?||?pc.t.replaceReqCanceler(req.cancelKey,?nil)?{
????if?debugRoundTrip?{
?????req.logf("closech?recv:?%T?%#v",?pc.closed,?pc.closed)
????}
????return?nil,?pc.mapRoundTripError(req,?startBytesWritten,?pc.closed)
???}
??case?<-respHeaderTimer:
???if?debugRoundTrip?{
????req.logf("timeout?waiting?for?response?headers.")
???}
???pc.close(errTimeout)
???return?nil,?errTimeout
??case?re?:=?<-resc:
???if?(re.res?==?nil)?==?(re.err?==?nil)?{
????panic(fmt.Sprintf("internal?error:?exactly?one?of?res?or?err?should?be?set;?nil=%v",?re.res?==?nil))
???}
???if?debugRoundTrip?{
????req.logf("resc?recv:?%p,?%T/%#v",?re.res,?re.err,?re.err)
???}
???if?re.err?!=?nil?{
????return?nil,?pc.mapRoundTripError(req,?startBytesWritten,?re.err)
???}
???return?re.res,?nil
??case?<-cancelChan:
???canceled?=?pc.t.cancelRequest(req.cancelKey,?errRequestCanceled)
???cancelChan?=?nil
??case?<-ctxDoneChan:
???canceled?=?pc.t.cancelRequest(req.cancelKey,?req.Context().Err())
???cancelChan?=?nil
???ctxDoneChan?=?nil
??}
?}
}

我們重點(diǎn)關(guān)注這兩個(gè)通道:

  • pc.writech :其類型是chan writeRequest ,writeLoop協(xié)程會(huì)循環(huán)寫入數(shù)據(jù),net/http.Request.write會(huì)根據(jù)net/http.Request結(jié)構(gòu)中的字段按照HTTP協(xié)議組成TCP數(shù)據(jù)段,TCP協(xié)議棧會(huì)負(fù)責(zé)將HTTP請(qǐng)求中的內(nèi)容發(fā)送到目標(biāo)服務(wù)器上;
  • pc.reqch:其類型是chan requestAndChan,readLoop協(xié)程會(huì)循環(huán)讀取響應(yīng)數(shù)據(jù)并且調(diào)用net/http.ReadResponse進(jìn)行協(xié)議解析,其中包含狀態(tài)碼、協(xié)議版本、請(qǐng)求頭等內(nèi)容;

小結(jié)

我們簡(jiǎn)單總結(jié)一下net/http庫中HTTP客戶端的實(shí)現(xiàn):

  • net/http.Client是級(jí)別最高的抽象,其中transport用于開啟HTTP事務(wù),jar用于處理cookie;
  • net/http.Transport中主要邏輯兩部分:
    • 從連接池中獲取持久化連接
    • 使用持久化連接處理HTTP請(qǐng)求

net/http庫中默認(rèn)有一個(gè)DefaultClient可以直接使用,DefaultClient有對(duì)應(yīng)DefaultTransport,可以滿足我們大多數(shù)場(chǎng)景,如果需要使用自己管理HTTP客戶端的頭域、重定向等策略,那么可以自定義Client,如果需要管理代理、TLS配置、連接池、壓縮等設(shè)置,可以自定義Transport;

因?yàn)镠TTP協(xié)議的版本是不斷變化的,所以為了可擴(kuò)展性,transport是一個(gè)接口類型,具體的是實(shí)現(xiàn)是Transporthttp2TransportfileTransport,這樣實(shí)現(xiàn)擴(kuò)展性變高,值得我們學(xué)習(xí);

HTTP在建立連接時(shí)會(huì)耗費(fèi)大量的資源,需要開辟一個(gè)goroutine去創(chuàng)建TCP連接,連接建立后會(huì)在創(chuàng)建兩個(gè)goroutine用于HTTP請(qǐng)求的寫入和響應(yīng)的解析,然后使用channel進(jìn)行通信,所以要合理利用連接池,避免大量的TCP連接的建立可以優(yōu)化性能;

服務(wù)端

我們可以用net/http庫快速搭建HTTP服務(wù),HTTP服務(wù)端主要包含兩部分:

  • 注冊(cè)處理器:net/http.HandleFunc函數(shù)用于注冊(cè)處理器
  • 監(jiān)聽端口:net/http.ListenAndServe用于處理請(qǐng)求

注冊(cè)處理器

直接調(diào)用net/http.HandleFunc可以注冊(cè)路由和處理函數(shù):

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

我們可以看到處理函數(shù)是一個(gè)統(tǒng)一的格式:

handler?func(ResponseWriter,?*Request)

默認(rèn)調(diào)用HTTP服務(wù)起的DefaultServeMux處理請(qǐng)求,DefaultServeMux本質(zhì)是ServeMux

type?ServeMux?struct?{
?mu????sync.RWMutex??//?讀寫鎖,保證并發(fā)安全,注冊(cè)處理器時(shí)會(huì)加寫鎖做保護(hù)
?m?????map[string]muxEntry?//?路由規(guī)則,一個(gè)string對(duì)應(yīng)一個(gè)mux實(shí)體,這里的string就是注冊(cè)的路由表達(dá)式
?es????[]muxEntry?//?slice?of?entries?sorted?from?longest?to?shortest.
?hosts?bool???????//?whether?any?patterns?contain?hostnames
}
  • mu:需要加讀寫鎖保證并發(fā)安全,注冊(cè)處理器時(shí)會(huì)加寫鎖保證寫map的數(shù)據(jù)正確性,這個(gè)map就是pattern和handler;
  • m:存儲(chǔ)路由規(guī)則,key就是pattern,value是muEntry實(shí)體,muEntry實(shí)體中包含:pattern和handler
  • es:存儲(chǔ)的也是muxEntry實(shí)體,因?yàn)槲覀兪褂胢ap存儲(chǔ)路由和handler的對(duì)應(yīng)關(guān)系,所以只能索引靜態(tài)路由,并不支持[path_param],所以這塊的作用是當(dāng)在map中沒有找到匹配的路由時(shí),會(huì)遍歷這個(gè)切片進(jìn)行前綴匹配,這個(gè)切片按照路由長度進(jìn)行排序;
  • hosts:這個(gè)也是用來應(yīng)對(duì)特殊case,如果我們注冊(cè)的路由沒有以/開始,那么就認(rèn)為我們注冊(cè)的路由包含host,所以路由匹配時(shí)需要加上host;

我看看一下路由注冊(cè)函數(shù):

func?(mux?*ServeMux)?Handle(pattern?string,?handler?Handler)?{
????//?加鎖,保證并發(fā)安全
?mux.mu.Lock()
?defer?mux.mu.Unlock()

?if?pattern?==?""?{
??panic("http:?invalid?pattern")
?}
?if?handler?==?nil?{
??panic("http:?nil?handler")
?}
?if?_,?exist?:=?mux.m[pattern];?exist?{
??panic("http:?multiple?registrations?for?"?+?pattern)
?}

?if?mux.m?==?nil?{
??mux.m?=?make(map[string]muxEntry)
?}
?e?:=?muxEntry{h:?handler,?pattern:?pattern}
????//?map存儲(chǔ)路由和處理函數(shù)的映射
?mux.m[pattern]?=?e
????//?如果路由最后加了`/`放入到切片后在路由匹配時(shí)做前綴匹配
?if?pattern[len(pattern)-1]?==?'/'?{
??mux.es?=?appendSorted(mux.es,?e)
?}
?//?如果路由第一位不是/,則認(rèn)為注冊(cè)的路由加上了host,所以在路由匹配時(shí)使用host+path進(jìn)行匹配;
?if?pattern[0]?!=?'/'?{
??mux.hosts?=?true
?}
}

監(jiān)聽端口

net/http庫提供了ListenAndServe()用來監(jiān)聽TCP連接并處理請(qǐng)求:

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

在這里初始化Server結(jié)構(gòu),然后調(diào)用ListenAndServe

func?(srv?*Server)?ListenAndServe()?error?{
?if?srv.shuttingDown()?{
??return?ErrServerClosed
?}
?addr?:=?srv.Addr
?if?addr?==?""?{
??addr?=?":http"
?}
????//?調(diào)用net進(jìn)行tcp連接
?ln,?err?:=?net.Listen("tcp",?addr)
?if?err?!=?nil?{
??return?err
?}
?return?srv.Serve(ln)
}

我們調(diào)用net網(wǎng)絡(luò)庫進(jìn)行tcp連接,這里包含了創(chuàng)建socket、bind綁定socket與地址,listen端口的操作,最后調(diào)用Serve方法循環(huán)等待客戶端的請(qǐng)求:

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

?origListener?:=?l
?l?=?&onceCloseListener{Listener:?l}
?defer?l.Close()

?if?err?:=?srv.setupHTTP2_Serve();?err?!=?nil?{
??return?err
?}

?if?!srv.trackListener(&l,?true)?{
??return?ErrServerClosed
?}
?defer?srv.trackListener(&l,?false)

?baseCtx?:=?context.Background()
?if?srv.BaseContext?!=?nil?{
??baseCtx?=?srv.BaseContext(origListener)
??if?baseCtx?==?nil?{
???panic("BaseContext?returned?a?nil?context")
??}
?}

?var?tempDelay?time.Duration?//?how?long?to?sleep?on?accept?failure

?ctx?:=?context.WithValue(baseCtx,?ServerContextKey,?srv)
?for?{
????????//?接收客戶端請(qǐng)求
??rw,?err?:=?l.Accept()
??if?err?!=?nil?{
???select?{
???case?<-srv.getDoneChan():
????return?ErrServerClosed
???default:
???}
????????????//?網(wǎng)絡(luò)錯(cuò)誤進(jìn)行延時(shí)等待
???if?ne,?ok?:=?err.(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",?err,?tempDelay)
????time.Sleep(tempDelay)
????continue
???}
???return?err
??}
??connCtx?:=?ctx
??if?cc?:=?srv.ConnContext;?cc?!=?nil?{
???connCtx?=?cc(connCtx,?rw)
???if?connCtx?==?nil?{
????panic("ConnContext?returned?nil")
???}
??}
??tempDelay?=?0
????????//?創(chuàng)建一個(gè)新的連接
??c?:=?srv.newConn(rw)
??c.setState(c.rwc,?StateNew,?runHooks)?//?before?Serve?can?return
????????//?讀取起一個(gè)goroutine處理客戶端請(qǐng)求
??go?c.serve(connCtx)
?}
}

從上述代碼我們可以到每個(gè)HTTP請(qǐng)求服務(wù)端都會(huì)單獨(dú)創(chuàng)建一個(gè)goroutine來處理請(qǐng)求,我們一下處理過程:

//?Serve?a?new?connection.
func?(c?*conn)?serve(ctx?context.Context)?{
?c.remoteAddr?=?c.rwc.RemoteAddr().String()
?ctx?=?context.WithValue(ctx,?LocalAddrContextKey,?c.rwc.LocalAddr())
?var?inFlightResponse?*response
?defer?func()?{
????????//?添加recover函數(shù)防止panic引發(fā)主程序掛掉;
??if?err?:=?recover();?err?!=?nil?&&?err?!=?ErrAbortHandler?{
???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)
??}
?}()


?//?HTTP/1.x?from?here?on.
?ctx,?cancelCtx?:=?context.WithCancel(ctx)
?c.cancelCtx?=?cancelCtx
?defer?cancelCtx()

?c.r?=?&connReader{conn:?c}
?c.bufr?=?newBufioReader(c.r)
?c.bufw?=?newBufioWriterSize(checkConnErrorWriter{c},?4<<10)

?for?{
????????//?讀取請(qǐng)求,從連接中獲取HTTP請(qǐng)求并構(gòu)建一個(gè)實(shí)現(xiàn)了`net/http.Conn.ResponseWriter`接口的變量`net/http.response`
??w,?err?:=?c.readRequest(ctx)
??if?c.r.remain?!=?c.server.initialReadLimitSize()?{
???c.setState(c.rwc,?StateActive,?runHooks)
??}
??if?err?!=?nil?{
??}
??//?處理請(qǐng)求
??serverHandler{c.server}.ServeHTTP(w,?w.req)
?}
}

我們繼續(xù)跟蹤ServeHTTP方法,ServeMux是一個(gè)HTTP請(qǐng)求的多路復(fù)用器,在這里可以根據(jù)請(qǐng)求的URL匹配合適的處理器,我們看代碼:

func?(mux?*ServeMux)?ServeHTTP(w?ResponseWriter,?r?*Request)?{
?if?r.RequestURI?==?"*"?{
??if?r.ProtoAtLeast(1,?1)?{
???w.Header().Set("Connection",?"close")
??}
??w.WriteHeader(StatusBadRequest)
??return
?}
????//?進(jìn)行路由匹配,獲取注冊(cè)的處理函數(shù)
?h,?_?:=?mux.Handler(r)
????//?這塊就是執(zhí)行我們注冊(cè)的handler,也就是例子中的getProfile()
?h.ServeHTTP(w,?r)
}

mux.Handler()中我們就看到了路由匹配的代碼:

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)?{
?//?先從map中查找
?v,?ok?:=?mux.m[path]
?if?ok?{
????????//?找打了返回注冊(cè)的函數(shù)
??return?v.h,?v.pattern
?}

?//?從切片中進(jìn)行前綴匹配
?for?_,?e?:=?range?mux.es?{
??if?strings.HasPrefix(path,?e.pattern)?{
???return?e.h,?e.pattern
??}
?}
?return?nil,?""
}

小結(jié)

服務(wù)端的代碼看主邏輯主要是看兩部分,一個(gè)是注冊(cè)處理器,標(biāo)準(zhǔn)庫使用map進(jìn)行存儲(chǔ),本質(zhì)是一個(gè)靜態(tài)索引,同時(shí)維護(hù)了一個(gè)切片,用來做前綴匹配,只要以/結(jié)尾的,都會(huì)在切片中存儲(chǔ);服務(wù)端監(jiān)聽端口本質(zhì)也是使用net網(wǎng)絡(luò)庫進(jìn)行TCP連接,然后監(jiān)聽對(duì)應(yīng)的TCP連接,每一個(gè)HTTP請(qǐng)求都會(huì)開一個(gè)goroutine去處理請(qǐng)求,所以如果有海量請(qǐng)求,會(huì)在一瞬間創(chuàng)建大量的goroutine,這個(gè)可能是一個(gè)性能瓶頸點(diǎn),所以小伙伴要注意下這塊的性能問題;

總結(jié)

net/HTTP的總體代碼行數(shù)是比較多的,我們只需要看主要邏輯是怎么實(shí)現(xiàn)的就可以了,別人問你原理能打出來個(gè)所以然就行,不必要扣細(xì)節(jié),當(dāng)出現(xiàn)問題或者想具體了解某部分協(xié)議的時(shí)候在細(xì)看源碼對(duì)應(yīng)部分即可;

像我們過了一遍源碼后我們就知道當(dāng)前net/http的一些優(yōu)缺點(diǎn),比如優(yōu)點(diǎn)是HTTP客戶端使用了連接池,避免頻繁建立帶來的大開銷,缺點(diǎn)是HTTP服務(wù)端的路由只是一個(gè)靜態(tài)索引匹配,對(duì)于動(dòng)態(tài)路由匹配支持的不好,并且每一個(gè)請(qǐng)求都會(huì)創(chuàng)建一個(gè)gouroutine進(jìn)行處理,海量請(qǐng)求到來時(shí)需要考慮這塊的性能瓶頸;

net/http標(biāo)準(zhǔn)庫可以讓我們很快的就實(shí)現(xiàn)一個(gè)HTTP服務(wù)器,并且也有很多我們值得借鑒學(xué)習(xí)的地方,所以源碼的學(xué)習(xí)還是很必要的;

好啦,今天的文章就到這里了,我是asong,我們下期見~


評(píng)論
  • ?摘要:低價(jià)大流量卡正全面退市,政策收緊已成定局。面對(duì)傳統(tǒng)號(hào)卡的不確定性,隨身WiFi憑借穩(wěn)定連接、多設(shè)備共享、獨(dú)立設(shè)備和靈活套餐等優(yōu)勢(shì),成為務(wù)實(shí)可靠的備選方案。1、流量卡黃金時(shí)代要結(jié)束了通信行業(yè)正迎來一場(chǎng)前所未有的“地震式”調(diào)整——曾讓無數(shù)用戶追捧的低價(jià)大流量卡,正在全速退出各大平臺(tái)。這早已不是一次普通的競(jìng)合,而是一場(chǎng)趨勢(shì)性的行業(yè)洗牌。回望過去,19元200G的神卡,那時(shí)真是“睡著都能笑醒”,便宜到不敢相信,爽到忍不住炫耀。那些日子,好像隨便刷、隨便看,流量自由觸手可及。可如今,回頭再看,那種
    愛上電路設(shè)計(jì) 2026-08-03 10:21 128877瀏覽
  • 在EMC測(cè)試過程中,傳導(dǎo)發(fā)射(Conducted Emission,CE)超標(biāo)是工程師最常遇到的問題之一。更令人頭疼的是,同樣的超標(biāo)曲線背后,可能對(duì)應(yīng)著完全不同的干擾機(jī)理:有的是差模噪聲沿電源回路傳播,有的是共模電流通過寄生路徑耦合,還有些問題甚至并非來自電源本身,而是負(fù)載、電纜布局或接地結(jié)構(gòu)引起的連鎖反應(yīng)。 · 150kHz~1MHz 超標(biāo):多為差模噪聲為主,干擾沿火線-零線回路傳輸。 · 1MHz~30MHz 超標(biāo):通常共模噪聲占主導(dǎo),干擾通過火線/零線對(duì)地回路傳播。 如果條件允許,用電流探
    譽(yù)磁電磁兼容EMC 2026-07-31 18:45 505瀏覽
  • 摘要在防爆電氣設(shè)計(jì)中,只參照芯數(shù)選型而忽略額定載流量,長期滿負(fù)荷運(yùn)行會(huì)出現(xiàn)插頭發(fā)熱、接觸面氧化、IP67 密封受熱老化等問題。結(jié)合常用 3 針 3 孔、4 芯、5PIN、8PIN 圓形防爆連接器,制作分級(jí)電流參數(shù)對(duì)照表,區(qū)分冷壓免焊接、焊接兩種接線結(jié)構(gòu)的安全載流數(shù)值,設(shè)置合理電流冗余系數(shù),搭配使用環(huán)境修正參數(shù),規(guī)范防爆接近開關(guān)連接器、防爆法蘭插座的選型計(jì)算方式。關(guān)鍵詞圓形防爆連接器,載流參數(shù)表,防爆法蘭插座,防爆端子接頭,IP67 防爆防水接插件1 產(chǎn)品概述圓形防爆連接器觸點(diǎn)載流能力由插針直徑、
    kyfbest科迎法電氣 2026-08-03 09:18 128644瀏覽
  • 產(chǎn)品使用18V供電,配套4串鋰離子電池,需要設(shè)計(jì)一個(gè)充電器。因?yàn)檩斎?8V,電池最高 16.8V,壓差僅 1.2V。因?yàn)閴翰钐 J褂瞄_關(guān)型的充電控制芯片不穩(wěn)定。嘗試自行設(shè)計(jì)一個(gè)。整體采用分立三極管 + TL431架構(gòu),分4個(gè)功能單元:使能控制回路、限流環(huán)路、TL431 精密恒壓反饋環(huán)路、PNP 功率管主功率輸出回路;實(shí)現(xiàn)完整鋰電池充電邏輯。使能控制回路按鍵按下:5V 電壓經(jīng) R12 (2kΩ) 送入 NPN 管 Q3 基極,Q3 飽和導(dǎo)通,對(duì)地拉低 R1、R2 組成的分壓控制節(jié)點(diǎn)電位;該節(jié)點(diǎn)直
    southcreek 2026-08-07 13:45 328瀏覽
  • 在物聯(lián)網(wǎng)從“萬物互聯(lián)”邁向“萬物智聯(lián)”的進(jìn)程中,LTE Cat.1憑借其在速率、成本與網(wǎng)絡(luò)覆蓋之間的完美平衡,正成為中低速物聯(lián)網(wǎng)市場(chǎng)的絕對(duì)主力。隨著2G/3G網(wǎng)絡(luò)的逐步退網(wǎng)以及新國標(biāo)對(duì)終端設(shè)備智能化、定位功能的強(qiáng)制要求,Cat.1模組迎來了爆發(fā)式的增長窗口。在這一關(guān)鍵節(jié)點(diǎn),基于華為海思Hi2131芯片設(shè)計(jì)的LTE Cat.1模組,憑借其在能效、射頻、架構(gòu)及物理規(guī)格上的底層創(chuàng)新,成功打破了“高性能必高功耗”的傳統(tǒng)魔咒,為行業(yè)提供了極具競(jìng)爭(zhēng)力的連接最優(yōu)解。核心技術(shù)優(yōu)勢(shì):重塑Cat.1能效與連接標(biāo)桿海
    用戶1778981989717 2026-08-05 21:04 169瀏覽
  • 在前六期中,我們已經(jīng)分別討論了 CRA 的適用范圍、整體影響、漏洞通報(bào)義務(wù)、風(fēng)險(xiǎn)評(píng)估與技術(shù)文檔證據(jù)鏈、第三方組件與 SBOM 供應(yīng)鏈治理,以及產(chǎn)品上市后的支持期、安全更新、用戶說明和終止支持管理。到這里,企業(yè)已經(jīng)能夠回答幾個(gè)核心問題:產(chǎn)品是否落入 CRA 適用范圍,產(chǎn)品由哪些組件構(gòu)成,企業(yè)如何證明自己在設(shè)計(jì)、開發(fā)和上市前采取了必要的安全措施,以及產(chǎn)品上市后如何持續(xù)維護(hù)和安全下線。但對(duì)于真正準(zhǔn)備落地 CRA 合規(guī)的企業(yè)來說,還有一個(gè)更現(xiàn)實(shí)的問題:CRA 的法規(guī)條文相對(duì)原則化,企業(yè)應(yīng)當(dāng)如何把“網(wǎng)絡(luò)安
    虹科云科技 2026-08-03 16:51 159573瀏覽
  •        很多人只見過封裝在SMD外殼里的小尺寸晶振,卻不知道一顆2016、1612甚至1210封裝的微型石英晶振,從一塊巴掌大的石英原礦,到最終能穩(wěn)定起振的成品,要經(jīng)過十幾道精密工序。它內(nèi)部的石英晶片厚度最薄不到20微米,比普通A4紙的1/5還薄,整個(gè)生產(chǎn)過程里哪怕0.1微米的誤差,都會(huì)直接廢掉整批產(chǎn)品,這也是小尺寸晶振過去長期依賴進(jìn)口,國內(nèi)廠商花了十幾年才啃下量產(chǎn)硬骨頭的核心原因。1. 原石定向切割:0.01度的角度誤差都不能有小尺寸晶振的第一步,
    TKD泰晶科技 2026-08-07 08:43 382瀏覽
  • 做藍(lán)牙音箱的工程師大概都碰過這個(gè)局面。產(chǎn)品要連手機(jī)放歌,要本地播開機(jī)提示音和按鍵音,還要接 App 做配網(wǎng)和狀態(tài)上報(bào)。三件事拆開,早先的常規(guī)做法是音頻藍(lán)牙一顆料、數(shù)傳藍(lán)牙一顆料、本地播放再掛一顆小 MCU 加一片 Flash。板子越畫越大,BOM 越算越貴,待機(jī)電流和固件工作量也跟著漲。WT2605C 出來以后,這套需求可以集成到一顆芯片,就是 WT2605C-32N,該芯片采用QFN32 封裝,4×4 毫米,把音頻藍(lán)牙、BLE 數(shù)傳和 MP3 本地播放三樣全包了。對(duì)藍(lán)牙音箱來說,最實(shí)在的好處就
    唯創(chuàng)知音語音芯片 2026-08-06 15:14 287瀏覽
  • 文:杜杰編輯:侯煜當(dāng)?shù)貢r(shí)間7月28日,美國特朗普政府落地最新科技封鎖政策,美國聯(lián)邦通信委員會(huì)(FCC)正式更新《受管制清單》,新增兩大類設(shè)備——境外生產(chǎn)的先進(jìn)機(jī)器人設(shè)備、境外生產(chǎn)的聯(lián)網(wǎng)電力逆變器。FCC在官方情況說明書中明確,該類境外制造產(chǎn)品,“會(huì)對(duì)美國國家安全或美國民眾的人身安全構(gòu)成無法承受的風(fēng)險(xiǎn)”。本次新規(guī)設(shè)置明確豁免機(jī)制:經(jīng)美國國防部(DoW)、電力逆變器品類經(jīng)美國國土安全部(DHS)審核并授予“有條件批準(zhǔn)”,認(rèn)定對(duì)應(yīng)設(shè)備品類不存在重大安全風(fēng)險(xiǎn),即可獲得準(zhǔn)入豁免。同時(shí)政策嚴(yán)格執(zhí)行新老劃斷原
    華爾街科技眼 2026-08-03 21:28 325瀏覽
  •                                                                        
    廣州鐵金剛 2026-08-06 17:44 110640瀏覽
  • 導(dǎo)語在現(xiàn)代高速數(shù)字電路設(shè)計(jì)中,信號(hào)的完整性已不僅是理想波形推演,而是一場(chǎng)與物理非理想特性正面博弈的工程實(shí)踐。濾波器芯片作為凈化傳輸鏈路的關(guān)鍵屏障,其核心挑戰(zhàn)在于:能否有效識(shí)別并濾除那些隱匿在電平跳變過程中的亞納秒級(jí)瞬態(tài)脈沖干擾,這類干擾持續(xù)極短,卻足以在高速鏈路中引發(fā)誤觸發(fā)。然而,傳統(tǒng)函數(shù)發(fā)生器受限于采樣率與邊沿速率,根本無法忠實(shí)復(fù)現(xiàn)如此狹窄且陡峭的異常波形。針對(duì)此困境,德思特TS-AWG7000系列任意波形發(fā)生器以17 GS/s采樣率與50 ps原生邊沿速度,為瞬態(tài)脈沖提供精細(xì)刻畫,配合SPG
    德思特測(cè)試測(cè)量 2026-08-05 14:06 5瀏覽
我要評(píng)論
0
點(diǎn)擊右上角,分享到朋友圈 我知道啦
請(qǐng)使用瀏覽器分享功能 我知道啦
主站蜘蛛池模板: 铠甲勇士之帝皇侠电影| 我邻居的老婆 电影| 年经的继坶3| 日本语徐娘半老电影有哪些| 台剧浪漫女家教免费观看| 无心法师第4季免费播放| 高清《虎啸龙吟》电视剧| 需要爸爸播种电影| 急速绯闻| 公之浮中字9的中文名| 上位完整版在线观看| 《爱你几何》免费观看| 明日战记在线观看免费完整版中文| 孤岛飞鹰 电视剧| 闪婚爱豆:顶流老公短剧全集| 正在播放:黑人巨屌 Karla Kush 金发美女对战大黑鸡巴 - 17 | 韩国 艳舞| 渔夫荒野史记1988版| 宁安如梦电视剧38集全| 蓝精灵国语| 《被丈夫的下属推来推去的妻子》HD中字-完整版全集在线观看-电影[1]-4m影院 | 人之怒在线观看完整免费| 指环王2快播| 金牌女销售3| 圣斗士星矢国语全集| 神机妙算刘伯温第七单元| 桃色激情| 《女律师替夫还债》剧情介绍曹查理| 牛仔裤的夏天1| 公之手中字5HD| 火舞风云电影粤语版| 变成黑皮后跟朋友无增删| 韩国电视剧漂亮女孩| 玉璞团之鉴宝2高清| 次子(打一字)| 美女在野外脱个精光| 《香港奇案之吸血贵利王》电影| 亲爱的孩子们演员表| 女超人韩国在线观看电影| 樱花动漫免费看妖神记第五季| 《韩国财阀》免费播放| 茶啊二中 电影| 私袜会所电影| 天堂执法者15| 开膛手杰克 电影| 狙击精英重装上阵| 《拳击手》满天星1997罗莎| 终极格斗4完整版在线观看| 永远的母亲| 男女在一起愁愁愁视频电视剧免费观看全集 | 残骸电影免费观看完整版| 卖房子的女销售2.| honeygirl| 老农民免费观看全集| 需要爸爸的种子伦理在线播放 | 韩国《色诱女教师》| 乡村爱情小夜曲第6部| 巡回检察组43全集在线观看星辰 | 忒弥斯的不确定法庭| 孩奴全集在线观看40| 县委大院电视剧免费播放| 孤独一掷免费完整观看高清2023| 新金瓶梅第2集| 人猿泰山1995免费观看完整版| 血战上海滩电影| 飞屋环游记电影免费版观看| 疯狂72小时| 金瓶梅电视剧第5集电视剧| 高清《如月车站Re:》电影| 学生酒店1-580集| 年轻的嫂子1韩版带中译| 电视剧与狼共舞2| 花与蛇2《牙医诊所》 | 末世超级系动漫免费观看完整版| 蛇姬恋没删减| 《成何体统》| 交换秘书| 韩国电影出轨| 女保镖满天星在线播放| 世界奇妙物语2012| 电视剧捍卫者在线观看| 兰陵王电视剧| 一段难一段难以启齿的感情| 余罪第一季免费观看全集| 三年高清免费观看在线播放| 因为爱情有幸福之一电视剧| 天赋异禀在线观看免费| 麦子交换| 龙门镖局2电视剧全集| 越南停播中国电视剧| 牙医姊妹电影在线| 欢天喜地七仙女38集| 荣誉守则成人在线观看| 黄健翔疯狂解说| 埋在体内吃饭h| 女版《壮志凌云》杰西 | 王力宏什么歌好听| 水灵的继拇BD中字3 | 月上重火免费观看全集完整版高清 | 西游记恶搞配音| 蓝精灵2| 网红逆袭全集免费| 瓦尼塔斯的笔记第一季免费观看| 美国大选首场辩论| 欢乐喜剧人下载| 妻子的姐姐2免费版电视剧大全| 完美搭配2韩版| 欲望酒店 百度影音| 奇兵电影| 被侵犯的美女教师天海翼| 不完美的受害者电视剧| 韩国电影表妹2现在改免费| 神鬼战士1满天星版| 《仙剑奇侠传三》电视剧| 穿越时空的爱恋免费观看完整版| 木下凛凛子AVHD101高清在线 | 《怪盗女谍》天海翼| 我们十七岁| 反击国产电视剧全集 | 吴昕李维嘉同框| 老师丝袜| xl司令第一季全集在线观看免费播放高清| 电锯人剧场版蕾塞篇| 混凝土市场全集观看| 女销售员2的销售秘密| 变形金刚2女机器人| 喜羊羊与灰太狼古古怪界大作战| 西环浮尸电影免费观看完整版| 牙医姐妹电影完整版在线看| 女婿回乡全集免费看| 城市的边缘| 狙击手拉苏尔电影免费观看完整版 | 蓝色之恋国语电视剧免费观看| 熊出没·伴我“熊芯”免费 | 雪中悍刀行20集在线观看| 雯雅婷2| 少女农场| 特种部队2高清| 擦皮鞋dj洋洋歌词| 新人类之毒医肥尸| 布拉格之恋下载| 美丽小蜜桃五| 斗罗大陆263集免费观看全集| 亢奋第二季未删减| 西游记续集16集免费观看| 广场舞开着车去草原| 迈开腿让尝尝你的CH宝宝| 小白船舞蹈| Lovefou| 赛德克巴莱完整版电影| 霜花店无删减版| 82板杨敏思版免费版电影| 电影植物学家的女儿| 西安大追捕20集免费观看| 刚结婚陪部长出差电影| 《无名之辈:否极泰来》| 沉香如屑电视剧结局| 24世纪性格爱情指南大结局| 天伦电视剧第40集观看| 快17电影网| 歼灭天际线日韩版最新消息| 悄悄地喜欢你免费观看| 入室暴行3 被揉| 我在你的未来吗免费观看完整版| 学生妈妈5字ID免费各种各样的| 兰桐花开电视剧| 年轻的嫂子2免费观看| 高压监狱2免费播放完整版 | 男女生一起愁愁愁电视剧在线观看| 刀郎新歌翩翩| 落魄贵族琉璃川在线观看免费动漫 | 年轻的妻子在线| 蜜桃成熟时3下载| 男女在一起愁愁愁免费观看全集 | 福尔摩斯第二季| 兽性之夜| 美国娃娃脸经典版下载 | 海贼王480| 姐妹牙医电影在线完整版观看中文| 牧神记第62集在线观看| 和台湾这次打得起来吗| 喜爱夜蒲 2| 电影种马猎场在线观看| 且试天下第45集全集免费| 灿烂人生国语版| 圣特罗佩姑娘们1982电影在线观看 | 巡视检查组| 近距离爱上你电视剧全集在线观看 | 《妻子的背叛》全集观看| 妈祖在线观看免费全集| 倔强的花子与驱魔师| 高压监狱3完整版在线看| 浪漫女家教台湾剧情版| 柳舟记张晚意电视剧免费观看 | 战狼6免费国语版高清| 中国交通频道| 日本大胸继拇| 新警察故事2013| 加勒比女海盗| 私じゃなかったんだね谐音| 2对1:三人一次性体检1韩剧| 听说你喜欢我免费观看完整版| 黑执事第三季全集| 钢铁铿锵免费观看| 春潮烂漫海棠红下载| 高压监狱完整版在线观看完整免费高清原声满天星感恩 y | 《时间静止》完整版| 一起来看流星雨电视剧免费全集| 神机妙算刘伯温第四单元| 三年中国免费观看完整版| 危机航线免费观看2024| 剥茧电视剧免费全集在线观看| 大决战电视剧| 美国一级黄色电影女儿需要爸爸播种子生孩子| 激情小说电影网站| se123| 八尺夫人满天星1978年版| 雪迷宫40集电视连续剧| 名侦探柯南绀青之拳免费观看| 希望爸爸播种普通话版| 芈月传电视剧全集在线观看| 赌圣之上海滩粤语| 高清《法国空乘2019》播放| 日本与部长出差住宿电影| 爱我几何,免费| 湄公河行动| 韩剧《我的游泳女教练》免费观看| 爱人韩剧免费观看电视剧| 伤痛的不止是一个| 男生女生一起愁愁愁电视剧在线| 萌学园之萌骑士传奇全集| 摇滚狂花电视剧免费观看| 善良的妻子2| 善良的妈妈中字开头9个字是什么| 元旦节祝福老师的话| 文轩3800汤臣一品探花| 激战丛林手机在线观看| 高清《月老》电影完整剧情| 越狱兔第5季| 介休市| 日剧我们再来一次高清观看| 狙击手电影免费观看完整版| 使徒行者3在线看免费| 本乡爱,二阶堂| 圣诞节的英文单词| 听不见的爱短剧免费观看| 战狼6女版免费观看高清版 | 野鸽子电视剧全集30| 小猪佩奇最新季全集| 无职转生 第二季| 《辞职欢送温泉之旅》| 我独自升级第二季免费完整版| 鬼吹灯湘西怒晴| 《越南俘虏营》在线观看| 重庆娱乐频道直播| 黑白诀电视剧免费观看完整版高清| 《战国》| 高压监狱2019| 强子哥哥| 理想之城全集| 《满天星》陈宝莲| 今生最爱| 穿靴子的猫第二季国语版| 真假公主| 我的寡妇房东| 电视剧问心免费观看| 《无限城决战篇》免费观看| 麦乐迪在线观看| 高清街头顽童| 天魔异种| 黑皮辣妹是我的兄弟第一季免费 | 猎战狂徒电影| 温暖的村庄电视剧全集在线观看| 孤岛惊魂2修改器| 壮志凌云女版成人H版免费观看 | 婚姻时差电视剧全集| 妻子8免费全集高清观看在线| 邵氏电影邪无删减在线观看| 《杀死比尔2》电影| 小鲤鱼跳龙门免费| 高压监狱完整版在线观看完整免费高清原声满天星2023 | 樱兰高校的男公关部| 壮志凌云啄木鸟满天星法版| 大江大河电视剧免费观看全集完整版 | 《爱我几何》原版免费观看| 杨思敏国语版最经典的一句| 贞观长歌高清| 聚会的目的1在观完整有限中字| 3个大学生的特殊按摩| 警方回应山东父子三人落水溺亡| 年轻保姆1韩国| 卫辉市| 男女愁愁愁全集哪里看| 邱淑贞三圾片大全| 霍比特人1意外之旅| 银魂123| 三更车库在线观看| 活死人军团| 女兵排电视剧全集40| 猎罪图鉴| 引爆流行| 宁安如梦电视剧免费观看| 燃罪电视剧在线观看全集免费播放| 电视剧后宫甄嬛传| 司法| 上课时胸罩不小心掉下来的事| 刘嘉玲被绑照片| 劲爆的士高| 胭脂霸王演员表| 我的波塞冬| 天使与龙的轮舞 在线观看| 电视剧西施秘史全集| 法版女超人电影在线观看| 广场舞洗衣歌| 黄圣依乳晕| 天赐的声音第五季| 1980悲惨遭遇免费看| 午夜凶铃完整版| 和讨厌的前男友一起去出差旅| 数码暴龙| 后海前街| 电影《青楼李师师》免费观看| 结婚吗好的电视剧| 丰满的女同学3| 罪恶斗争电影在线播放| 强制绝顶绝叫拷问调教| 三个少妇按摩记在线播放| 薄冰电视剧免费观看| 小小许无忧| 告诉我你想要什么电影在线观看免费| 东宫电视剧免费观看全集剧情| 盛唐风流电影完整版播放时间| 封神演义评书| 绝命凯特| 女超人麦乐迪版在线观看| 封神英雄榜第二部1| 天美影院在线观看电视剧| 天龙八部24| 大内密探零零性| 《战争之暴行女囚》电影电影| 进口媳妇在线观看免费西瓜| 哇嘎电影高清完整视频| 龙年档案电视剧26集免费观看| 律师大人巴布印度电视剧在线观看 | 亲爱的热爱的第二季免费全集观看| 年轻的女按摩师| 年青护土3| 爱情真善美全集80| 知否连续剧全集免费观看| 光溜溜的荒野求生| 韩剧big| 牧教师在线观看| 年轻老师2| 琉璃免费观看电视剧全集在线观看| 动感妖精| 妖精的尾巴51| 非常完美全集免费观看| 真咲惠美| 犯罪心理第十八季| 跟部长出差的温泉之旅在线观看 | 西班牙情事| 吗吗朋友33| 激战丛林在线观看洛克帝希佛| 奇遇 电影| 《我和班主任的故事》剧本| 莫莉3特别酒店免费| 折腰短剧全集免费观看完整版| 少年派2正版全集免费观看| 庙街故事国语| 69XXXX美国| 斗罗在线观看免费完整版高清 | 五等分的新娘特别篇| 鉴证实录| 破冰行动免费完整版| 阿朱无删减版HD免费观看 | 千年情人| 小人物在线观看| 高压监狱2线观看完整版免费| 各国好声音| 烂滚夫斗烂滚妻国语| 插曲的痛30集全集高清下载| 《倔强驱魔师》免费观看| 闺蜜在线观看| 给你我的独家宠爱电视剧在线观看| 爱唯侦察dvd| 一千滴泪| 千金丫鬟电视剧在线观看完整版| 《夫妻的世界》电视剧| 姐妹牙医免费完整版观看| 电影《误杀3》免费观看| 日下美人| 拳皇电影中文版| 《斗罗大陆》.免费观看| 黑豹迅雷下载| 齐天大剩| 艳情电影邻居的阿姨| 爱人欲望| 蝙蝠侠:黑暗骑士免费| 《混血的摇篮曲》日本 | 公浮之手中2完整版免费播放| 今生最爱| 圣德罗佩斯的姑娘们在线播放| 百家讲坛光绪| 法国电影《农场保卫战》在线观看| 滴血玫瑰演员表| 我和丰满瑜伽老师| 哎呀我愿意全集| 美国版《灭火宝贝1》免费观看在线中| 成一家言| 四个少夫按摩精油伦理片| 十九岁韩国免费观看动漫| 战狼12欧式少女全部视频| 他带我去没人的地方吃我豆腐| 结婚吗好的电视剧| 妈妈朋友| 美女一脱到底| 1dldss00361当我打电话叫应召女郎时,我发现了一个高傲的大胸秘书......在办公 | 狐狸的夏天百度云| 我怎么哭了| 世界第一的初恋第一季| 双人调情按摩| 伤离别 张学友| 丁度电影| 恋爱先生大结局| 韩国维修工的艳遇在线观看| 野店百度影音| 木下凛凛经典电影回老家三天高清| 枪口电视剧| 入室暴行3被蹂躏在线观看| 疾速营救| 女房东的秘密 韩国电影在线观看| 黄金未来5年还会到500以下吗| 荣誉守则(成人版)在线| 巳怎么读| 《法国空乘8》完整版| 中国刑警之九月风暴电视剧| 酒店1-40集在线观看| 上海女子图鉴在线观看免费完整版 | 对不起我爱你国语版全集| 需要爸爸播种| 在线播放国风按摩院 春宵一克值千金 第1集 - 高清资源 - 大鸟视频 | 斗罗大陆163集在线观看完整版| 晨曦中的女孩| 点解阿sir| 盛唐风流电影完整版| 黑莲花攻略手册全集免费观看| 撒旦有几个女儿| 校园时代电视剧在线观看| 高清《于成龙》电视剧| 圆桌骑士rom| 寂寞护士公与媳| 壮志凌云女版(H)| 高清《蒙特利尔,我的美人》电影| 剪刀手爱德华| 战狼8欧式少女全部 | 美丽小蜜桃5大结局| 比基尼勇士| 色九月| 斗罗大陆193集免费完整观看| 企业宣传短片| 恋爱结婚国语| 国乒提前包揽混双冠亚军| 激战丛林美国未删减完整版在线观看 | 电视剧生死相依| 梦幻诛仙电影| 东北往事之黑道风云20年15集| 法官李汉英全集观看| 玉蒲团3完整版高清| 高清《妻子的背叛》全集观看| 《房产销售员》在线| 卑触家的规矩全集免费观看 | 特殊交易| 木子檀檀子剧免费观看第10集| 变身小姐 豆瓣| 姚明场边大喊:不要犯规| 坏中尉 下载| 《伊波拉病毒》普通话| 何以笙箫默电视剧| 在线观看电视剧| 荼靡电视剧| 《丰胸沙龙治疗3》HD无字高清完整版| 再逢明月照九州| 永生守卫2妹妹归来| 哇嘎电影在线观看入口| 《寒武纪》| 美景之屋6:我的完美人生全集| 画江湖之不良人电视剧| 老婆招待丈夫朋友| 《忠者无敌》电视剧| 荒野女战士HD版在线播放| 大闹天宫电影免费观看| 猛虎威龙| 偷窥无罪2诱人犯罪| 和部长出差的日子电影在线观看| 超人钢铁之躯qvod| 仙逆动漫全集在线观看完整版高清| 偶滴神啊| 满天星1980在线观看| 语义错误电影版| 立功电视剧| 《义子侵犯》松下纱荣子| 梁以沫冷夜沉免费阅读| 坎贝奇品味人生无憾在线观看全集 | 陆判仙根全集免费观看| 电视剧婆婆来了大结局| xl司令第一季免费全集在线观看 | 《开端》| 人猿泰山电影1995版在线播放 | 电影斯巴达克斯第四季| 村上沙兽皇免费版在线观看| 夫妻的春天电视剧| 金瓶梅电影完整版在线观看| 女超人麦乐迪无删减版百度网盘资源 | 高度潜力美剧| 法国空姐4终极版免费完整| 日本混血大学生精油按摩电影在线观看 | 智取威虎山电影| 姚明场边大喊:不要犯规| 兄嫂动漫在线观看第一集免费| 电影禁忌3大结局| 小青扮演者陈美琪| 盗墓笔记在线观看| 终末的女武神第三季在线播放 | 足称老婆八两夫| 李菁飙车| 监狱风云2 电影| 中国玉米市场网| 古惑仔电影1-6全集| 被可怕的科长欺负的员工电影名字是| 白峰美羽 magnet| 农业部长| 毒爱泰剧| 1986版乌龙山剿匪记免费观看 | 疯狂的石头电影| 天空之城| 以法之名电视剧全集| 娃娃脸3在线观看完整免费高清原声满天星百 | 傲世九重天主题曲| 荷尔蒙少女3高清完整版| 隔壁邻居很美味免费版| 影帝合约:变真爱短剧全集| 青柠在线电视剧免费观看第二季| 垂帘听政电影| 真人版古墓丽影| 初级瑜伽视频2| 行尸走肉1| 双胞胎男主共享一女主小说宠文| 礼物电影完整版在线看| 玛丽玛丽| 铁核桃之无间风云剧情介绍| 塞罗奥特曼电影版| 敢死队成女版满天星| 高清《希望的大地》电视剧| 广场舞阿哥阿妹背面| 时间静止器全集免费观看第二集| 22集电视剧女特警| 了不起的盖茨比 快播| 《电影《千金与佣人》》| 心动瞬间| 背叛爱情| 旺达卢佩的玫瑰| 老舅1-40集完整版免费看| 龙帅归来:都市再掀风云全集免费| 黑衣人高清下载| 插曲的痛30集全集| 迷人的岳母在线| 甲铁城的卡巴内瑞第一季在线观看| 继母的拖油瓶是我的前女友| 冰湖重生全集观看| 《女超人麦乐迪满天星版》| 八月的棒球甜心动漫免费观看| 罗丽星克莱尔迷宫片长| 祈今朝 电视剧| 插曲的痛30集全集免费播放完整剧情| 丹麦vs塞尔维亚| 黄健翔疯狂解说| 女超人(麦乐迪)版迅雷| 双胞胎男主共享一女主小说宠文| 白川三姐妹| 单身即地狱综艺在线观看| 雪豹百度影音| 美貌的社长夫人| 侏罗纪世界3免费观看完整版 | 女孩1小时接10个客| 罪恶王冠my dearest| 暗杀教室真人版| 暴力谎言| 八百壮士在线观看| 《《替夫还债3》HD中字》电影-高清完整版在线观看-JJ影院 | 多情小屋| 来了!张宝利| 不雅空乘电影完整版免费观看| 善良的的瘦子| 勇士的奇遇| 年轻的母亲5电影| 圣特罗佩斯的姑娘| 战勇第二季| 《利刃出鞘3》| shoushoumen| 水润女人全免费高清电视剧| 火影忍者485集| 艾曼妞3| 大侦探11| 龙年图片壁纸| 女超人满天星版| 念无双动漫免费观看完整版| 月光宝盒私人免费影院| 欲望的火花电视剧| 漂亮的妈妈中字开头7个字| 假面骑士000全集| 樱桃嘴与大鼻子| 龙帅归来全集免费| 黑白配在线观看免费观看高清版 | 躁动的荷尔蒙3| 地狱宝贝| 哆啦a梦伴我同行| 战狼六俄罗斯美女| 国家要案| 风起陇西免费观看完整版| 乌龙元首| 疯狂外星人免费观看完整版国语版| 台湾电影村姑的故事免费观看| 在异世界迷宫开后宫樱花无修版| 绝世红颜短剧全集免费观看| 皮囊之下在线观看| 花样姐姐中国版| 哇嘎美国| 伦敦战场118分| 兰若寺电影| 土耳其惊艳的MV短片| 仓井老师的电影| 三位大学位按摩记在线观看 | 大染房电视免费观看| 黑白配视频免费看| 桑拿房的秘密三级国产在线| 建党伟业之践行者| 致命弯道3在线观看| 公司新来的女员工| 人狗大战8集全集观看免费西瓜 | 民国枭雄| 瘦身男女粤语| 社长的能力能干秘书| 双人调情按摩术韩国在线观看| 用身体偿还给社长的妻子电影| 晚娘百度影音完整版| 《美国式禁忌3》中文| 媳妇的秘密| 说爱你蔡依林| 隐形帽子电影免费| 闪婚顶流短剧全集| 都匀市| 环太平洋2| 地铁迷情| 千金与佣人日剧电影免费观看| 香瓜七兄弟全集爱奇艺| 蜂鸟电视剧| 与讨厌的部长一起去出差| 无人区在线完整免费| 浴火危城电视剧| 3v1三人一次性体检韩国电影| 狙击蝴蝶电视剧全集| 宝葫芦的秘密免费| 战狼6观看在线下载高清电影| 电视剧《唐太宗李世民》| 高清《黎明之前》| 大水井风云| 于和伟最新剧《城中之城》| 韩国电影需要爸爸播种子| 我在80年代撩首富短剧| 不伦纯爱| 假面骑士01| 五十度灰ed2k| 荒岛女儿国满天星版| 需要爸爸播种子类似电影 | 你们一个一个来我好痛| 面朝大海春暖花开赏析| 酒泉市| 法国空姐第4部完整版在线播放 | 归路在线观看免费完整版高清| 坎贝奇《无憾》免费下载| 祝英台与梁山伯的电视剧| 河南男子与猪结婚引争议 | 巨蟒之灾2| 大胆妹妹| 谢霆锋演唱会2012| 插曲的痛10集全集免费播放电影| 香港金瓶梅| 疯狂瑜伽电影完整版| 爱我就别想太多在线看| 美国电影《需要爸爸播种2》在线观看 | 粤语版青花瓷| 《女销售员的销售秘密》| 难哄第21集免费观看| 刑警队长 电视剧| 420电影网| 星辰变后传2| 乡村三姐妹保姆医药| 《特殊客房》| 罗曼蒂克消亡史免费观看| 勇敢传说电影| 女律师替丈夫还钱电影免费观看 | 借种完整版电影在线观看| 浴火之路免费观看策驰影院| 电影《美味快递员2》演员阵容 | 哪吒传奇动画片免费观看完整版| overflower高清无马| 韩国三姐妹| 我叫李焕英电影| 正在播放: JUL-945 在没有丈夫的平日里,我被打工归来的无与伦比的岳父强奸, | 原振侠全集| 黛比浪漫女家教BD播放| 城中之城电视剧在线播放免费观看| 新包青天之碧血丹心| 壮志凌云法国版完整版| 上海申花VS川崎前锋| 宇宙战舰大和号复活| 一代宗师高清| 瓜达卢佩的玫瑰(原版)在线播放| 风流名妓| 三合人才市场| 战狼9免费高清版999| 小小水蜜桃6[电视剧]在线观看| 悬崖电视剧免费观看全集在线观看| 谁的青春不迷茫免费| 维修人员的艳遇| 斗罗大陆110集| 碧海雄心| 苍月奥特曼战败| 《Wan etd 西部通緝令》| 你是我的眼mv| 时光沙漏在线观看免费完整版| 越策越开心2013| 正在播放: JUQ-101 在没有我丈夫的五天里,我被命令禁欲到第一个晚上。不想要 | 狂飙电视剧国产全集免费播放| 狼群资源在线高清免费观看| 进击的巨人第四季14集| 墨西哥电视剧《la rosa de guadalupe》(瓜达卢佩的玫瑰/奇迹玫瑰 在线 | 坎贝奇第二部曲品味人生电影| 欢乐家长群2电视剧免费播放完整版| 大鸟笼电影免费观看| 哈利波特1—8免费观看| overflower第一季免费观看| 李小冉《过界》未删减| 侵犯高傲女教师在线播放| 海霞免费| 奶爸战神:打仗带娃完整版观看 | 法国版女超人在线观看| 排球少年第四季免费| 黄种人粤语| 老公原谅我电视剧免费观看| 电视剧今生无悔| 砍贝奇品味人生| 交换的一天2韩国中汉字普通话| 特殊的美容院| 美酒加咖啡dj| 第一滴血1| 徐悲鸿电视剧全集免费| 需要爸爸播种子-在线追剧| 酒店1-50集在线观看| 追星星的人| 邻家女孩百度影音| 进度条 英文| 天黑请闭眼电视剧| 大黑屌在线观看| 父亲动漫在线观看完整版 | 残奥会主题曲| 柯南日语版| 《房屋销售员》| 日韩r片| 最美和声第一期| 少狼第三季13| 千山暮雪百度影音| 庆余年2电视剧免费87集| 《善良的女邻居》4| 牙医姐妹电影免费观看| 沉香如屑全集在线观看| 与敌同行国语下载| 情牵百子柜| 左右不逢源第四季| 《新上任的秘书》全集| 村妓妻子校友二| 黛玉传下载| 先结婚后恋爱电视剧| 同学聚会的目的电影| 杜拉拉之似水年华下载| 战狼6免费观看在线播放| 法国《古墓丽影》| 太九在线观看免费版电视剧| 熊出没之环游世界| 简言的夏冬在线观看| 扶摇电视剧| 姑娘在线第8集| 刘纪鹏:股市破3000点是爆发| 猎物电影| 一寸一寸进入小姪女| 强伦妺妺免费播放视频| 北京遇上西雅图海清| 北上电视剧在线观看全集免费播放| 护花危情电视剧国语在线观看| 爱几何完整版| 无懈可击之美女如云结局| 《原始人》电影免费观看| 甄嬛传第64集| 白石茉里奈| 《善良的小峓子》3| 战狼12欧美版免费观看完整版国语百度 | 火蓝刀锋第二部| 朗读者 电影完整版| 喜人奇妙夜第二季在线观看| 我的气质你模仿不来是什么歌| 韦小宝玩女大王| 站着再来一次26集更新时间| 八犬传东方八犬异闻第二季| 飙速战车队免费全集在线观看| 权游前传龙族第二季| 官方回应一餐厅致多人腹泻1人死亡 | 武状元苏乞儿之天降神谕| 平乡县| 天美影院在线观看免费观看电视剧大全 | 巴黎危机完整版高清免费观看| 《爱你几何》免费观看| 被义子侵犯的继拇HD| 某小说家的日记未删减| 日本h小游戏| 侦察兵电影| 明星大侦探第八季免费观看完整版| 《法国瑜伽》在线观看| 《痛难了》完整版| 电车之狼R通关攻略| 电视剧光荣岁月| 印度剧继承者| 绅士和小姐| 热天午后| 白峰美羽《被讨厌的上司》完整版| 壮志凌云 满天星在线观看 | 平顶山市| 新的上任秘书| 庆余年第二季40集免费观看| 凤与朝电视剧免费观看| 举重妖精金福珠在线观看| 还珠格格第一部高清| 僵尸家族电影| 做aj的电视剧| 欧洲妈妈爱上儿子第一季的剧情简介 | 女人是男人的未来免费观看| 上海路炒粉| 洞口县| 泰国十部必看的满天星电影| 在线播放需要爸爸播种| 甜蜜惩罚免费观看第一季全集| 我强行与岳的性关系在线观看|