Go Web学习(1)——标准库http实现server

最近放假在家好好学习了一下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功能。好了,今天写到这里,太晚了(笑)。

相关文章
相关标签/搜索