Golang HTTP使用方式

Go调用几个常见的大模型基座方法 Go 调用大模型的核心是构造符合平台规范的 JSON 请求体 + 正确的认证方式 + 解析 JSON 响应。优先使用各平台推荐的第三方库(如 OpenAI 的go-openai),可简化开发、减少手动构造请求的错误。不同平台的差异主要在于:认证流程(文心一言需先获取 Access Token)、接口地址、请求/响应结构体字段。原生 HTTP 请求无第三方库依赖,灵活性更高,适合对依赖包有严格限制的场景。 阅读详情
前言

关于Golang HTTP服务器实现原理,本文将通过下面两点来讲述,希望能帮到大家!

  1. 如何创建一个HTTP服务器
  2. HTTP服务器实现原理
如何创建一个HTTP服务器

创建一个 HTTP服务器 的步骤:

  1. 创建一个处理器(实际的业务逻辑)
  2. 创建一个路由器并与处理器进行绑定/注册(这样才能根据URL匹配到对应的函数)
  3. 启动HTTP服务器,并监听指定端口

对于处理器的实现,其实只有两种,一是使用处理器函数实现,二是创建一个结构体,并实现ServeHTTP方法。

而对于处理器与路由器的绑定方式,一种是通过HandleFunc方法直接绑定处理器;一种是通过 Handle方法变向绑定。

使用处理器函数实现
// 使用处理器函数实现HTTP服务器 (写法一)
func index(w http.ResponseWriter, r *http.Request) {
    _, err := w.Write([]byte("Hello World"))
    if err != nil {
        fmt.Println("err", err)
    }
}

func httpServerByFunc() {
    http.HandleFunc("/", index)
    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        panic(err)
    }
}


// 使用处理器函数实现HTTP服务器 (写法二)
func httpServerByFunc2()  {
    http.Handle("/", http.HandlerFunc(index))
    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        panic(err)
    }
}

// 使用处理器函数实现HTTP服务器 (写法三)
func httpServerByFunc3() {
    // 匿名函数
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        _, err := w.Write([]byte("Hello World"))
        if err != nil {
            fmt.Println("err", err)
        }
    })
    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        panic(err)
    }
}
使用处理器结构体实现
type MyHandler struct {

}

// 实现ServeHTTP方法
func (my *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    _, err := w.Write([]byte("Hello World"))
    if err != nil {
        fmt.Println("err", err)
    }
}

func httpServerByStruct()  {
    mux := http.NewServeMux()
    myHandler := MyHandler{}
    mux.Handle("/", &myHandler)

    // 注意这里传的是mux (mux也是一个Handler)
    err := http.ListenAndServe(":5000", mux)
    if err != nil {
        panic(err)
    }
}

注:上面的例子一开始看不懂没关系,可以先看下面的原理,然后再返回来看,就能理解了。

HTTP服务器实现原理

关于 HTTP服务器实现原理,本质上是一个如何创建路由器,并如何将路由器与处理器绑定的过程。(个人觉得比较核心的一个问题)

Golang HTTP服务器的实现不算很复杂,但是比较蛋疼的一个点是它的函数名(或者是函数签名)非常接近,所以一开始的时候会非常懵,典型的如 Handler、Handle、HandlerFunc以及HandleFunc,这四者的区别会在下文中讲到,注意看源码的时候不要看错了。

如何创建路由

创建路由有两种方式,第一是使用默认路由,即 DefaultServeMux,第二是使用 NewServeMux方法创建一个路由,这两种方式没有本质的区别,只不过Golang一贯的写法就是定义一个路由结构体,并同时提供默认路由,以及自定义路由的方法。

HTTP路由器的定义如下:

// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    es    []muxEntry // slice of entries sorted from longest to shortest.
    hosts bool       // whether any patterns contain hostnames
}

type muxEntry struct {
    h       Handler
    pattern string
}

上面的注释翻译后就是:ServeMux是一个HTTP请求多路复用器,也就是我们常说的路由器,它会将请求的URL与注册的URL进行匹配,匹配成功的话就交由处理器(Handler)处理。

路由器与处理器的绑定

路由器与处理器的绑定,主要用到 Handler 和 Handle。

首先从语义上看,Handler表示的是一个名词,代表处理器;而Handle表示的是一个动词,代表处理。与之相对应的还有 HandlerFunc 和 HandleFunc。

