GO语言web开发的几种Mux库阅读

2024-02-03 02:58

本文主要是介绍GO语言web开发的几种Mux库阅读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DefaultServeMux阅读

可以查看到ServeMux结构的定义如下:

type ServeMux struct {mu    sync.RWMutexm     map[string]muxEntryhosts bool 
}type muxEntry struct {explicit boolh        Handlerpattern  string
}

ServeMux结构中最重要的字段为map[string]muxEntry类型的m,这是一个map,key为表示url模式的string,value则是muxEntry结构。可以看到muxEntry结构中存储了表示具体的url模式的string和handler,以及是否explicit的bool类型。由于ServeMux也实现了ServeHTTP函数,其也可以看成是一个handler接口,与之前ServeHTTP函数处理http请求并产生http响应不同,ServeMux的ServeHTTP方法是用来找到路由对应的handler的。

ServeMux的任务是将用户请求中 path 映射到 Handler,由于其可能会被并发访问,因此其还有一个子段mu作为sync.RWMutex类型,也就是读写互斥锁,其保证不同go程对该线程不安全变量的互斥访问。

可以查看到ServeMux的Match函数如下:

// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
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.  mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, ""
}

可以看到,其首先检查mux.m中是否含有path对应的精确匹配,如果有的话就直接返回对应的handler和pattern。否则则遍历所有的patterns,由于patterns首先按照长度从大到小排序,因此其会找到具有最长匹配前缀的pattern并将其和对应的handler返回。这相比过去版本的match有了优化,可以更快地找到第一个具有最长匹配的pattern。

而Handle函数可以用于注册路由,可以看到其实现如下:

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
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)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}
}

正如之前所述,由于ServeMux不是线程安全的,因此Handle函数首先将ServerMux加上互斥锁,并在函数处理完后解开互斥锁。之后其检查pattern和handler是否是合法且是没有注册过的,如果是的话则将其加入到mux.m这个map中(如果还没有申请mux.m就创建)。之后按照长度的顺序将其插入到es中,使得插入后es仍然有序。

然后可以查看到handler方法:

// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
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
}

同样,其首先上互斥锁,然后调用match检查是否存在host+path路径,如果有就返回对应的h和pattern,否则再调用match检查是否存在path路径,如果有也返回对应的h和pattern。否则就进入NotFoundHandler的处理。

// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }

NotFoundHandler如上吗,将NotFound作为参数,使用HandlerFunc函数处理,也就是处理了404错误的情况。

经过上面的分析,可以看到DefaultServeMux实现了对用户请求url进行路由的功能。可以看到,DefaultServeMux支持url的精确匹配和最长前缀匹配。但由于路由的时候还需要一条条检查所有项目来匹配,因此在路由条目比较多的时候可能还是比较慢。为了加快速度,可以像其他一些后端路由一样采取基于Trie前缀树(用/分隔)等层次匹配的算法,可以使得最长查找次数为树的高度。此外,DefaultServeMux也不支持Method,header,host等信息匹配以及正则表达式的url匹配。

gorilla/mux阅读

进一步对比阅读gorilla/mux代码。可以看到gorilla/mux比DefaultServeMux实现了更强大的功能,包括正则表达式匹配以及Method,header,host等信息的匹配,利用gorilla/mux可以更方便地开发server程序。

可以看到其定义Route如下,Route结构存储了匹配请求的handler和name等结构其还有着map[string]类型指针,指向全局索引所有命名Routes的结构。

// Route stores information to match a request and build URLs.
type Route struct {// Request handler for the route.handler http.Handler// If true, this route never matches: it is only used to build URLs.buildOnly bool// The name used to build URLs.name string// Error resulted from building a route.err error// "global" reference to all named routesnamedRoutes map[string]*Route// config possibly passed in from `Router`routeConf
}

Router的代码则如下:

type Router struct {// Configurable Handler to be used when no route matches.NotFoundHandler http.Handler// Configurable Handler to be used when the request method does not match the route.MethodNotAllowedHandler http.Handler// Routes to be matched, in order.routes []*Route// Routes by name for URL building.namedRoutes map[string]*Route// If true, do not clear the request context after handling the request.//// Deprecated: No effect, since the context is stored on the request itself.KeepContext bool// Slice of middlewares to be called after a match is foundmiddlewares []middleware// configuration shared with `Route`routeConf
}

可以看到,其包括特定的NotFoundHandler 和MethodNotAllowedHandler 字段来处理页面没有找到,和方法不允许的通常情况,这两个handler也是http.handler类型。而其存储路由条目则是用一个[]*Route存储,同时还有一个map[string]*Route类型根据名称索引到对应路由。

其Match函数与net/http包中的不同,其首先遍历所有routes,然后调用route.Match看能否匹配,如果可以匹配就检查如果在没有错误的情况下,添加各个中间件作为回调函数链。这样可以支持服务端通过中间件实现更强大的路由功能。之后其检查方法如果不匹配的话,就用MethodNotAllowedHandler进行处理,最后检查如果是没有找到页面的话,就用NotFoundHandler 进行处理。

