最近放假在家好好学习了一下Go语言,Go做为Google官推的Server语言,由于天生的并发性和完备的标准库让Go语言在服务端如鱼得水。笔者在简单的学习了以后,真的是惊讶连连,好了进入正题。编程
首先,咱们必须实现一个Go Web版的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)
if err != nil {
log.Fatal(err)
}
}
复制代码
咱们能够看到Go语言实现一个Web HelloWorld的简洁程度甚至直接媲美Node.js,不须要任何容器即可以实现一个高并发的简单服务器。下面咱们来分析一下这个代码:bash
首先,咱们导入了fmt,http包,log包其实对于HelloWorld来讲并无导入的必要,可是日志输出这个良好习惯仍是得听从。在main()函数的第一行,咱们经过http.HandleFunc定义了路由为"/"的响应函数,这个响应函数,接受传来的Request,并对Response作必定的处理即写入HelloWorld而后直接返回给浏览器。而后即可以直接调用http.ListenAndServe来监听本地的8000端口,即可以直接在浏览器上看到HelloWorld。服务器
好,上面的流程其实很简单,有必定Web编程的人便都能明白,接下来咱们便从Go的源码中看一看,这段代码到底是如何实现的。并发
// 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)
}
复制代码
上面这段即是Go源码中对HandleFunc函数的实现,咱们能够看到这个函数直接将全部参数所有传递给了DefaultServeMux.HandleFunc来调用。函数
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
复制代码
DefaultServeMux是http包中的全局变量,它的原型是ServeMux这个结构体,咱们再往上翻看这个结构体的HandleFunc方法。高并发
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
复制代码
咱们能够看到,彷佛没完没了,HandleFunc也是直接调用这个结构体的另外一个方法Handle,另外HandlerFunc(handler)中的HandlerFunc也只是一个type的定义。学习
type HandlerFunc func(ResponseWriter, *Request)
复制代码
这个函数自己并无实现什么,须要咱们本身去实现它的内容。也就是咱们上面所提到的响应函数。ui
// 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)
复制代码
终于咱们找到了源头,固然这个方法的源代码还比较长,这里就不贴出所有,Handle这个方法接受两个参数,pattern这个string类型的参数表示路由,第二个参数handle它实际上是Handler接口。url
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
复制代码
能够看到Handler这个接口中只定义了ServeHTTP这一个方法,换句话说,咱们也能够直接实现ServeHTTP这个方法来实现Handler这个接口,而后咱们即可以传给ServeMux来自定义咱们的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)
if err != nil {
log.Fatal(err)
}
}
复制代码
上面的代码能够看到,咱们定义了一个CustomHandler,而后实现了ServeHTTP这个方法从而实现了Handler这个接口,在main方法中,咱们经过NewServeMux建立了一个本身的mux而不去使用http内的默认ServerMux。而后调用ListenAndServe方法,并将本身的mux传入,程序便会实现自定义的HelloWorld了。 接下来咱们来看一下ListenAndServe这个方法:
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
复制代码
源码中能够看到该方法会将传入进来的addr参数和handler送给Server这个结构体,从而新建一个server而后调用这个server的ListenAndServe方法,对于Server这个结构它已是Go语言对于这个方面很是底层的实现了,它很是强大,并且实现了不少的方法,这里不过多阐述,主要是实力不够(笑)。 好,回到正题,既然如此,咱们即可以本身建立Server这个实例,来自定义咱们的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()
if err != nil {
log.Fatal(err)
}
}
复制代码
上面这段代码即是自创server的实现了,这里挑选几条新的代码说明一下,咱们定义了一个mux的全局变量,它来装配咱们的路由与相应函数的映射,至关于上面的mux.Handle("/", .....),这里比较简陋的直接用Map来实现,接下来咱们定义了Hello这个响应函数,咱们也重写了ServeHTTP这个方法,它会判断request的url路径与咱们mux里面的路径是否匹配,若是匹配在从mux中取出相应的响应函数并将w http.ResponseWriter, r *http.Request这两个参数传递给这个相应函数。
在main函数里,咱们建立了本身的server,经过端口号,Handler及timeout时间来定义它,而后调用它的ListenAndServe方法,即可以实现与前面两个相同的HelloWorld功能。好了,今天写到这里,太晚了(笑)。