//开启web服务
func test(){
http.HandleFunc("/", sayHello)
err := http.ListenAndServe(":9090",nil)
if err!=nil {
log.Fatal("ListenAndServer:",err)
}
}
func sayHello(w http.ResponseWriter, r *http.Request){
r.ParseForm()
fmt.Println("path",r.URL.Path)
fmt.Println("scheme",r.URL.Scheme)
fmt.Fprintf(w, "Hello Guest!")
}
в настоящее время используетListenAndServe
В этом методе система назначит нам роутер,DefaultServeMux
маршрутизатор по умолчанию, используемый системой, еслиListenAndServe
Второй параметр этого метода передается в nil, и система будет использовать его по умолчанию.DefaultServeMux
. Конечно, здесь также можно передать собственный маршрутизатор.
Первый взглядhttp.HandleFunc("/", sayHello)
,отHandleFunc
Метод нажимается следующим образом:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
звонил сюдаDefaultServeMux
изHandleFunc
метод, этот метод имеет два параметра,pattern
это соответствующее правило маршрутизации,handler
Указывает метод обработки, соответствующий этому правилу маршрутизации, и этот метод обработки имеет два параметра.
В примере кода, который мы написали,pattern
соответствовать/
,handler
соответствоватьsayHello
, когда мы набираем в браузереhttp://localhost:9090
, это вызоветsayHello
метод.
мы следуемDefaultServeMux
изHandleFunc
Метод продолжает указывать, следующим образом:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
В этом методе маршрутизатор снова вызываетHandle
метод, обратите внимание на этоHandle
Второй параметр метода, переданный ранееhandler
Этот метод ответа заставляетHandlerFunc
тип.
этоHandlerFunc
Что это за тип? следующее:
type HandlerFunc func(ResponseWriter, *Request)
Кажется, и мы определяемSayHello
Типы методов аналогичны. но! ! !
этоHandlerFunc
Реализовано по умолчаниюServeHTTP
интерфейс! такHandlerFunc
объект имеетServeHTTP
метод! следующее:
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Эта деталь очень важна, потому что этот шаг связан с тем, будет ли вызываться соответствующий метод ответа при совпадении правила маршрутизации! Когда этот метод вызывается, будет описано в следующем разделе.
Далее возвращаемся и продолжаем смотретьmux
изHandle
метод, который является этим кодомmux.Handle(pattern, HandlerFunc(handler))
. Что делает этот код? Исходный код выглядит следующим образом:
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
}
// Helpful behavior:
// If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
n := len(pattern)
if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
// If pattern contains a host name, strip it and use remaining
// path for redirect.
path := pattern
if pattern[0] != '/' {
// In pattern, at least the last character is a '/', so
// strings.Index can't be -1.
path = pattern[strings.Index(pattern, "/"):]
}
url := &url.URL{Path: path}
mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
}
}
Кода много, на самом деле он в основном делает одно,DefaultServeMux
изmap[string]muxEntry
Добавьте соответствующие правила маршрутизации иhandler
.
map[string]muxEntry
Что это за фигня?
map
это объект словаря, который содержитkey-value
.[string]
представляет этот словарьkey
даstring
тип, этоkey
Это значение будет содержать наши правила маршрутизации.muxEntry
Это экземпляр объекта, в котором хранится метод обработки, соответствующий правилу маршрутизации.
Найдите соответствующий код следующим образом:
//路由器
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry //路由规则,一个string对应一个mux实例对象,map的key就是注册的路由表达式(string类型的)
hosts bool // whether any patterns contain hostnames
}
//muxEntry
type muxEntry struct {
explicit bool
h Handler //这个路由表达式对应哪个handler
pattern string
}
//路由响应方法
type Handler interface {
ServeHTTP(ResponseWriter, *Request) //handler的路由实现器
}
ServeMux
Это маршрутизатор по умолчанию в этой системе.
Наконец, резюмируя этот раздел:
1. звонокhttp.HandleFunc("/", sayHello)
2. позвонитьDefaultServeMux
изHandleFunc()
, который определяет нашуsayHello()
упаковано вHandlerFunc
тип
3. Продолжайте звонитьDefaultServeMux
изHandle()
,В направленииDefaultServeMux
изmap[string]muxEntry
Добавьте правила маршрутизации и соответствующиеhandler
Хорошо, эта часть кода так много делает, и первая часть закончена.
Вторая часть предназначена в основном для изучения этого кодаerr := http.ListenAndServe(":9090",nil)
, это,ListenAndServe
Сюда. Нажмите на этот метод следующим образом:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
В этом методе инициализируйтеserver
объект, затем вызовите это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)
, то есть нижний уровень строит службу с протоколом TCP, а затем отслеживает установленный нами порт.
В конце кода вызовитеsrv
изServe
Методы, как показано ниже:
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)
ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
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)
}
}
Последние три фрагмента кода более важны, и они также являются воплощением языка Go, поддерживающего высокий параллелизм, а именно:
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
Большой кусок кода выше означает, что после ввода метода сначала откройтеfor
цикл, вfor
Принять запрос в данный момент в цикле.После прихода запроса для каждого запроса будет создаваться запрос.Conn
, затем откройте отдельныйgoroutine
, кинуть данные этого запроса в качестве параметра в этотConn
Служить:go c.serve()
. Каждый запрос от пользователя выполняется в новомgoroutine
Для обслуживания каждый запрос не влияет друг на друга.
существуетconn
изserve
В методе есть очень важный код, а именно:
serverHandler{c.server}.ServeHTTP(w, w.req)
выражатьserverHandler
ДостигнутоServeHTTP
интерфейс,ServeHTTP
Метод реализуется следующим образом:
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
пусто (этоhandler
Его можно понимать как наш пользовательский маршрутизатор), будет использоваться системное значение по умолчанию.DefaultServeMux
, последний вызов кодаDefaultServeMux
изServeHTTP()
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
}
h, _ := mux.Handler(r) //这里返回的h是Handler接口对象
h.ServeHTTP(w, r) //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法
}
После того, как роутер получает запрос, если оно*
тогда закройте ссылку, если нет*
просто позвониmux.Handler(r)
Возвращает обработку, соответствующую маршрутуHandler
, затем выполнитеhandler
изServeHTTP
Метод, который является кодомh.ServeHTTP(w, r)
,mux.Handler(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 { //mux.m就是系统默认路由的map
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h
pattern = v.pattern
}
}
return
}
это будет запрошено пользователемURL
хранится в роутереmap
Если совпадение успешно, он вернет сохраненныйhandler
, назовите этоhandler
изServeHTTP()
Вы можете выполнить соответствующий метод обработки, этот метод обработки фактически является тем, что мы только что начали.sayHello()
Но этоsayHello()
одеялоHandlerFunc
другой слой, потому чтоHandlerFunc
ДостигнутоServeHTTP
интерфейс, так называемыйHandlerFunc
объектServeHTTP()
когда на самом делеServeHTTP ()
внутренне звонит нашемуsayHello()
.
в заключении:
1. звонокhttp.ListenAndServe(":9090",nil)
2. Создать экземплярserver
3. позвонитьserver
изListenAndServe()
4. звонокserver
изServe
метод, включитьfor
Цикл, принять запрос в цикле
5. Создайте один экземпляр для каждого запросаConn
и откройтеgoroutine
обслужить этот запросgo c.serve()
6. Прочитайте содержимое каждого запросаc.readRequest()
7. ПризватьserverHandler
изServeHTTP()
,еслиhandler
пусто, поставьhandler
Установить в качестве системного маршрутизатора по умолчаниюDefaultServeMux
8. Призватьhandler
изServeHTTP()
=> на самом деле называетсяDefaultServeMux
изServeHTTP()
9. ВServeHTTP()
Соответствующая обработка маршрута будет вызвана вhandler
10. Соответствующая обработка в маршрутизацииhandler
будет казненsayHello()
Следует отметить один момент:DefaultServeMux
Метод обработки, соответствующий маршрутуhandler
все сбудетсяServeHTTP
интерфейс, они оба имеютServeHTTP
метод, но цель метода иная, вDefaultServeMux
изServeHttp()
Обработка, соответствующая маршруту, будет выполняться вhandler
изServeHttp()
.