最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang高并發(fā)的深入理解

 更新時(shí)間:2019年03月10日 15:29:10   作者:飛翔碼農(nóng)  
golang從語(yǔ)言級(jí)別上對(duì)并發(fā)提供了支持,而且在啟動(dòng)并發(fā)的方式上直接添加了語(yǔ)言級(jí)的關(guān)鍵字。下面這篇文章主要給大家介紹了關(guān)于golang高并發(fā)的相關(guān)資料,需要的朋友可以參考下

前言

GO語(yǔ)言在WEB開(kāi)發(fā)領(lǐng)域中的使用越來(lái)越廣泛,Hired 發(fā)布的《2019 軟件工程師狀態(tài)》報(bào)告中指出,具有 Go 經(jīng)驗(yàn)的候選人是迄今為止最具吸引力的。平均每位求職者會(huì)收到9 份面試邀請(qǐng)。


想學(xué)習(xí)go,最基礎(chǔ)的就要理解go是怎么做到高并發(fā)的。

那么什么是高并發(fā)?

高并發(fā)(High Concurrency)是互聯(lián)網(wǎng)分布式系統(tǒng)架構(gòu)設(shè)計(jì)中必須考慮的因素之一,它通常是指,通過(guò)設(shè)計(jì)保證系統(tǒng)能夠同時(shí)并行處理很多請(qǐng)求。

嚴(yán)格意義上說(shuō),單核的CPU是沒(méi)法做到并行的,只有多核的CPU才能做到嚴(yán)格意義上的并行,因?yàn)橐粋€(gè)CPU同時(shí)只能做一件事。那為什么是單核的CPU也能做到高并發(fā)。這就是操作系統(tǒng)進(jìn)程線程調(diào)度切換執(zhí)行,感覺(jué)上是并行處理了。所以只要進(jìn)程線程足夠多,就能處理C1K C10K的請(qǐng)求,但是進(jìn)程線程的數(shù)量又受到操作系統(tǒng)內(nèi)存等資源的限制。每個(gè)線程必須分配8M大小的棧內(nèi)存,不管是否使用。每個(gè)php-fpm需要占用大約20M的內(nèi)存。所以目前有線程的Java就比只有進(jìn)程的PHP的并發(fā)處理能力高。當(dāng)然了,軟件的處理能力不僅僅跟內(nèi)存有關(guān),還有是否阻塞,是否異步處理,CPU等等。Nginx作為單線程的模型卻可以承擔(dān)幾萬(wàn)甚至幾十萬(wàn)的并發(fā)請(qǐng)求,Nginx的話題說(shuō)起來(lái)也就更多了。
我們繼續(xù)聊我們的Go,那么是不是可以有一種語(yǔ)言使用更小的處理單元,占用內(nèi)存比線程更小,那么它的并發(fā)處理能力就可以更高。所以Google就做了這件事,就有了golang語(yǔ)言,golang從語(yǔ)言層面就支持了高并發(fā)。

go為什么能做到高并發(fā)

goroutine是Go并行設(shè)計(jì)的核心。goroutine說(shuō)到底其實(shí)就是協(xié)程,但是它比線程更小,幾十個(gè)goroutine可能體現(xiàn)在底層就是五六個(gè)線程,Go語(yǔ)言內(nèi)部幫你實(shí)現(xiàn)了這些goroutine之間的內(nèi)存共享。執(zhí)行g(shù)oroutine只需極少的棧內(nèi)存(大概是4~5KB),當(dāng)然會(huì)根據(jù)相應(yīng)的數(shù)據(jù)伸縮。也正因?yàn)槿绱耍赏瑫r(shí)運(yùn)行成千上萬(wàn)個(gè)并發(fā)任務(wù)。goroutine比thread更易用、更高效、更輕便。

一些高并發(fā)的處理方案基本都是使用協(xié)程,openresty也是利用lua語(yǔ)言的協(xié)程做到了高并發(fā)的處理能力,PHP的高性能框架Swoole目前也在使用PHP的協(xié)程。

協(xié)程更輕量,占用內(nèi)存更小,這是它能做到高并發(fā)的前提。

go web開(kāi)發(fā)中怎么做到高并發(fā)的能力

學(xué)習(xí)go的HTTP代碼。先創(chuàng)建一個(gè)簡(jiǎn)單的web服務(wù)。

package main

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

func response(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintf(w, "Hello world!") //這個(gè)寫(xiě)入到w的是輸出到客戶端的
}

func main() {
 http.HandleFunc("/", response)
 err := http.ListenAndServe(":9000", nil)
 if err != nil {
  log.Fatal("ListenAndServe: ", err)
 }
}

然后編譯

go build -o test_web.gobin
./test_web.gobin

然后訪問(wèn)

curl 127.0.0.1:9000
Hello world!

