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

news/2025/1/12 19:50:19/

正文:

 Golang http 包详解(源码剖析)

前面小节我们认识了 Web 的工作方式,也成功用 Go 搭建了一个最简单的 Web 服务了解了 Golang 运行 Web 的原理。现在我们详细地去解剖以下 http 包,看看它如何实现整个过程的

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

 Conn 的 goroutine

与我们使用其他语言编写 http 服务器不同, Go为了实现高并发和高性能,使用了 goroutines 来处理 Conn 的读写事件。这样让每个请求都能保持独立,相互不会阻塞,可以高效地响应网络事件,这是 Go 高效的保证。

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

点击查看代码

 

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

 ServeMux 的自定义

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

结构如下:

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

    image

  • 路由规则中一个 string 对应一个 mux 实体,我们来看看 muxEntry 它也是一个自定义类型结构体,包含一个 布尔值,一个Handler 处理函数

    image

  • 最后再来看看 Handler 的定义,它其实是一个接口,实现了 ServeHTTP 这个函数

    image

这个时候我们可以回过头来看我们之前自己写的 Web 服务器

点击查看代码

 

调用: `http.HandleFunc("/", sayhelloName) // 设置访问的路由`

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

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

image

路由器里存储好了相应的路由规则(Response / Request)之后,那么具体的请求又是怎么分发的呢?
路由器接收到请求之后调用 mux.handler(r).ServeHTTP(w,r)
也就是调用对应路由的 handler 的 ServerHTTP 接口,让我们来看看
mux.handler(r)是怎么处理的↓

image

我们可以看到它是根据用户请求的 URL 和路由器里面存储的 map 去匹配的,当匹配到之后返回存储的 handler,调用这个 handler 的 ServeHTTP 接口就可以执行相应的函数了

通过上面的介绍,我们大致了解了整个构建路由的过程,Go其实支持外部实现的路由器 而 ListenAndServe 的第二个参数就是用来配置外部路由器的,它是一个 Handler 接口,所以我们的外部路由只要实现了 Handler 接口就可以发挥作用,因此我们可以在自己实现的路由器的 ServeHTTP 里面实现自定义的路由功能

贴个代码↓

点击查看代码

 

实现效果:

image

 Go 代码的执行流程

最后我们来梳理一下整个代码的执行过程

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

  2. 调用 Server 的 ListenAndServer()

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

  4. 启动一个 for 循环,在循环题中 Accept 请求

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

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

  7. 判断 handler 是否为空,如果没有就设置 handler(默认设置)

  8. 调用 handler 的ServeHTTP

  9. 进入到 DefaultServerMux.ServeHTTP

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

    image

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

 总结

到这里为止我们从第一章介绍了 HTTP 协议,DNS 解析过程,了解了 Web 的工作方式,第二章分别用 Go 搭建一个最简单的 Web 服务,并且了解 Golang 运行 web 的原理,在最后一章,我们还深入到 net/http 包中的源码里为大家揭开了更底层的原理 


http://www.ppmy.cn/news/1008848.html

相关文章

识jvm堆栈中一个数据类型是否为为引用类型,目前虚拟机实现中是如何做的?

调用栈里的引用类型数据是GC的根集合(root set)的重要组成部分;找出栈上的引用是GC的根枚举(root enumeration)中不可或缺的一环。 要看JVM选择用什么方式。通常这个选择会影响到GC的实现。 如果JVM选择不记录任何这种…

CISCO MDS 9148 SAN Switch 交换机命令配置方法:

前言 CISCO MDS 9148 SAN 交换机已经停产,但还是要掌握一下配置的方法: 升级款后面 9148S 或者 9100系列,但配置方式基本都差不多,掌握一个就好: 高性能和极具吸引力的价值 Cisco MDS 9148S 16G 多层光纤交换机是下…

Android中的SharedPreferences

Android中的SharedPreferences 在Android中,SharedPreferences是一种用于存储轻量级数据的机制。它允许应用程序存储和检索键值对数据,并且对于保存一些简单的配置信息、用户偏好设置或状态信息非常有用。SharedPreferences的数据存储是基于XML文件的&a…

【websocket - Tornado】简易聊天应用

1、背景 项目测试的过程中需要自己搭建一个webscoket站点,确保此类服务接入后台系统后访问不受影响。python的服务框架常用的有Flask、Django、Tornado,每个框架的侧重点不同,导致使用的场景就会有所差异。 Flask轻量级,采用常规的同步编程方式,需要安装其他模块辅助,主…

Palo Alto Networks® PA-220R 下一代防火墙 确保恶劣工况下的网络安全

一、主要安全功能 1、每时每刻在各端口对全部应用进行分类 • 将 App-ID 用于工业协议和应用,例如 Modbus、 DNP3、IEC 60870-5-104、Siemens S7、OSIsoft PI 等。 • 不论采用何种端口、SSL/SSH 加密或者其他规避技术,都会识别应用。 • 使用…

Unity小游戏——迷你拼图

游戏展示 拼图演示 资源: 链接:https://pan.baidu.com/s/1BGeSmRCO_WZRUyl3MxefGw 提取码:0n4a 一、玩法介绍 排列拼图碎片,拼出最后的图案。可以点住碎片的任意位置拖动;点击"重来"按钮,可以…

如何在轻量级RTSP服务支持H.264扩展SEI发送接收自定义数据?

为什么开发轻量级RTSP服务? 开发轻量级RTSP服务的目的是为了解决在某些场景下用户或开发者需要单独部署RTSP或RTMP服务的问题。这种服务的优势主要有以下几点: 便利性:通过轻量级RTSP服务,用户无需配置单独的服务器,…

区块链实验室(16) - FISCO BCOS实验环境

经过多次重复,建立一个FISCO BCOS实验环境。该环境是一个VMWare虚拟机,能够启动FISCO BCOS自创建的4节点区块链,不必下载依赖包即可编译Fisco Bcos目标文件,安装有VsCode1.81版本。 启动4节点的Fisco Bcos区块链 启动控制台 编译…