Go HTTP模块处理流程简析
Go语言提供完善的net/http包,用户使用起来非常方便简单,只需几行代码就可以搭建一个简易的Web服务,可以对Web路由、静态文件、cookie等数据进行操作。
一个使用http包建立的Web服务
package main
import (
"fmt"
"log"
"net/http"
)
func RequestHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", RequestHandler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
核心代码代码如下,下面对它们进行解析
// 用户自定义HTTP路由句柄
func RequestHandler(w http.ResponseWriter, r *http.Request)
// Multiplexer路由注册
http.HandleFunc("/", RequestHandler)
// 服务器监听本地地址,并循环处理HTTP请求
http.ListenAndServe(":8080", nil)
创建ServeMux路由handler
func RequestHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
这个是用户自定义的函数,结合第二行代码看,作为http.HandleFunc函数调用的第二个参数,因为http.HandleFunc第二个参数类型是func(ResponseWriter, *Request),所以本函数定义带有(ResponseWriter, *Request)参数列表,无返回值。第一个参数http.ResponseWriter是一个接口,用于对一次HTTP请求做响应处理,第二个参数http.Request是一次HTTP请求实例,后面再做详细分析。本函数本质上是一个http请求处理的回调函数,是由http包框架开放出,用户可以自定义处理函数。
ServeMux路由表结构
Go默认路由表结构为ServerMux
// ServeMux结构是一个HTTP请求多路复用器(Multiplexer)
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry // ServeMux路由表
hosts bool // whether any patterns contain hostnames
}
// 路由表项
type muxEntry struct {
h Handler // 请求处理handler
pattern string // 请求路径
}
ServeMux路由注册过程过程
// 往系统默认的ServerMux中添加一条路由
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
// 系统默认的Multiplexer:ServeMux
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
// 将用户定义的handler方法,强转为HandlerFunc类型,即实现Handler接口,
// 后面直接通过f.ServeHTTP(w, r)实现对用户注册的handler的调用,f类型为HandlerFunc
mux.Handle(pattern, HandlerFunc(handler))
}
// HandlerFunc类型
type HandlerFunc func(ResponseWriter, *Request)
// HandlerFunc类型实现了Handler接口中ServeHTTP方法
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
// 往ServeMux路由表(mux.m,map结构)中添加路由
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
// 向路由表中新增路由表项
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
// 如果不是以'/'开头,则从host开始匹配
if pattern[0] != '/' {
mux.hosts = true
}
}
ServeMux路由匹配过程
// 见net/http/server.go中func (c *conn) serve(ctx context.Context) 1847行(Go SDK 1.11.5)
// 这里就完成对用户注册的路由handler调用
serverHandler{c.server}.ServeHTTP(w, w.req)
// serverHandler类型
type serverHandler struct {
srv *Server
}
// serverHandler也实现了Handler接口中ServeHTTP方法
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
// http.ListenAndServe(":8080", nil)中第二个参数为nil,表示使用系统默认Multiplexer:DefaultServeMux,
// 在这里就启用DefaultServeMux,可以查看Server结构对Handler成员注释说明(handler to invoke, http.DefaultServeMux if nil)
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
// 调用ServeMux实现Handler接口中ServeHTTP方法
handler.ServeHTTP(rw, req)
}
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
// 通过Request找到路由
h, _ := mux.Handler(r)
// 到这里就调用用户注册的路由Handler
h.ServeHTTP(w, r)
}
// 进一步跟踪mux.Handler(r)
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// ...
return mux.handler(host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// 通过路径匹配路由
if mux.hosts {
h, pattguizeern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
// Check for longest valid match.
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
continue
}
// 匹配时选择匹配度最高的路由(长匹配优先于短匹配)
if h == nil || len(k) > n {
n = len(k)
h = v.h
pattern = v.pattern
}
}
return
}
// 如果pattern不是以'/'结尾,则需完全匹配
// 如果pattern是以'/'结尾,并且pattern(/tree/)是path(/tree/xxx)的前缀子串,
// 则path(/tree/xxx)属于pattern(/tree/*)路由子集下,后面通过match中for语句,基于长匹配优先于短匹配原则,完全匹配路由
func pathMatch(pattern, path string) bool {
if len(pattern) == 0 {
// should not happen
return false
}
n := len(pattern)
if pattern[n-1] != '/' {
return pattern == path
}
return len(path) >= n && path[0:n] == pattern
}
ServeMux路由匹配规则
对于每个HTTP请求,ServeHTTP会对URL进行模式(pattern)匹配,然后调用注册在此pattern下的handler来处理当前请求。
1、如果pattern以'/'开头,表示匹配URL的路径部分,如果不是以'/'开头,表示从host开始匹配;
2、模式匹配时,以匹配度最高为原则(长匹配优先于短匹配);
3、如果pattern(/tree/)以'/'结尾,将会对不带'/'的URL(/tree)进行301重定向到'/tree/'上,除非单独为'/tree'模式注册handler;
4、如果pattern(/tree)注册了handler,当请求的路径为'/tree/'时,则无法匹配该模式;
**实现Handler接口**
Handler结构定义如下
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
net/http/server.go中实现Handler接口有以下方法
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)}
func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request)
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request)
func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request)
func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request)
func (h initNPNRequest) ServeHTTP(rw ResponseWriter, req *Request)
Go HTTP模块处理流程简析的更多相关文章
- zxing二维码扫描的流程简析(Android版)
目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...
- OpenStack Cinder源代码流程简析
版权声明:本博客欢迎转载,转载时请以超链接形式标明文章原始出处!谢谢! 博客地址:http://blog.csdn.net/i_chips 一.概况 OpenStack的各个模块都有对应的client ...
- React Native 启动流程简析
导读:本文以 react-native-cli 创建的示例工程(安卓部分)为例,分析 React Native 的启动流程. 工程创建步骤可以参考官网.本文所分析 React Native 版本为 v ...
- LinkedHashMap结构get和put源码流程简析及LRU应用
原理这篇讲得比较透彻Java集合之LinkedHashMap. 本文属于源码阅读笔记,因put,get调用逻辑及链表维护逻辑复杂(至少网上其它文章的逻辑描述及配图,我都没看明白LinkedHashMa ...
- jquery选择器的实现流程简析及提高性能建议!
当我们洋洋得意的使用jquery强大的选择器功能时有没有在意过jquery的选择性能问题呢,其实要想高效的使用jquery选择器,了解其实现流程是很有必要的,那么这篇文章我就简单的讲讲其实现流程,相信 ...
- Tomcat启动流程简析
Tomcat是一款我们平时开发过程中最常用到的Servlet容器.本系列博客会记录Tomcat的整体架构.主要组件.IO线程模型.请求在Tomcat内部的流转过程以及一些Tomcat调优的相关知识. ...
- 【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析
前言 学习类加载必然离开不了sun.misc.Launcher这个类和Class.forName()这个方法. 分析ClassLoader.getSystemClassLoader()这个流程可以明白 ...
- HTTPS及流程简析
[序] 在我们在浏览某些网站的时候,有时候浏览器提示需要安装根证书,可是为什么浏览器会提示呢?估计一部分人想也没想就直接安装了,不求甚解不好吗? 那么什么是根证书呢?在大概的囫囵吞枣式的百度之后知道了 ...
- Postfix 发送邮件流程简析
PostFix接受和转发邮件的说明 来源ip符合inet_interfaces,收件人域符合mydestination, Postfix将接收到本地. 来源ip符合inet_interfaces, ...
随机推荐
- POJ 3177——Redundant Paths——————【加边形成边双连通图】
Redundant Paths Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Sub ...
- Day4下午
不会啊. T1 找规律: 辗转相减,加速. #include<iostream> #include<cstring> #include<cstdio> #inclu ...
- xml schema数据类型
1.简单数据类型 (1)内置简单数据类型 schema中定义了一些简单数据类型,包括primitive原始数据类型和derived派生数据类型,这些类型都是schema中使用的 最基本的数据类型,我们 ...
- js删除数组里指定的元素
js删除数组里指定的元素 首先可以给JS的数组对象定义一个函数,用于查找指定的元素在数组中的位置,即索引,代码为: Array.prototype.indexOf = function(val) { ...
- Python元组类型、字典类型及常用操作
一.元组类型 1.用途 记录多个值,当多个值没有改的需求,此时用元组更合适,Python的元组与列表类似,不同之处在于元组的元素不能修改. 2.定义方式 在()内用逗号分隔开多个任意类型的值 t=(1 ...
- scss的使用方式(环境搭建)
我用的是Koala. IDE是intellij_idea(其他IDE也可) 下载Koala:http://koala-app.com/ 2.安装(选好位置,下一步即可) 3.打开Koala,创建项目 ...
- URL地址中中文乱码详解(javascript中encodeURI和decodeURI方法、java.net.URLDecoder.encode、java.net.URLDecoder.decode)
引言: 在Restful类的服务设计中,经常会碰到需要在URL地址中使用中文作为的参数的情况,这种情况下,一般都需要正确的设置和编码中文字符信息.乱码问题就此产生了,该如何解决呢?且听本文详细道来. ...
- Docker build 安装报错, Could not open requirments file: [Errno 2] No such file or directory:'requirements.txt'
docker安装教程 https://docs.docker.com/get-started/part2/#build-the-app 相关帖子 https://stackoverflow.com/q ...
- May 7th 2017 Week 19th Sunday
A chain is no stronger than its weakest link. 链条的坚固程度取决于它最薄弱的环节. The same as the well-known buckets ...
- May 1 2017 Week 18 Monday
The very essence of romance is uncertainty. 浪漫的精髓就在于它充满了种种可能. Yesterday my girl friend told me that ...