Skip to content

Latest commit

 

History

History
196 lines (143 loc) · 5.38 KB

http.md

File metadata and controls

196 lines (143 loc) · 5.38 KB

HTTP

上一章介绍了 Socket 的使用,主要提到了 TCP 以及 UDP,它们是属于传输层的协议,今天我们将介绍一个非常流出的应用层协议--HTTP。

为什么是 HTTP

HTTP 是建立在 TCP 之上的 超文本传输协议,是万维网的数据通信基础。

传统的 HTTP 协议是基于 Request/Response 模式的,一般一次请求会新建一个连接,响应结束会关闭连接。这对于早起服务端贫瘠的资源非常有意义,使其能够尽快释放连接资源,服务更多的用户。

HTTP 能够流行起来,最主要一点还是因为它对于用户而言,使用起来简单方便。

Go 语言的标准库提供了 HTTP 完整的支持,这点比很多语言好太多,你可以不使用任何第三方库或者框架就能轻松写出一个的 HTTP Server 或者 HTTP Client。

HTTP Server

下面我们就来看一个最简单的例子。

// http/server/example01.go

package main

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

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w,
			"Hello %s, current time is %s \n",
			r.URL.Query().Get("name"),
			time.Now().Format(time.RFC3339))
	})

	log.Fatal(http.ListenAndServe(":3000", nil))
}

本示例中我主要使用 Go 的 http 包新建并在 3000 端口启动一个 HTTP Server,然手我向这个 Server 注册一个 path 为 / 的处理函数。

处理函数逻辑为读取 HTTP Request 中的 name Query 参数,然后向 HTTP Response 的 Body 打印输出获取的 name 和当前时间。运行效果如下:

$ curl http://localhost:3000?name=songjiayang
Hello songjiayang, current time is 2020-08-01T21:57:39+08:00 

代码说明:

  • ListenAndServe(addr string, handler Handler) error 该方法可以新建和启动一个 HTTP Server,它主要包括两个参数,第一个是监听地址,第二个是 http.Handler。 如果 handler 为 nil, 那么会使用 http.DefaultServeMux 这个 默认 Handler。
  • HandleFunc(pattern string, handler func(ResponseWriter, *Request)) 该方法就是向 http.DefaultServeMux 这个 默认 Handler 注册对应的处理逻辑,而默认的 DefaultServeMux 内部其实是使用一个 map 来存储这些 path 的注册的,不支持正则。
配置 Http Server

上面的例子中我们使用 http.ListenAndServe 直接完成了 Server 的创建和监听,但是如果我们想做一些配置,例如 读写超时时间、TLS ,那么就需要将 Server 的创建和 Listen 分开,代码如下:

// http/server/example02.go

package main

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

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w,
			"Hello %s, current time is %s \n",
			r.URL.Query().Get("name"),
			time.Now().Format(time.RFC3339))
	})

	server := &http.Server{
		Addr: ":3000",
		Handler: nil, 
		TLSConfig: nil, 
		ReadTimeout: 0, 
		ReadHeaderTimeout: 0, 
		WriteTimeout: 0,
		IdleTimeout: 0,
		// 其它字段 ..
	}

	log.Fatal(server.ListenAndServe())
}

字段解读:

  • Handler: 具体的 handler,如果为空则使用默认的 http.DefaultServeMux
  • TLSConfig: TLS 相关的配置,如果不为空,那么使用 TLS 监听
  • ReadTimeout: 整个 http request 的读超时时间,包括 header 和 body,如果为 0 表示永远不超时
  • ReadHeaderTimeout: http request header 读取超时时间设置,如果为 0 表示永远不超时
  • WriteTimeout: http response 写超时时间,如果为 0 表示永远不超时
  • IdleTimeout: keep alive 的 http 连接空闲超时时间,如果为空 0 表示永远不超时
自定义 http.Handler

前面的例子我们使用的都是 http.DefaultServeMux 这个 http 包默认的 Handler,那么我们如何实现一个属于自己的 Handler 呢?

http 包中的 Handler 是一个接口,定义如下:

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

所以只要实现该接口即可,下面我们就编写一个自己的 Handler 来重写上面的代码逻辑:

// http/server/example03.go

package main

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

func main() {
	handler := NewMyHandler()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w,
			"Hello %s, current time is %s \n",
			r.URL.Query().Get("name"),
			time.Now().Format(time.RFC3339))
	})

	handler.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "Pong\n")
	})

	log.Fatal(http.ListenAndServe(":3000", handler))
}

type MyHandler struct {
	mux    sync.RWMutex
	router map[string]HandFunc
}

type HandFunc func(w http.ResponseWriter, r *http.Request)

func NewMyHandler() *MyHandler {
	return &MyHandler{
		router: make(map[string]HandFunc, 8),
	}
}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.mux.RLock()
	hf, ok := h.router[r.URL.Path]
	h.mux.RUnlock()

	// if not found
	if !ok {
		http.NotFound(w, r)
		return
	}

	hf(w, r)
}

func (h *MyHandler) HandleFunc(pattern string, hf HandFunc) {
	h.mux.Lock()
	h.router[pattern] = hf
	h.mux.Unlock()
}

代码运行结果如下:

$ curl http://localhost:3000?name=songjiayang
Hello songjiayang, current time is 2020-08-01T22:59:28+08:00 

curl http://localhost:3000/ping
Pong

HTTP Client