Skip to content

Latest commit

 

History

History
135 lines (99 loc) · 4.41 KB

File metadata and controls

135 lines (99 loc) · 4.41 KB

3.4 Get into http package

In previous sections, we learned the work flow of web, and talked about a little about http package. In this section, we are going to learn two core functions in http package: Conn, ServeMux.

goroutine in Conn

Unlike normal HTTP servers, Go uses goroutine for every affair that created by Conn in order to achieve high concurrency and performance, so every affair is independent.

Go uses following code to wait for new connections from clients.

c, err := srv.newConn(rw)
if err != nil {
	continue
}
go c.serve()

As you can see, it creates goroutine for every connection, and passes handler that is able to read data from request to the goroutine.

Customized ServeMux

We used default router in previous section when talked about conn.server, the router passed request data to back-end handler.

The struct of default router:

type ServeMux struct {
	mu sync.RWMutex   // because of concurrency, we have to use mutex here
	m  map[string]muxEntry  // router rules, every string mapping to a handler
}

The struct of muxEntry:

type muxEntry struct {
	explicit bool   // exact match or not
	h        Handler
}

The interface of Handler:

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)  // routing implementer
}

Handler is a interface, but the function sayhelloName didn't implement this interface, why could we add it as handler? Because there is another type HandlerFunc in http package. We called HandlerFunc to define our sayhelloName, so sayhelloName implemented Handler at the same time. it's like we call HandlerFunc(f), and function f is forced converted to type HandlerFunc.

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

How the router calls handlers after we set router rules?

The router calls mux.handler.ServeHTTP(w, r) when it receives requests. In other words, it calls ServeHTTP interface of handlers.

Now, let's see how mux.handler works.

func (mux *ServeMux) handler(r *Request) Handler {
	mux.mu.RLock()
	defer mux.mu.RUnlock()

	// Host-specific pattern takes precedence over generic ones
	h := mux.match(r.Host + r.URL.Path)
	if h == nil {
    	h = mux.match(r.URL.Path)
	}
	if h == nil {
    	h = NotFoundHandler()
	}
	return h
}

The router uses URL as a key to find corresponding handler that saved in map, and calls handler.ServeHTTP to execute functions to handle data.

You should understand the router work flow now, and Go actually supports customized routers. The second argument of ListenAndServe is for configuring customized router, it's a interface of Handler. Therefore, any router implements interface Handler that can be used.

The following example shows how to implement a simple router.

package main

import (
	"fmt"
	"net/http"
)

type MyMux struct {
}

func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
    	sayhelloName(w, r)
    	return
	}
	http.NotFound(w, r)
	return
}

func sayhelloName(w http.ResponseWriter, r 	*http.Request) {
	fmt.Fprintf(w, "Hello myroute!")
}

func main() {
	mux := &MyMux{}
	http.ListenAndServe(":9090", mux)
}

Go code execution flow

Let's take a look at the list of whole execution flow.

  • Call http.HandleFunc
    1. Call HandleFunc of DefaultServeMux
    2. Call Handle of DefaultServeMux
    3. Add router rules to map[string]muxEntry of DefaultServeMux
  • Call http.ListenAndServe(":9090", nil)
    1. Instantiated Server
    2. Call ListenAndServe of Server
    3. Call net.Listen("tcp", addr) to listen to port.
    4. Start a loop, and accept requests in loop body.
    5. Instantiated a Conn and start a goroutine for every request: go c.serve().
    6. Read request data: w, err := c.readRequest().
    7. Check handler is empty or not, if it's empty then use DefaultServeMux.
    8. Call ServeHTTP of handler.
    9. Execute code in DefaultServeMux in this case.
    10. Choose handler by URL and execute code in that handler function: mux.handler.ServeHTTP(w, r)
    11. How to choose handler: A. Check router rules for this URL. B. Call ServeHTTP in that handler if there is one. C. Call ServeHTTP of NotFoundHandler otherwise.

Links