【Go入门】 Go的http包详解
【Go入门】 Go的http包详解
前面小节介绍了Go怎么样实现了Web工作模式的一个流程,这一小节,我们将详细地解剖一下http包,看它到底是怎样实现整个过程的。
Go的http有两个核心功能:Conn、ServeMux
Conn的goroutine
与我们一般编写的http服务器不同, Go为了实现高并发和高性能, 使用了goroutines来处理Conn的读写事件, 这样每个请求都能保持独立,相互不会阻塞,可以高效的响应网络事件。这是Go高效的保证。
Go在等待客户端请求里面是这样写的:
c, err := srv.newConn(rw)
if err != nil {continue
}
go c.serve()
这里我们可以看到客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到对应的handler,该handler中便可以读取到相应的header信息,这样保证了每个请求的独立性。
ServeMux的自定义
我们前面小节讲述conn.server的时候,其实内部是调用了http包默认的路由器,通过路由器把本次请求的信息传递到了后端的处理函数。那么这个路由器是怎么实现的呢?
它的结构如下:
type ServeMux struct {mu sync.RWMutex //锁,由于请求涉及到并发处理,因此这里需要一个锁机制m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式hosts bool // 是否在任意的规则中带有host信息
}
下面看一下muxEntry
type muxEntry struct {explicit bool // 是否精确匹配h Handler // 这个路由表达式对应哪个handlerpattern string //匹配字符串
}
接着看一下Handler的定义
type Handler interface {ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
Handler是一个接口,但是前一小节中的sayhelloName函数并没有实现ServeHTTP这个接口,为什么能添加呢?原来在http包里面还定义了一个类型HandlerFunc,我们定义的函数sayhelloName就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个接口,即我们调用了HandlerFunc(f),强制类型转换f成为HandlerFunc类型,这样f就拥有了ServeHTTP方法。
type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)
}
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?请看下面的代码,默认的路由器实现了ServeHTTP:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {if r.RequestURI == "*" {w.Header().Set("Connection", "close")w.WriteHeader(StatusBadRequest)return}h, _ := mux.Handler(r)h.ServeHTTP(w, r)
}
如上所示路由器接收到请求之后,如果是*那么关闭链接,不然调用mux.Handler(r)返回对应设置路由的处理Handler,然后执行h.ServeHTTP(w, r)
也就是调用对应路由的handler的ServerHTTP接口,那么mux.Handler®怎么处理的呢?
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {if r.Method != "CONNECT" {if p := cleanPath(r.URL.Path); p != r.URL.Path {_, pattern = mux.handler(r.Host, p)return RedirectHandler(p, StatusMovedPermanently), pattern}} return mux.handler(r.Host, r.URL.Path)
}func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()// Host-specific pattern takes precedence over generic onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return
}
原来他是根据用户请求的URL和路由器里面存储的map去匹配的,当匹配到之后返回存储的handler,调用这个handler的ServeHTTP接口就可以执行到相应的函数了。
通过上面这个介绍,我们了解了整个路由过程,Go其实支持外部实现的路由器 ListenAndServe的第二个参数就是用以配置外部路由器的,它是一个Handler接口,即外部路由器只要实现了Handler接口就可以,我们可以在自己实现的路由器的ServeHTTP里面实现自定义路由功能。
如下代码所示,我们自己实现了一个简易的路由器
package mainimport ("fmt""net/http"
)type MyMux struct {
}func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {if r.URL.Path == "/" {sayhelloName(w, r)return}http.NotFound(w, r)return
}func sayhelloName(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello myroute!")
}func main() {mux := &MyMux{}http.ListenAndServe(":9090", mux)
}
Go代码的执行流程
通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。
-
首先调用Http.HandleFunc
按顺序做了几件事:
1 调用了DefaultServeMux的HandleFunc
2 调用了DefaultServeMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
-
其次调用http.ListenAndServe(“:9090”, nil)
按顺序做了几件事情:
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen(“tcp”, addr)监听端口
4 启动一个for循环,在循环体中Accept请求
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
8 调用handler的ServeHttp
9 在这个例子中,下面就进入到DefaultServeMux.ServeHttp
10 根据request选择handler,并且进入到这个handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)11 选择handler:
A 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry)
B 如果有路由满足,调用这个路由handler的ServeHTTP
C 如果没有路由满足,调用NotFoundHandler的ServeHTTP
相关文章:
【Go入门】 Go的http包详解
【Go入门】 Go的http包详解 前面小节介绍了Go怎么样实现了Web工作模式的一个流程,这一小节,我们将详细地解剖一下http包,看它到底是怎样实现整个过程的。 Go的http有两个核心功能:Conn、ServeMux Conn的goroutine 与我们一般编…...
解决k8s node节点报错: Failed to watch *v1.Secret: unknown
现象: 这个现象是发生在k8s集群证书过期,重新续签证书以后。 记得master节点的/etc/kubernetes/kubelet.conf文件已经复制到node节点了。 但是为什么还是报这个错,然后运行证书检查命令看一下: 看样子是差/etc/kubernetes/pki/…...
日志维护库:loguru
在复杂的项目中,了解程序的运行状态变得至关重要。在这个过程中,日志记录(logging)成为我们追踪、调试和了解代码执行的不可或缺的工具。在python语言中常用logging日志库,但是logging日志库使用相对繁琐,在…...
【Go入门】 Go如何使得Web工作
【Go入门】 Go如何使得Web工作 前面小节介绍了如何通过Go搭建一个Web服务,我们可以看到简单应用一个net/http包就方便的搭建起来了。那么Go在底层到底是怎么做的呢?万变不离其宗,Go的Web服务工作也离不开我们第一小节介绍的Web工作方式。 w…...
汽车虚拟仿真视频数据理解--CLIP模型原理
CLIP模型原理 CLIP的全称是Contrastive Language-Image Pre-Training,中文是对比语言-图像预训练,是一个预训练模型,简称为CLIP。该模型是 OpenAI 在 2021 年发布的,最初用于匹配图像和文本的预训练神经网络模型,这个任…...
【Web】Ctfshow SSTI刷题记录1
目录 ①web361 362-无过滤 ②web363-过滤单双引号 ③web364-过滤单双引号和args ④web365-过滤中括号[]、单双引号、args ⑤web366-过滤单双引号、args、中括号[]、下划线 ⑦web367-过滤单双引号、args、中括号[]、下划线、os ⑧web368-过滤单双引号、args、中括号[]、下…...
【广州华锐互动】VR可视化政务服务为公众提供更直观、形象的政策解读
虚拟现实(VR)技术正在逐渐应用于政务服务领域,为公众提供更加便捷、高效和个性化的服务体验。通过VR眼镜、手机等设备,公众可以在虚拟环境中参观政务服务中心,并根据自己的需求选择不同的办事窗口或事项进行咨询和办理…...
音视频项目—基于FFmpeg和SDL的音视频播放器解析(七)
介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本…...
Sql Server 2017主从配置之:发布订阅
使用发布订阅模式搭建Sql Server 2017主从同步,类似事件通知机制,基本可以做到准实时同步,可以同时做到一对多的数据同步。 不过发布订阅模式,只能同时数据,不能同步表结构。在创建发布的时候,需要选择需要…...
聊聊logback的EvaluatorFilter
序 本文主要研究一下logback的EvaluatorFilter EvaluatorFilter ch/qos/logback/core/filter/EvaluatorFilter.java public class EvaluatorFilter<E> extends AbstractMatcherFilter<E> {EventEvaluator<E> evaluator;Overridepublic void start() {if …...
解决vue 部分页面缓存,部分页面不缓存的问题
前端时间项目迭代,其中有个需求 在vue里面,有a.b.c三个页面,要达到的效果是从a页面进去b页面,b页面需要刷新,但若从b页面进入c页面了以后再回到b页面,b页面需要保留之前的值,不做刷新࿱…...
修完这个 Bug 后,MySQL 性能提升了 300%
最近 MySQL 官方在 8.0.35 上修复了一个 bug: 这个 bug 是由 Mark Callaghan 发现的。Mark 早年在 Google MySQL 团队,后来去了 Meta MySQL,也主导了 RocksDB 的开发。 Mark 在 #109595 的 bug report 给出了非常详细的复现步骤 在官方修复后…...
【C/PTA】数组进阶练习(二)
本文结合PTA专项练习带领读者掌握数组,刷题为主注释为辅,在代码中理解思路,其它不做过多叙述。 目录 7-1 字符串逆序7-2 字符串替换7-3 统计字符出现次数7-4 IP地址转换7-1 删除重复字符7-2 说反话-加强版7-3 数组-回文串7-4 数组-无聊的菇菇…...
Mysql MMM
MMM概述 MMM(Master-Master replication manager for MvSQL,MySQL主主复制管理器) 是一套支持双主故障切换和双主日常管理的脚本程序。 MMM 使用 Perl 语言开发,主要用来监控和管理MySQL Master-Master(双主)复制&…...
GDPU 数据结构 天码行空10
目录 数据结构实验十 树遍历应用一、【实验目的】二、【实验内容】三、【实验源代码】⭐ CPP版⭐ c语言版 四、实验结果 数据结构实验十 树遍历应用 一、【实验目的】 1、了解树的建立方法 2、掌握树与二叉树的转化及其遍历的基本方法 3、掌握递归二叉树遍历算法的应用 二、…...
CD36 ; + Lectin;
CD2 LIMP-2, LGP85 SR-BI, CD36; 清道夫受体蛋白CD36超家族的成员是 脂质代谢 和 先天免疫 的重要调节因子。它们识别正常和修饰的脂蛋白,以及与病原体相关的分子模式。 该家族由三个成员组成: SR-BI &am…...
Git 分支管理
目录 列出分支 删除分支 分支合并 合并冲突 几乎每一种版本控制系统都以某种形式支持分支,一个分支代表一条独立的开发线。 使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。 Git 分支实际上是指向更改快照的指针。 有…...
Vue23全局事件总线
Vue2&3全局事件总线 Vue2全局事件总线 功能:可以解决所有组件之间通信传数据的问题原理:通过一个共享对象,将所有组件全部绑定到对象上,即可通过这个对象实现组件与组件之间的传递数据,而这个共享对象叫做全局事件…...
GEM5 Garnet DVFS / NoC DVFS教程:ruby.clk_domain ruby.voltage_domain
简介 gem5中的 NoC部分是Garnet实现的,但是Garnet并没有单独的时钟域,而是保持ruby一致,要做noc的DVFS,便是要改ruby的 改电压 #这里只是生成一个随便变量名,存一下值。改是和频率一起的 userssaved_voltage_domain…...
java命令 jmap 堆参数分析
jmap -heap pid 展示pid的整体堆信息 bash-4.4# jmap -heap 10 Attaching to process ID 10, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.172-b11using thread-local object allocation. Garbage-First (G1) GC with 8 th…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
