目录:
一、HTTP服务器:
- 创建路由器
- 设置路由规则
- 创建服务器
- 监听端口并提供服务
二、HTTP客户端:
- 创建连接池
- 创建客户端
- 请求资源(简单请求、携带header请求、携带body请求)
- 读取内容
三、http服务器源码分析:
- mux为何能作为http.Server的Handler参数?
- 自定义路由器
三、http客户端transport分析:
- http超时控制
- RoundTrip流程
一、HTTP服务器
var addr string = "172.?.?.249:8888"
func update(rw http.ResponseWriter, req *http.Request) {
// 1. 如何body不是json数据结构
if !strings.Contains(req.Header.Get("Content-Type"), "json") {
rw.Write([]byte("无效的数据格式"))
return
}
// 2. 处理json字符串
var accept = make(map[string]interface{}, 5)
bytes, _ := ioutil.ReadAll(req.Body)
json.Unmarshal(bytes, &accept)
rw.Write([]byte(fmt.Sprintf("update session: %v", accept["session"])))
}
func hello(rw http.ResponseWriter, req *http.Request) {
rw.Write([]byte("welcome"))
}
func main() {
// 1. 创建路由器
mux := http.NewServeMux()
// 2. 设置路由规则
mux.HandleFunc("/hello", hello)
mux.HandleFunc("/update", update)
// 3. 创建服务器
server := &http.Server{
Addr: addr,
WriteTimeout: time.Second * 3,
Handler: mux,
}
log.Printf("listen on %v\n", addr)
// 4. 监听端口并提供服务
log.Fatal(server.ListenAndServe())
}
二、HTTP客户端
func main() {
// 1. 创建连接池
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, // 连接超时
KeepAlive: 30 * time.Second, // 长连接超时
}).DialContext,
MaxIdleConns: 100, // 最大空闲连接
IdleConnTimeout: 90 * time.Second, // 空闲连接超时
TLSHandshakeTimeout: 10 * time.Second, // tls握手时间
ExpectContinueTimeout: 1 * time.Second, // 100-continue状态码超时时间
}
// 2. 创建客户端
client := &http.Client{
Timeout: time.Second * 30, // 请求超时时间
Transport: transport,
}
// 3. 普通请求
resp1, err := client.Get("http://172.?.?.249:8888/hello")
if err != nil {
log.Fatal(err)
}
defer resp1.Body.Close()
bytes1, _ := ioutil.ReadAll(resp1.Body)
fmt.Println("simple request:", string(bytes1))
// 4. 复杂请求(携带header和body)
req, _ := http.NewRequest("GET", "http://172.?.?.249:8888/update", strings.NewReader(`{"session": "9527"}`))
req.Header.Add("Content-Type", "application/json;charset=utf-8")
resp2, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp2.Body.Close()
bytes, _ := ioutil.ReadAll(resp2.Body)
fmt.Println(string(bytes))
}
三、http服务器源码分析
3.1 mux为何能作为http.Server的Handler参数?
先看http.Handler,是一个接口方法,实现该接口的所有方法即可套用
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
再看http.ServeMux代码,里面实现了一个ServerHTTP方法,所以http.ServeMux可以当做http.Handler参数
func (mux *ServeMux) match(path string) (h Handler, pattern string) {}
func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) {}
func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {}
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {}
func (mux *ServeMux) Handle(pattern string, handler Handler) {}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {}
3.2 自定义路由器
根据以上机制实现一个简单的路由器
- 实现ServerHTTP方法
- 将对象传入即可
var addr string = "172.?.?.249:8888"
type Handler func(http.ResponseWriter, *http.Request)
type MyMux struct {
Func map[string]Handler
}
// SetFunc 设置路由函数
func (m *MyMux) SetFunc(uri string, handler func(rw http.ResponseWriter, req *http.Request)) {
if m.Func == nil {
m.Func = make(map[string]Handler, 5)
}
m.Func[uri] = handler
}
func (m *MyMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
for uri, def := range m.Func {
if req.RequestURI == uri {
def(rw, req)
return
}
}
notFound(rw, req)
}
func notFound(rw http.ResponseWriter, req *http.Request) {
rw.Write([]byte("no found"))
}
func hello(rw http.ResponseWriter, req *http.Request) {
rw.Write([]byte("welcome2"))
}
func main() {
// 1. 创建路由器
mymux := &MyMux{}
// 2. 注册路由
mymux.SetFunc("/hello", hello)
// 3. 创建服务器
server := &http.Server{
Addr: addr,
WriteTimeout: time.Second * 3,
Handler: mymux,
}
log.Printf("listen on %v\n", addr)
// 4. 监听端口并提供服务
log.Fatal(server.ListenAndServe())
}
四、Transport讲解
4.1 HTTP超时控制
func main() {
// 1. 创建连接池
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second, // 连接超时
KeepAlive: 30 * time.Second, // 长连接超时
}).DialContext,
MaxIdleConns: 100, // 最大空闲连接
IdleConnTimeout: 90 * time.Second, // 空闲连接超时
TLSHandshakeTimeout: 10 * time.Second, // tls握手时间
ExpectContinueTimeout: 1 * time.Second, // 100-continue状态码超时时间
}
// 2. 创建客户端
client := &http.Client{
Timeout: time.Second * 30, // 请求超时时间
Transport: transport,
}
4.2 RoundTrip流程