接着,我们从源码上看 Handler 和 Handle的区别:

// A Handler responds to an HTTP request.
// Handler,即处理器,接收请求并响应
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
// 注册Handler
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

上面列举了四个迷惑性比较大的函数,或者接口,它们的作用如下:

  1. Handler接口:从技术看,它的确是一个接口,并包含 ServeHTTP 方法;但从逻辑上看,它是一个处理器,作用是接收HTTP请求并响应,同时,只要有结构体实现了 ServeHTTP 方法,它就可以看做一个处理器。
  2. HandlerFunc类型:从源码注释可以看到,它是一个适配器,作用是将一个普通函数转变为一个处理器。为什么要这样设计?因为之前那种创建处理器的方式太麻烦,也不够简洁,需要创建一个结构体,并实现 ServeHTTP 方法。

这也是为什么我们常见的处理器都是这样的:

func index(w http.ResponseWriter, r *http.Request) {
}

那 HandlerFunc 是如何将普通函数转换成一个处理器呢?

答案就在下面的源码中,HandlerFunc虽然是一个类型,但在这里个人觉得有点结构体的味道,且它实现 ServeHTTP 方法,也就意味着 HandlerFunc 也是一个Handler,这也是为什么一个普通函数能作为一个处理器的原因。

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
// 调用我们实际的handler函数
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
  1. Handle和HandleFunc:这两个方法的作用都是一致的,都是将处理器注册到路由器中,它们的区别主要是HandleFunc是在 Handle之上再封装一层,仅此而已(可以通过查看源码得证)

我们可以对以上三点进行一个简单总结:ServeHTTP 与 HandlerFunc 的作用是生成一个 Handler,而 Handle 和 HandleFunc 则是将生成的Handler与路由器进行绑定,或者也可以说是注册。
最后的结论如下:

  1. HTTP服务器的实现涉及到路由器,处理器,以及它们之间的映射关系
  2. 路由器有两种形式,一是使用默认的路由(DefaultServeMux),二是使用 NewServeMux 方法创建一个路由,两者无本质区别
  3. Handler表示处理器,它的作用是接收HTTP请求,并响应 ,也就我们写业务逻辑的地方,上面例子的 index、login都是处理器
  4. 从源码上看,Handler是一个接口,包含 ServeHTTP 方法,这就意味着只要有结构体实现了 ServeHTTP,它就可以作为一个处理器。
  5. 由于实现ServeHTTP才能成为一个处理器这种方式比较冗余,也不够简洁,所以就有了 HandlerFunc 类型,它的作用是将一个普通函数转变为一个处理器
  6. 值得注意的是,ServeMux 本身也实现了 ServeHTTP方法,即它也是一个处理器,但它的作用只是将请求转发到其他处理器。

最后,关于HTTP服务器实现原理,实际涉及的知识点远比上文多的多,本文只是重点讲述了路由器、处理器的创建以及两者的绑定,希望能帮到大家!

补充
ServeMux 的 ServeHTTP

这里只是为了证明 ServeMux 也实现了ServeHTTP方法

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
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
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}
HTTP路由闭包问题

示例如下:

func routerClosure()  {
    // 当方法名/URL 是动态获取的时候
    methods := []string{"index", "login", "logout"}

    for _, m := range methods {
        http.HandleFunc("/" + m, func(w http.ResponseWriter, r *http.Request) {
            fmt.Println("method", m)
            w.Write([]byte("method name is : " + m))
        })
    }

    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        panic(err)
    }
}

通过测试,你会发现,不管用index,login,最终都会路由到 logout 的处理函数,这是因为闭包导致的。

$ curl http://127.0.0.1:5000/index
method name is : logout% 

$ curl http://127.0.0.1:5000/login
method name is : logout% 

解决办法如下:

func routerClosure2()  {
    // 当方法名/URL 是动态获取的时候
    methods := []string{"index", "login", "logout"}

    for _, m := range methods {
        newM := m
        http.HandleFunc("/" + newM, func(w http.ResponseWriter, r *http.Request) {
            fmt.Println("method", newM)
            w.Write([]byte("method name is : " + newM))
        })
    }

    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        panic(err)
    }
}

