大神的教程https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.0.md
https://www.yuque.com/liujunjie/pshhng/inyv2t
Web是基于http協(xié)議的一個服務(wù)湾宙,Go語言里面提供了一個完善的net/http包歌粥,通過http包可以很方便的搭建起來一個可以運行的Web服務(wù)襟士。同時使用這個包能很簡單地對Web的路由,靜態(tài)文件,模版,cookie等數(shù)據(jù)進行設(shè)置和操作材诽。
它通過HTTP協(xié)議與客戶端通信
HTTP是一種讓Web服務(wù)器與瀏覽器(客戶端)通過Internet發(fā)送與接收數(shù)據(jù)的協(xié)議,它建立在TCP協(xié)議之上渤昌,一般采用TCP的80端口。
HTTP協(xié)議是無狀態(tài)的澈驼,同一個客戶端的這次請求和上次請求是沒有對應(yīng)關(guān)系的辛燥,對HTTP服務(wù)器來說,它并不知道這兩個請求是否來自同一個客戶端。為了解決這個問題挎塌, Web程序引入了Cookie機制來維護連接的可持續(xù)狀態(tài)徘六。
HTTP協(xié)議是建立在TCP協(xié)議之上的
我們先來看看Request包的結(jié)構(gòu), Request包分為3部分,第一部分叫Request line(請求行), 第二部分叫Request header(請求頭),第三部分是body(主體)榴都。header和body之間有個空行待锈,請求包的例子所示:
GET /domains/example/ HTTP/1.1 ?//請求行: 請求方法 請求URI HTTP協(xié)議/協(xié)議版本
Host:www.iana.org ? ?//服務(wù)端的主機名
User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 ? //瀏覽器信息
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 //客戶端能接收的MIME
Accept-Encoding:gzip,deflate,sdch ?//是否支持流壓縮
Accept-Charset:UTF-8,*;q=0.5 ?//客戶端字符編碼集
//空行,用于分割請求頭和消息體
//消息體,請求資源參數(shù),例如POST傳遞的參數(shù)
我們再來看看HTTP的response包,他的結(jié)構(gòu)如下:
HTTP/1.1 200 OK ? ? ?//狀態(tài)行
Server: nginx/1.0.8 ? ? //服務(wù)器使用的WEB軟件名及版本
Date:Date: Tue, 30 Oct 2012 04:14:25 GMT ?//發(fā)送時間
Content-Type: text/html ? ?//服務(wù)器發(fā)送信息的類型
Transfer-Encoding: chunked ? //表示發(fā)送HTTP包是分段發(fā)的
Connection: keep-alive ? ?//保持連接狀態(tài)
Content-Length: 90 ? ? //主體內(nèi)容長度
//空行 用來分割消息頭和主體
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //消息體
客戶機通過TCP/IP協(xié)議建立到服務(wù)器的TCP連接
瀏覽器會去請求DNS服務(wù)器嘴高,通過DNS獲取相應(yīng)的域名對應(yīng)的IP竿音,然后通過IP地址找到IP對應(yīng)的服務(wù)器后,要求建立TCP連接拴驮,等瀏覽器發(fā)送完HTTP Request(請求)包后春瞬,服務(wù)器接收到請求包之后才開始處理請求包,服務(wù)器調(diào)用自身服務(wù)莹汤,返回HTTP Response(響應(yīng))包快鱼;客戶端收到來自服務(wù)器的響應(yīng)后開始渲染這個Response包里的主體(body),等收到全部的內(nèi)容隨后斷開與該服務(wù)器之間的TCP連接纲岭。
DNS(Domain Name System)是“域名系統(tǒng)”的英文縮寫抹竹,是一種組織成域?qū)哟谓Y(jié)構(gòu)的計算機和網(wǎng)絡(luò)服務(wù)命名系統(tǒng),它用于TCP/IP網(wǎng)絡(luò)止潮,它從事將主機名或域名轉(zhuǎn)換為實際IP地址的工作窃判。
(Uniform Resource Locator)是“統(tǒng)一資源定位符”的英文縮寫,用于描述一個網(wǎng)絡(luò)上的資源, 基本格式如下
scheme://host[:port#]/path/.../[?query-string][#anchor]
scheme ? ? ? ? 指定底層使用的協(xié)議(例如:http, https, ftp)
host ? ? ? ? ? HTTP服務(wù)器的IP地址或者域名
port# ? ? ? ? ?HTTP服務(wù)器的默認端口是80喇闸,這種情況下端口號可以省略袄琳。如果使用了別的端口,必須指明燃乍,例如 http://www.cnblogs.com:8080/
path ? ? ? ? ? 訪問資源的路徑
query-string ? 發(fā)送給http服務(wù)器的數(shù)據(jù)
anchor ? ? ? ? 錨
幾個概念Request?Response?Conn?Handler
Request:用戶請求的信息唆樊,用來解析用戶的請求信息,包括post刻蟹、get逗旁、cookie、url等信息
Response:服務(wù)器需要反饋給客戶端的信息
Conn:用戶的每次請求鏈接
Handler:處理請求和生成返回信息的處理邏
如何監(jiān)聽端口舆瘪?
go是通過一個函數(shù)ListenAndServe來處理這些事情的片效,這個底層其實這樣處理的:初始化一個server對象,然后調(diào)用了net.Listen("tcp", addr)英古,也就是底層用TCP協(xié)議搭建了一個服務(wù)淀衣,然后監(jiān)控我們設(shè)置的端口。
如何接收客戶端請求召调?
調(diào)用了srv.Serve(net.Listener)函數(shù)膨桥,這個函數(shù)就是處理接收客戶端的請求信息蛮浑。這個函數(shù)里面起了一個for{},首先通過Listener接收請求国撵,其次創(chuàng)建一個Conn陵吸,最后單獨開了一個goroutine,把這個請求的數(shù)據(jù)當做參數(shù)扔給這個conn去服務(wù):go c.serve()介牙。這個就是高并發(fā)體現(xiàn)了,用戶的每一次請求都是在一個新的goroutine去服務(wù)澳厢,相互不影響环础。
如何分配handler?
conn首先會解析request:c.readRequest(),然后獲取相應(yīng)的handler:handler := c.server.Handler剩拢,也就是我們剛才在調(diào)用函數(shù)ListenAndServe時候的第二個參數(shù)线得,我們前面例子傳遞的是nil,也就是為空徐伐,那么默認獲取handler = DefaultServeMux,那么這個變量用來做什么的呢贯钩?對,這個變量就是一個路由器办素,它用來匹配url跳轉(zhuǎn)到其相應(yīng)的handle函數(shù)角雷,那么這個我們有設(shè)置過嗎?有,我們調(diào)用的代碼里面第一句不是調(diào)用了http.HandleFunc("/", sayhelloName)嘛性穿。這個作用就是注冊了請求/的路由規(guī)則勺三,當請求uri為"/",路由就會轉(zhuǎn)到函數(shù)sayhelloName需曾,DefaultServeMux會調(diào)用ServeHTTP方法吗坚,這個方法內(nèi)部其實就是調(diào)用sayhelloName本身,最后通過寫入response的信息反饋到客戶端呆万。
設(shè)置路由對應(yīng)的handle
//? ? http.HandleFunc("/", sayhelloName)// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.func(mux*ServeMux)Handle(patternstring,handlerHandler) {mux.mu.Lock()//鎖defermux.mu.Unlock()//解鎖ifpattern==""{panic("http: invalid pattern")? }ifhandler==nil{panic("http: nil handler")? }if_,exist:=mux.m[pattern];exist{panic("http: multiple registrations for "+pattern)? }ifmux.m==nil{mux.m=make(map[string]muxEntry)? }e:=muxEntry{h:handler,pattern:pattern}mux.m[pattern]=eifpattern[len(pattern)-1]=='/'{mux.es=appendSorted(mux.es,e)? }ifpattern[0]!='/'{mux.hosts=true}}
根據(jù)路由獲取對應(yīng)handle
//serverHandler{c.server}.ServeHTTP(w, w.req) 中調(diào)用 會判斷 sh.srv.Handler是否為空,如果為空就賦值為//DefaultServeMux 為公共變量ServeMux,//http.HandleFunc("/", sayhelloName) //設(shè)置訪問的路由 會將pattern設(shè)置到ServeMux的m? ? map[string]muxEntrytypeserverHandlerstruct{srv*Server}func(shserverHandler)ServeHTTP(rwResponseWriter,req*Request) {handler:=sh.srv.Handlerifhandler==nil{handler=DefaultServeMux}ifreq.RequestURI=="*"&&req.Method=="OPTIONS"{handler=globalOptionsHandler{}? }handler.ServeHTTP(rw,req)}typeServeMuxstruct{musync.RWMutexmmap[string]muxEntryes[]muxEntry// slice of entries sorted from longest to shortest.hostsbool// whether any patterns contain hostnames}
設(shè)置監(jiān)聽端
ln,err:=net.Listen("tcp",addr)c:=srv.newConn(rw)// A conn represents the server side of an HTTP connection.c.setState(c.rwc,StateNew)// before Serve can returngoc.serve(ctx)serverHandler{c.server}.ServeHTTP(w,w.req)
go c.serve(ctx) 是一個for循環(huán)里面,接收端口傳過的信息
// Serve accepts incoming connections on the Listener l, creating a// new service goroutine for each. The service goroutines read requests and// then call srv.Handler to reply to them.//// HTTP/2 support is only enabled if the Listener returns *tls.Conn// connections and they were configured with "h2" in the TLS// Config.NextProtos.//// Serve always returns a non-nil error and closes l.// After Shutdown or Close, the returned error is ErrServerClosed.func(srv*Server)Serve(lnet.Listener)error{iffn:=testHookServerServe;fn!=nil{fn(srv,l)// call hook with unwrapped listener}l=&onceCloseListener{Listener:l}deferl.Close()iferr:=srv.setupHTTP2_Serve();err!=nil{returnerr}if!srv.trackListener(&l,true) {returnErrServerClosed}defersrv.trackListener(&l,false)vartempDelaytime.Duration// how long to sleep on accept failurebaseCtx:=context.Background()// base is always background, per Issue 16220ctx:=context.WithValue(baseCtx,ServerContextKey,srv)for{rw,e:=l.Accept()ife!=nil{select{case<-srv.getDoneChan():returnErrServerCloseddefault:? ? ? }ifne,ok:=e.(net.Error);ok&&ne.Temporary() {iftempDelay==0{tempDelay=5*time.Millisecond}else{tempDelay*=2}ifmax:=1*time.Second;tempDelay>max{tempDelay=max}srv.logf("http: Accept error: %v; retrying in %v",e,tempDelay)time.Sleep(tempDelay)continue}returne}tempDelay=0c:=srv.newConn(rw)c.setState(c.rwc,StateNew)// before Serve can returngoc.serve(ctx)? }}