Recently, I studied Go language at home during the vacation. Go is the official Server language of Google. Because of its natural concurrency and complete standard library, Go language works like a duck to water on the Server side. The author in a simple study, really surprised again and again, good into the topic.

First, we must implement a Go Web version of Hello World.

package main

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

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s\n"."Hello World")
	})
	err := http.ListenAndServe(": 8000", nil)
	iferr ! = nil { log.Fatal(err) } }Copy the code

We can see that the simplicity of the Go language to implement a Web HelloWorld is even directly comparable to That of Node.js, and a simple server with high concurrency can be implemented without any containers. Let’s examine this code:

First, we imported the FMT, HTTP, and log packages that are not really necessary for HelloWorld, but logging is a good habit to follow. On the first line of main(), we define a Response function with a “/” route via http.handlefunc. This Response function accepts the incoming Request and does something with the Response: writes HelloWorld and returns it directly to the browser. ListenAndServe is used to listen on port 8000 and you can see HelloWorld directly on your browser.

Ok, the above process is actually very simple, anyone who has some Web programming will understand, let’s take a look at the Go source code to see how this code is implemented.

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
Copy the code

The above is to Go in the source code for HandleFunc function implementation, we can see this function directly pass all the parameters to the DefaultServeMux. HandleFunc to invoke.

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux
Copy the code

DefaultServeMux, the global variable in the HTTP package, is modeled after the ServeMux structure, and we’ll look up at the HandleFunc method of that structure.

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	mux.Handle(pattern, HandlerFunc(handler))
}
Copy the code

HandleFunc is another method that calls this structure directly, Handle, and HandlerFunc(Handler) is just a definition of Type.

type HandlerFunc func(ResponseWriter, *Request)
Copy the code

The function itself does not implement anything, we need to implement its content. That’s the response function we mentioned above.

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler)
Copy the code

Of course, the source code of this method is still quite long, so I won’t post all of it here. The Handle method accepts two parameters. The string parameter pattern represents the route, and the second parameter Handle is actually a Handler interface.

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
Copy the code

We can see that the Handler interface defines only ServeHTTP. In other words, we can implement the Handler interface directly with ServeHTTP, and then we can pass it to ServeMux to customize our HelloWorld.

package main

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

type CustomHandler struct{}

func (*CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "%s\n"."Hello World")
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", &CustomHandler{})
	err := http.ListenAndServe(": 8000", mux)
	iferr ! = nil { log.Fatal(err) } }Copy the code

As you can see from the above code, we defined a CustomHandler and implemented the ServeHTTP method to implement the Handler interface. In the main method, We created our own MUx with NewServeMux instead of using the default ServerMux in HTTP. Then call ListenAndServe and pass in your MUX, and the application will implement the custom HelloWorld. ListenAndServe = ListenAndServe = ListenAndServe

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}
Copy the code

Can be seen in the source will be introduced to the method in the addr parameter and the handler Server this structure, so as to create a new Server and then call the Server ListenAndServe method, The Server structure is already a very low-level implementation of the Go language. It’s very powerful, and it implements a lot of methods, but I won’t elaborate on it here, mainly because it’s not strong enough. (Laughter) Ok, back to business, in this case, we can create our own Server instance to customize the second version of our HelloWorld.

package main

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

type CustomHandler struct{}

var mux = make(map[string]func(http.ResponseWriter, *http.Request))

func Hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "%s\n"."Hello World")
}

func (*CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if handler, ok := mux[r.URL.String()]; ok {
		handler(w, r)
	}
}

func main() {
	server := http.Server{
		Addr:": 8000",
		Handler:&CustomHandler{},
		ReadHeaderTimeout:5 * time.Second,
	}
	mux["/"] = Hello
	err := server.ListenAndServe()
	iferr ! = nil { log.Fatal(err) } }Copy the code

We define a mux global variable that maps our route to the corresponding function, equivalent to the mux.handle (“/”,…..) above. Next, we define the Hello response function. We also rewrite the ServeHTTP method, which determines whether the request URL path matches the path in our MUX. If it matches, fetch the corresponding response function from the MUx and pass the parameters w http.ResponseWriter and r *http.Request to the corresponding function.

In the main function, we create our server, define it by port number, Handler, and timeout, and then call its ListenAndServe method to implement the same HelloWorld function as the first two. Well, it’s too late to write this today.