作者:言淦
链接:https://juejin.im/post/6867149633328513038
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

go.mod 使用私有依赖 背景 启动了一个新项目,做一个运维小工具,准备尝试一下 Golang。 因需要对接内部的 RABC 系统,索性封装了一个 SDK,也方便之后新项目的对接使用。 那么,怎么能够使用 go.mod 来使用私有的依赖呢? 环境 gitlab(私有) golang v1.16.3 解决方案 我们需要对 Golang 做一些简单的配置。 1、使用 go module go env -w GO111MODULE="on" 2、配置 GOPRIVATE,当使用 go get 时,指定的域名会直接通过 git 拉取. 阅读详情

相关推荐

golang】net/http

golang】net/http包 在golang中,使用net/http包可以轻松的创建一个web服务器。 其中,http.listenAndServe是一个很重要的方法,这里说说我踩的一个坑吧。这跟自己的开发背景有关。 listenAndServe接受两个参数,第一个是需要一个字符串形式的服务器地址,比如”localhost:8080”,第二个参数是一个用于分派所有请求的http.Han...

小辣抓 2008

Golang进阶 —— Http服务器详解

由于Golang优秀的并发处理,很多公司使用Golang编写微服务。对于Golang来说,只需要短短几行代码就可以实现一个简单的Http服务器。加上Golang的协程,这个服务器可以拥有极高的性能。然而,正是因为代码过于简单,我们才应该去研究他的底层实现,做到会用,也知道为什么这么用。 在本文中,会以自顶向下的方式,从如何使用,到如何实现,一点点的分析Golang中net/http这个包中关于Http服务器的实现方式。内容可能会越来越难理解,作者会尽量把这些源码讲的更清楚一些,希望对各位有所帮助。

金戈鐡馬 2311

使用Golang实现HTTP GET请求