func (r *Router) Match(req *http.Request, match *RouteMatch) bool {for _, route := range r.routes {if route.Match(req, match) {// Build middleware chain if no error was foundif match.MatchErr == nil {for i := len(r.middlewares) - 1; i >= 0; i-- {match.Handler = r.middlewares[i].Middleware(match.Handler)}}return true}}if match.MatchErr == ErrMethodMismatch {if r.MethodNotAllowedHandler != nil {match.Handler = r.MethodNotAllowedHandlerreturn true}return false}// Closest match for a router (includes sub-routers)if r.NotFoundHandler != nil {match.Handler = r.NotFoundHandlermatch.MatchErr = ErrNotFoundreturn true}match.MatchErr = ErrNotFoundreturn false
}

进一步查看route.Match函数的代码如下:

// Match matches the route against the request.
func (r *Route) Match(req *http.Request, match *RouteMatch) bool {if r.buildOnly || r.err != nil {return false}var matchErr error// Match everything.for _, m := range r.matchers {if matched := m.Match(req, match); !matched {if _, ok := m.(methodMatcher); ok {matchErr = ErrMethodMismatchcontinue}// Ignore ErrNotFound errors. These errors arise from match call// to Subrouters.//// This prevents subsequent matching subrouters from failing to// run middleware. If not ignored, the middleware would see a// non-nil MatchErr and be skipped, even when there was a// matching route.if match.MatchErr == ErrNotFound {match.MatchErr = nil}matchErr = nilreturn false}}if matchErr != nil {match.MatchErr = matchErrreturn false}if match.MatchErr == ErrMethodMismatch && r.handler != nil {// We found a route which matches request method, clear MatchErrmatch.MatchErr = nil// Then override the mis-matched handlermatch.Handler = r.handler}// Yay, we have a match. Let's collect some info about it.if match.Route == nil {match.Route = r}if match.Handler == nil {match.Handler = r.handler}if match.Vars == nil {match.Vars = make(map[string]string)}// Set variables.r.regexp.setMatch(req, match, r)return true
}

可以看到,其首先检查buildOnly和err,在buildOnly为假和err为nil的情况下,遍历r.matchers ,调用m.Match检查是否方法匹配,如果发生错误就进行错误处理,直到找到匹配的match。之后就收集相关信息,设定对应的Route和handler,以及参数Vars,之后通过正则表达式的setMatch匹配相应正则表达式中的参数。

然后可以查看Vars函数如下,其取出http请求相关联的变量的信息。该函数从上下文中获得对应的varsKey,将存放该请求对应的变量值集合的string映射到string的map类型返回。

func Vars(r *http.Request) map[string]string {if rv := r.Context().Value(varsKey); rv != nil {return rv.(map[string]string)}return nil
}

还可以查看handler的实现如下,HandlerFunc调用Route的Handler函数字段对http.HandlerFunc(f)返回的带handler的Route进行处理。而Handler函数则设置了Route的handler并将其返回。

func (r *Route) HandlerFunc(f func(http.ResponseWriter, *http.Request)) *Route {return r.Handler(http.HandlerFunc(f))
}
// Handler sets a handler for the route.
func (r *Route) Handler(handler http.Handler) *Route {if r.err == nil {r.handler = handler}return r
}

查看Router的ServeHTTP函数如下,其处理http的请求,并在http.ResponseWriter类型参数中写入响应报文。其首先在r.skipClean为假的情况下获得请求的url路径,之后在r.useEncodedPath为真的情况下调用EscapedPath()对路径进行处理。之后其清理路径到规范的形式并重定向。之后就检查是否有match和请求相匹配,匹配的话就设定handler为match的Handler字段,以及设置请求包括match的参数,路由等。之后看是否需要用到methodNotAllowedHandler和NotFoundHandler,最终让handler处理请求。

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {if !r.skipClean {path := req.URL.Pathif r.useEncodedPath {path = req.URL.EscapedPath()}// Clean path to canonical form and redirect.if p := cleanPath(path); p != path {// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:// http://code.google.com/p/go/issues/detail?id=5252url := *req.URLurl.Path = pp = url.String()w.Header().Set("Location", p)w.WriteHeader(http.StatusMovedPermanently)return}}var match RouteMatchvar handler http.Handlerif r.Match(req, &match) {handler = match.Handlerreq = requestWithVars(req, match.Vars)req = requestWithRoute(req, match.Route)}if handler == nil && match.MatchErr == ErrMethodMismatch {handler = methodNotAllowedHandler()}if handler == nil {handler = http.NotFoundHandler()}handler.ServeHTTP(w, req)
}

这篇关于GO语言web开发的几种Mux库阅读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/672791

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验