這樣簡(jiǎn)單的一個(gè)WEB服務(wù)就搭建起來(lái)。接下來(lái)我們一步一步理解這個(gè)Web服務(wù)是怎么運(yùn)行的,怎么做到高并發(fā)的。
我們順著http.HandleFunc("/", response)方法順著代碼一直往上看。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 DefaultServeMux.HandleFunc(pattern, handler)
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux

type ServeMux struct {
 mu sync.RWMutex//讀寫(xiě)鎖。并發(fā)處理需要的鎖
 m  map[string]muxEntry//路由規(guī)則map。一個(gè)規(guī)則一個(gè)muxEntry
 hosts bool //規(guī)則中是否帶有host信息
}
一個(gè)路由規(guī)則字符串,對(duì)應(yīng)一個(gè)handler處理方法。
type muxEntry struct {
 h  Handler
 pattern string
}

上面是DefaultServeMux的定義和說(shuō)明。我們看到ServeMux結(jié)構(gòu)體,里面有個(gè)讀寫(xiě)鎖,處理并發(fā)使用。muxEntry結(jié)構(gòu)體,里面有handler處理方法和路由字符串。

接下來(lái)我們看下,http.HandleFunc函數(shù),也就是DefaultServeMux.HandleFunc做了什么事。我們先看mux.Handle第二個(gè)參數(shù)HandlerFunc(handler)

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 mux.Handle(pattern, HandlerFunc(handler))
}
type Handler interface {
 ServeHTTP(ResponseWriter, *Request) // 路由實(shí)現(xiàn)器
}
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
 f(w, r)
}

我們看到,我們傳遞的自定義的response方法被強(qiáng)制轉(zhuǎn)化成了HandlerFunc類型,所以我們傳遞的response方法就默認(rèn)實(shí)現(xiàn)了ServeHTTP方法的。

我們接著看mux.Handle第一個(gè)參數(shù)。

func (mux *ServeMux) Handle(pattern string, handler Handler) {
 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)
 }
 mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

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

將路由字符串和處理的handler函數(shù)存儲(chǔ)到ServeMux.m 的map表里面,map里面的muxEntry結(jié)構(gòu)體,上面介紹了,一個(gè)路由對(duì)應(yīng)一個(gè)handler處理方法。

接下來(lái)我們看看,http.ListenAndServe(":9000", nil)做了什么

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)})
}

net.Listen("tcp", addr) ,就是使用端口addr用TCP協(xié)議搭建了一個(gè)服務(wù)。tcpKeepAliveListener就是監(jiān)控addr這個(gè)端口。

接下來(lái)就是關(guān)鍵代碼,HTTP的處理過(guò)程

func (srv *Server) Serve(l net.Listener) error {
 defer l.Close()
 if fn := testHookServerServe; fn != nil {
  fn(srv, l)
 }
 var tempDelay time.Duration // how long to sleep on accept failure

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

 srv.trackListener(l, true)
 defer srv.trackListener(l, false)

 baseCtx := context.Background() // base is always background, per Issue 16220
 ctx := context.WithValue(baseCtx, ServerContextKey, srv)
 for {
  rw, e := l.Accept()
  if e != nil {
   select {
   case <-srv.getDoneChan():
    return ErrServerClosed
   default:
   }
   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
   }
   return e
  }
  tempDelay = 0
  c := srv.newConn(rw)
  c.setState(c.rwc, StateNew) // before Serve can return
  go c.serve(ctx)
 }
}

for里面l.Accept()接受TCP的連接請(qǐng)求,c := srv.newConn(rw)創(chuàng)建一個(gè)Conn,Conn里面保存了該次請(qǐng)求的信息(srv,rw)。啟動(dòng)goroutine,把請(qǐng)求的參數(shù)傳遞給c.serve,讓goroutine去執(zhí)行。

這個(gè)就是GO高并發(fā)最關(guān)鍵的點(diǎn)。每一個(gè)請(qǐng)求都是一個(gè)單獨(dú)的goroutine去執(zhí)行。

那么前面設(shè)置的路由是在哪里匹配的?是在c.serverde的c.readRequest(ctx)里面分析出URI METHOD等,執(zhí)行serverHandler{c.server}.ServeHTTP(w, w.req)做的??聪麓a

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)
}

handler為空,就我們剛開(kāi)始項(xiàng)目中的ListenAndServe第二個(gè)參數(shù)。我們是nil,所以就走DefaultServeMux,我們知道開(kāi)始路由我們就設(shè)置的是DefaultServeMux,所以在DefaultServeMux里面我一定可以找到請(qǐng)求的路由對(duì)應(yīng)的handler,然后執(zhí)行ServeHTTP。前邊已經(jīng)介紹過(guò),我們的reponse方法為什么具有ServeHTTP的功能。流程大概就是這樣的。

我們看下流程圖


結(jié)語(yǔ)

