【每日筆記】【Go學習筆記】2019-01-04 Codis筆記

作者:譚淼

1、dlv的使用

dlv是調試go語言的工具,與gdb類似。下面是一些dlv的常用命令:

  • (1)dlv attach pid:類似與gdb attach pid,可以對正在運行的進程直接進行調試(pid爲進程號)。
  • (2)dlv debug:運行dlv debug test.go會先編譯go源文件,同時執行attach命令進入調試模式,該命令會在當前目錄下生成一個名爲debug的可執行二進制文件,退出調試模式會自動被刪除。
  • (3)dlv exec executable_file:直接從二進制文件啓動調試模式。

調試命令(常用的爲break,continue,next,print):

clipboard.png

注意:dlv可以通過goroutines查看所有的協程,並通過“goroutine 協程號”來切換協程。

2、martini框架

github地址:https://github.com/go-martini...

Martini是一個爲了編寫模塊化Web應用而生的強大的GO語言框架。其使用參考:https://github.com/go-martini...

3、codis-fe的啓動

codis-fe啓動首先要解析參數,然後啓動martini。

m := martini.New()
m.Use(martini.Recovery())
m.Use(render.Renderer())
m.Use(martini.Static(assets, martini.StaticOptions{SkipLogging: true}))

首先調用martini.New()方法創建一個Martini結構體,然後在調用該結構體的Use方法註冊中間件。

中間件的註冊比較簡單,就是向handlers切片添加方法。

func (m *Martini) Use(handler Handler) {
   //校驗方法
   validateHandler(handler)

   m.handlers = append(m.handlers, handler)
}

在處理請求時,會遍歷註冊的處理函數執行。

func (c *context) run() {
   for c.index <= len(c.handlers) {
      _, err := c.Invoke(c.handler())
      if err != nil {
         panic(err)
      }
      c.index += 1

      if c.Written() {
         return
      }
   }
}

除此之外,還有一個疑問,對於靜態資源,是在哪裏決定Content-Type的呢?

func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) {
    ......
    // If Content-Type isn't set, use the file's extension to find it, but
    // if the Content-Type is unset explicitly, do not sniff the type.
    ctypes, haveType := w.Header()["Content-Type"]
    var ctype string
    if !haveType {
        ctype = mime.TypeByExtension(filepath.Ext(name))
        if ctype == "" {
           // read a chunk to decide between utf-8 text and binary
           var buf [sniffLen]byte
           n, _ := io.ReadFull(content, buf[:])
           ctype = DetectContentType(buf[:n])
           _, err := content.Seek(0, io.SeekStart) // rewind to output whole file
           if err != nil {
              Error(w, "seeker can't seek", StatusInternalServerError)
              return
           }
        }
        w.Header().Set("Content-Type", ctype)
    } else if len(ctypes) > 0 {
       ctype = ctypes[0]
    }
    ......
}

從代碼可以看出,是根據後綴名來決定的。

後面將會新建一個路由實例,並添加路由。

r := martini.NewRouter()
r.Get("/list", func() (int, string) {
   names := router.GetNames()
   sort.Sort(sort.StringSlice(names))
   return rpc.ApiResponseJson(names)
})

r.Any("/**", func(w http.ResponseWriter, req *http.Request) {
   name := req.URL.Query().Get("forward")
   if p := router.GetProxy(name); p != nil {
      p.ServeHTTP(w, req)
   } else {
      w.WriteHeader(http.StatusForbidden)
   }
})

註冊路由後便開啓服務。

hs := &http.Server{Handler: h}
if err := hs.Serve(l); err != nil {
   log.PanicErrorf(err, "serve %s failed", listen)
}

Serve函數的實現也很簡單,就是循環進行accept,每收到請求便創建一個協程進行處理。

func (srv *Server) Serve(l net.Listener) error {
   ......
   for {
      rw, e := l.Accept()
      ......
      tempDelay = 0
      c := srv.newConn(rw)
      c.setState(c.rwc, StateNew) // before Serve can return
      go c.serve(ctx)
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章