该函数需要三个参数:请求方法(“GET”)、请求URL(https://api.example.com/data)和请求正文(在此示例中为空)。Golang是一种强大的编程语言,它提供了一种简单且高效的方式来执行HTTP请求。在本文中,我将向您展示如何使用Golang编写代码来执行HTTP GET请求。请注意,上述示例中的URL(https://api.example.com/data)只是一个示例,您应该将其替换为您实际想要发送GET请求的URL。包,该包提供了执行HTTP请求所需的功能。

MxTransforms的博客 271

handlehandleFunc的区别

一般handleFuc传入的是实现了接口 1 Handler{ 2 3 ServerHttp();//一个写入responseWriter的函数 4 5 } 就是handler了。 但是handle,接受的是一个handler,一般是官方的标准写法,然后返回的一个实现了ServerHTTPhandler 转载于:https://www.cnblogs.com/ggaa...

weixin_30448685的博客 653

第十章 golang http操作

发送http请求

qq_24430629的博客 2472

golang go创建http服务

在这个示例中,我们首先导入了 `fmt`、`log` 和 `net/http` 包。然后,在 `main` 函数中,我们使用 `http.HandleFunc` 函数将请求路由到名为 `handler` 的函数。要运行这个示例,您可以将代码保存在名为 `main.go` 的文件中,然后在终端中使用 `go run main.go` 命令来运行它。在 `handler` 函数中,我们使用 `fmt.Fprint` 函数向用户发送一条简单的消息。这个消息将显示在用户访问网站时的页面上。

m0_61412114的博客 323

golang两种请求http方式

http请求在工作很常见了,下面看一下常见的2中http请求方式 ifresponse!=nil{ deferresponse.Body.Close() } 注意这段代码,先判断response是否nil,如果response是nil,程序会崩溃 package main import ( "crypto/tls" "encoding/json" "fmt" "io/ioutil" ...

7435

http2curl 项目使用教程

``` http2curl/ ├── AUTHORS ├── COPYRIGHT ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── doc/ │ └── godoc.go ├── go.mod ├── go.sum ├── http2curl.go ├── http2curl_test.go └── rules.

gitblog_01147的博客 608

阅读:《Golang web 应用开发》

阅读:《Golang web 应用开发》 一. Web工作方式 上网过程 对于普通的上网过程,系统其实是这样做的:浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包,服务器调用自身服务,...

Zqz983944的博客 488

linux pprof工具安装,Golang性能分析工具PProf的使用/Go GC监控

Golang提供了强大的代码性能分析工具PProf,能够非常方便的监控代码运行性能。性能分析可以采用命令行方式 go tool pprof 同时也提供直观的http可视化方式,非常方便。数据采集性能分析基础数据的获取有三种方式:1> runtime/pprof 包2> net/http/pprof 包3> go test 时添加收集参数runtime/pprof 包的使用[不推荐...

weixin_39643244的博客 1053

golang 性能分析(pprof)

性能分析 从业务目标角度,通常我们的瓶颈出现在业务的通量和时延两个问题上 比如一个MySQL数据库,你从其他机器上对它发请求,每秒它能处理10万个请求,这个就是通量性能;每个请求的反应时间是0.5ms,这个就是时延性能 通量和时延是相互相承的两个量,当通量达到系统上限,时延就会大幅提高 一般而言,性能分析主要关注 CPU、内存、磁盘 IO、网络这些指标 runtime/pprof 适用...

firefly 5614

go语言html css,go 1.16 embed 实现资源文件(html, css, js等)内嵌

Golang 发布1.16,如今通过//go:embed 注解内嵌资源文件并打包到二进制文件,关于//go:embed的使用网上很多教程,我想也不需要我在此在啰嗦一遍,今天的重点:用go开发网站时候内嵌的css、html、js以及图片等资源如何内嵌和渲染到网页。含有资源文件的demo项目结构1. 在没有//go:embed支持之前的实现serve文件的方式如下:package mainimport...

weixin_39806603的博客 639

golang http 操作 简介

Golang提供了官方的http包,对于http操作非常的方便和简洁。 但是不同于PHP,使用Golang的包来做http操作,还是没有那么”直接“,需要实例化一下这个,实例化一下那个,有点像Java,因此,为了以后书写方便,就把基本的请求写在此。下次用的时候,直接copy就好了。 get 请求 get请求有好几种方式 直接使用net/http包内的函数请求 import "net/http" ... resp, err := http.Get("http://wwww.baidu.com")

whatday的专栏 5925

如何快速上手VictoriaMetrics/metrics:5分钟实现Go应用监控

VictoriaMetrics/metrics 是一个轻量级的 Go 应用监控库,作为 github.com/prometheus/client_golang 的替代方案,它具有更简洁的 API 和更小的依赖体积。通过这个库,开发者可以快速为 Go 应用添加监控指标,实现对应用性能和运行状态的实时跟踪。 ## 🚀 安装与配置步骤 ### 1. 克隆仓库 首先,将项目代码克隆到本地: ```b

gitblog_00719的博客 474

[7天实战入门Go语言后端] Day 6:测试与 Docker 部署——单元测试与多阶段构建

两阶段:第一阶段用带 Go 编译器的镜像(如 golang:1.21-alpine)编译出二进制;第二阶段用很小的 alpine 只拷贝二进制和 ca-certificates,不包含源码和编译器,镜像体积小,部署快。构建 day7:本系列把「可运行的应用」放在 Day 7,Day 6 只讲测试和镜像写法;所以 Dockerfile 里,打出来的镜像跑的是 Day 7 的 server。学完 Day 7 后,同一 Dockerfile 即可用于上线部署。

消极的人永远是对的,积极的人选择勇往直前 826

【Go开源宝藏】Golang 爬虫 整点新花样_golang 使用session爬虫

Python爬虫可能大家都玩腻了,那就玩一下Golang的爬虫吧!这篇文章会持续更新哒!

2401_84617731的博客 1659

GO学习记录五——数据库表的增删改查

增删改查

chillxiaohan的博客 411

组件分享之后端组件——grpc组件库grpc-go

组件分享之后端组件——grpc组件库grpc-go 背景 近期正在探索前端、后端、系统端各类常用组件与工具,对其一些常见的组件进行再次整理一下,形成标准化组件专题,后续该专题将包含各类语言中的一些常用组件。欢迎大家进行持续关注。 组件基本信息 组件:grpc-go 开源协议:Apache-2.0 License 内容 在使用Golang中grpc就不得不提,其强大的性能为其接口提供了有力的...

CN華少的博客 468
上一篇: Go中net/context和http.Handler
下一篇: Go中方法调用和接口实现
sb___itfk
博客等级 码龄12年 19粉丝 23原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值