Go For Web:Golang http 包详解(源码剖析)

科技资讯 投稿 11700 0 评论

Go For Web:Golang http 包详解(源码剖析)

前言:

文章预计分为四个部分逐步更新
2023-04-13 星期四 一更 全文共计约 3800 字 阅读大约花费 5 分钟
2023-04-14 星期五 二更(两篇) 全文共计约 2000 字 阅读大概花费 4 分钟
2023-04-14 星期五 三更 全文共计约 2000 字 阅读大概花费 5 分钟


文章目录:

    Web 的工作方式
  1. 用 Go 搭建一个最简单的 Web 服务
  2. 了解 Golang 运行 web 的原理
  3. Golang http 包详解(源码剖析)

正文:

Golang http 包详解(源码剖析)

Go 的 http 包中有两个核心功能:Conn 、ServeMux

Conn 的 goroutine

根据上一节,我们知道 Go 在等待客户端请求里面是这样写的:

点击查看代码
c, err := srv.newConn(rw
if ree != nil {
	continue
}
go c.serve(

这段代码中,客户端的每一次请求都会创建一个 Conn,这个 Conn 里面保存了这次请求的信息,然后再传递到对应的 handler,该handler中便可以读取到相应的 header 信息,这样保证了每个请求的独立性。

ServeMux 的自定义

conn.server 的时候,其实内部是调用了 http 包默认的路由器也就是DefaultServeMux,通过这个路由器把本次请求的信息传递到了后端的处理函数。那么这个路由器是怎么实现的呢?

    首先是一个 自定义类型结构体 ServeMux 其中包含一个
    和一个 路由规则

  • ServeHTTP 这个函数

点击查看代码
// Handler处理函数
func sayhelloName(w http.ResponseWriter, r *http.Request {
	r.ParseForm( // 解析参数,默认不会解析
	fmt.Println(r.Form// 以下这些信息是输出到服务端的打印信息:请求表单form、路径path、格式scheme
	fmt.Println("path", r.URL.Path
	fmt.Println("scheme", r.URL.Scheme
	fmt.Println(r.Form["url_long"]
	for k, v := range r.Form {
		fmt.Println("key:", k
		fmt.Println("val:", strings.Join(v, ""
	}
	fmt.Fprintln(w, "Hello astaxie!" // 输出到客户端
}
调用: `http.HandleFunc("/", sayhelloName // 设置访问的路由`

我们会发现,我们自己写的 sayhelloName 函数并没有实现 ServeHTTP 这个函数,也就是说按照常理我们并没有实现 Handler 这个接口,那我们是怎么添加的?

HandlerFunc,而我们定义的函数 sayhelloName 就是这个 HandlerFunc 调用之后的结果,这个自定义函数类型默认会实现 ServeHTTP 这个方法,即我们调用了 HandlerFunc(f强制类型转换 f 成为了 HandlerFunc 类型,这样 f 就拥有了ServeHTTP 方法

路由器接收到请求之后调用 mux.handler(r.ServeHTTP(w,r
也就是调用对应路由的 handler 的 ServerHTTP 接口,让我们来看看
mux.handler(r是怎么处理的↓

ServeHTTP 接口就可以执行相应的函数了

ListenAndServe 的第二个参数就是用来配置外部路由器的,它是一个 Handler 接口,所以我们的外部路由只要实现了 Handler 接口就可以发挥作用,因此我们可以在自己实现的路由器的 ServeHTTP 里面实现自定义的路由功能

点击查看代码
package main

import (
	"fmt"
	"net/http"


type MyMux struct {
}

func (p *MyMux ServeHTTP(w http.ResponseWriter, r *http.Request {
	if r.URL.Path == "/" {
		sayhelloName2(w, r
		return
	} else {
		http.NotFound(w, r
		return
	}
}

func sayhelloName2(w http.ResponseWriter, r *http.Request {
	fmt.Fprintf(w, "Hello myroute!"
}

func main( {
	mux := &MyMux{}
	http.ListenAndServe(":9090", mux
}

实现效果:

Go 代码的执行流程

    首先调用 Http.HandleFunc
    按照顺序做了这几件事:
    调用了 DefaultServeMux 的 HandleFunc
  1. 调用了 DefaultServeMux 的 Handler
  2. 往 DefaultServeMux 的 map[string]muxEntry 中增加对应的handler 和 路由规则
    其次调用 http.ListenAndServe(":9090",nil
    按顺序做了这几件事:

    实例化 Server

  1. 调用 net.Listen("tcp", addr监听端口

  2. 对每一个请求实例化一个 Conn,并且开启一个 goroutine 为这个请求开一个 go.c.serve(

  3. 读取每个请求的内容 w, err := c.readRequest(

  4. 调用 handler 的ServeHTTP

  5. 根据 request 选择 handler,并且进去到这个 handler 的 ServerHTTP

  6. 选择 handler
    A 判断是否有路由能满足这个 request (循环遍历 ServerMux 的 muxEntry)
    B 如果满足,则调用这个路由 handler 的 ServeHTTP
    C 如果不满足,则调用 NotFoundHandler 的 ServeHTTP

总结

net/http 包中的源码里为大家揭开了更底层的原理

关于 Golang 基础部分 以及 计算机网络部分读者可以参阅我的往期 blog👇
Goalng:基础复习一遍过

以上

看完记得留下一个👍

编程笔记 » Go For Web:Golang http 包详解(源码剖析)

赞同 (52) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