我們基本已經(jīng)學(xué)習(xí)忘了GO 的HTTP的整個(gè)工作原理,了解到了它為什么在WEB開(kāi)發(fā)中可以做到高并發(fā),這些也只是GO的冰山一角,還有Redis MySQL的連接池。要熟悉這門語(yǔ)言還是多寫(xiě)多看,才能掌握好它。靈活熟練的使用。

好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Go語(yǔ)言數(shù)據(jù)類型簡(jiǎn)單介紹

    Go語(yǔ)言數(shù)據(jù)類型簡(jiǎn)單介紹

    這篇文章主要介紹了Go語(yǔ)言數(shù)據(jù)類型簡(jiǎn)單介紹的相關(guān)資料,需要的朋友可以參考下
    2023-08-08
  • golang?Strings包使用總結(jié)

    golang?Strings包使用總結(jié)

    Go語(yǔ)言在處理字符串時(shí),strings包提供了豐富的函數(shù),如常用的strings.Contains檢查是否包含子串,strings.Join拼接字符串?dāng)?shù)組,strings.Split切割字符串等,熟悉這些函數(shù)能有效提高編程效率,尤其是在算法競(jìng)賽或筆試題中
    2021-03-03
  • go版tensorflow安裝教程詳解

    go版tensorflow安裝教程詳解

    這篇文章主要介紹了go版tensorflow安裝教程詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • GO語(yǔ)言文件的創(chuàng)建與打開(kāi)實(shí)例分析

    GO語(yǔ)言文件的創(chuàng)建與打開(kāi)實(shí)例分析

    這篇文章主要介紹了GO語(yǔ)言文件的創(chuàng)建與打開(kāi)的具體用法,實(shí)例分析了GO語(yǔ)言文件創(chuàng)建與打開(kāi)操作中所涉及的函數(shù)具體用法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-12-12
  • golang 整合antlr語(yǔ)法校驗(yàn)解析

    golang 整合antlr語(yǔ)法校驗(yàn)解析

    Antlr是一個(gè)語(yǔ)法分析器,本身是用java實(shí)現(xiàn)的,然是Runtime的庫(kù)也支持Golang、Java、Python等,本文給大家講解使用golang整合antlr進(jìn)行語(yǔ)法解析,感興趣的朋友一起看看吧
    2023-02-02
  • golang遍歷時(shí)修改被遍歷對(duì)象的示例詳解

    golang遍歷時(shí)修改被遍歷對(duì)象的示例詳解

    這篇文章主要介紹了golang遍歷時(shí)修改被遍歷對(duì)象的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Go結(jié)合MQTT實(shí)現(xiàn)通信的示例代碼

    Go結(jié)合MQTT實(shí)現(xiàn)通信的示例代碼

    本文主要介紹了Go結(jié)合MQTT實(shí)現(xiàn)通信的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Go語(yǔ)言Web開(kāi)發(fā)框架之路由、中間件、參數(shù)校驗(yàn)實(shí)踐記錄

    Go語(yǔ)言Web開(kāi)發(fā)框架之路由、中間件、參數(shù)校驗(yàn)實(shí)踐記錄

    Gin框架以極簡(jiǎn)設(shè)計(jì)、靈活路由分組、可插拔中間件及自動(dòng)化參數(shù)校驗(yàn)聞名,支持多來(lái)源數(shù)據(jù)綁定、結(jié)構(gòu)體驗(yàn)證與統(tǒng)一錯(cuò)誤處理,顯著提升開(kāi)發(fā)效率與系統(tǒng)維護(hù)性,本文給大家介紹Go語(yǔ)言Web開(kāi)發(fā)框架之路由、中間件、參數(shù)校驗(yàn)實(shí)踐記錄,感興趣的朋友一起看看吧
    2025-06-06
  • 基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt

    基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt

    這篇文章主要介紹了基于gin的golang web開(kāi)發(fā)之認(rèn)證利器jwt,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • go語(yǔ)言中基本數(shù)據(jù)類型及應(yīng)用快速了解

    go語(yǔ)言中基本數(shù)據(jù)類型及應(yīng)用快速了解

    這篇文章主要為大家介紹了go語(yǔ)言中基本數(shù)據(jù)類型應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07

最新評(píng)論

夏津县| 襄城县| 逊克县| 乐平市| 韶山市| 济南市| 息烽县| 云浮市| 横山县| 辽中县| 耿马| 柘荣县| 手游| 江津市| 兴宁市| 洪雅县| 吴忠市| 沙河市| 崇仁县| 盈江县| 华安县| 怀来县| 黎平县| 砀山县| 云和县| 靖边县| 怀仁县| 松江区| 临夏县| 太保市| 汕头市| 简阳市| 遵义县| 夏邑县| 天水市| 绥芬河市| 齐齐哈尔市| 瓦房店市| 汉沽区| 建水县| 自治县|