深入 net/http:看一下 http.RoundTripper

golang net/http包部分实现原理详解 net/http包在编写golang web应用中有很重要的作用,它主要提供了基于HTTP协议进行工作的client实现和server实现,可用于编写HTTP服务端和客户端。 其使用方法也跟其他面向对象语言很相似,我们可以先从它的一些基础用法来感受一下: 以下是示例客户端代码: // 用http包中默认的Client进行请求(因为没有指定Client) resp, err := http.Get(... 阅读详情

我已经写了很多关于HTTP的文章。这篇博文只是另一篇文章,它讨论了 Go 处理方式中的另一个有趣概念,HTTP以及它如何使与 HTTP 相关的东西变得更加有趣。

在这篇文章中,我将介绍Round tripping是什么,它的适用用例和一个显示它的应用程序的小演示。

我想谈的这个概念被称为Round tripping或godoc描述它执行单个 HTTP 事务的能力,获得给定 Request 的 Response。基本上,这意味着能够了解发出HTTP请求和接收响应之间发生的情况。用外行的话来说,它就像中间件,但对于http.Client. 我这样说是因为往返发生在请求实际发送之前。

尽管可以在该RoundTrip方法中执行任何操作(例如 HTTP 处理程序的中间件),但建议您不要检查响应、返回错误(nil 或非 nil)并且不应该执行用户之类的操作身份验证(或 cookie 处理)..

因为http.RoundTripper是一个接口。要获得此功能,您所要做的就是实施RoundTrip:

type SomeClient struct {}

func (s *SomeClient) RoundTrip(r *http.Request)(*Response, error) {
	//Something comes here...Maybe
}

这只是与 stdlib 中的其他方法接口保持一致。小而简洁。

用例

  • 缓存 http 响应。例如,您的网络应用程序必须连接到 Github 的 API 以获取内容(其中之一是趋势回购)。在现实生活中,这种情况经常发生变化,但我们假设他们每 30 分钟重建一次趋势板,并且您的应用程序拥有大量用户。您显然不想每次都点击 api 来请求趋势排行榜。因为它在30 分钟的窗口中总是相同的,并且考虑到 API 调用是有速率限制的,并且由于您的应用程序的高使用率,您几乎总是达到/超过限制。

    一个解决方案是使用http.RoundTripper. 您可以http.Client使用执行以下操作的 RoundTripper 配置您的:

    • 缓存商店有这个项目吗?

      • 不要提出HTTP要求。
      • 通过将缓存中的数据读取到响应正文中来返回新的响应。
    • 缓存存储没有此项(可能是因为缓存每 31 分钟失效一次)

      • 向HTTPapi 发出请求。
      • 缓存从 api 接收到的数据。

您不必为此使用 RoundTripper,因为(在处理程序内)您可以在发出HTTP 请求之前检查缓存中是否存在某个项目。但是通过 RoundTripper 实现,您可能会正确分配职责[0]

  • 根据需要向请求添加适当的(授权)标头...... 一个很容易想到的例子是google/go-github,Github 的 api 的 Golang 客户端。Github 的 api 的某些部分需要对请求进行身份验证,有些则不需要。默认情况下,该库不处理身份验证,它使用默认的 HTTP 客户端,如果您需要能够访问 api 的经过身份验证的部分,您可以携带自己的 HTTP 客户端,例如oauth2受保护的端点。那么如何这个问题往返,有这个ghinstallation允许你使用 go-github 验证 Github 应用程序。如果您查看它的代码库,它所做的只是提供一个http.Client实现http.RoundTripper. 之后它在RoundTrip方法。

  • 速率限制。这与上面的非常相似,也许您有一个存储桶,用于保存最近建立的连接数。您检查您是否仍处于可接受的 API 状态,并决定是否应该发出请求、停止发出请求或安排它在将来运行。

  • 不管你有什么……也许没有。

真实世界的使用

我们将HTTP通过http.RoundTripper. 我们将创建一个只响应一个路由的服务器,然后创建一个连接到该服务器的客户端包。客户端将利用它自己的实现,http.Client因此我们可以提供我们自己的 RoundTripper,因为我们正在尝试缓存响应。

所以这就是它的样子,

  • 客户端向服务器发出请求。
    • 如果该 url 的响应存在于缓存存储中?
      • 不要调用服务器。
      • 从商店取货。
      • 将其写入响应并直接返回。
    • 如果缓存存储中不存在该 url 的响应
      • 向服务器发出请求。
      • 将响应的主体写入缓存存储。
      • 返回响应。

这已经放在github上了。


$ mkdir client server

我们将首先构建服务器,因为它的实现非常简单

import (
	"fmt"
	"net/http"
)

func main() {

	// server/main.go

	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// This is here so we can actually see that the responses that have been cached don't get here
		fmt.Println("The request actually got here")

		w.Write([]byte("You got here"))
	})

	http.ListenAndServe(":8000", mux)
}

然后我们将构建客户端包。这是最有趣的部分,虽然它很长(130 多个 LOC),但应该相对容易理解。我强烈建议您前往github存储库。

首先,我们需要一个缓存存储。由于这是一个最小的项目,字典/地图可以帮助我们尽快离开。我们将创建一个http.Transport实现http.RoundTripper但也是缓存存储的对象。

但在现实生活中,您会希望将它们彼此分开。

func cacheKey(r *http.Request) string {
	return r.URL.String()
}

type cacheTransport struct {
	data              map[string]string
	mu                sync.RWMutex
	originalTransport http.RoundTripper
}

func (c *cacheTransport) Set(r *http.Request, value string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.data[cacheKey(r)] = value
}

func (c *cacheTransport) Get(r *http.Request) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if val, ok := c.data[cacheKey(r)]; ok {
		return val, nil
	}

	return "", errors.New("key not found in cache")
}

// Here is the main functionality
func (c *cacheTransport) RoundTrip(r *http.Request) (*http.Response, error) {

	// Check if we have the response cached..
	// If yes, we don't have to hit the server
	// We just return it as is from the cache store.
	if val, err := c.Get(r); err == nil {
		fmt.Println("Fetching the response from the cache")
		return cachedResponse([]byte(val), r)
	}

	// Ok, we don't have the response cached, the store was probably cleared.
	// Make the request to the server.
	resp, err := c.originalTransport.RoundTrip(r)

	if err != nil {
		return nil, err
	}

	// Get the body of the response so we can save it in the cache for the next request.
	buf, err := httputil.DumpResponse(resp, true)

	if err != nil {
		return nil, err
	}

	// Saving it to the cache store
	c.Set(r, string(buf))

	fmt.Println("Fetching the data from the real source")
	return resp, nil
}

func (c *cacheTransport) Clear() error {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.data = make(map[string]string)
	return nil
}

func cachedResponse(b []byte, r *http.Request) (*http.Response, error) {
	buf := bytes.NewBuffer(b)
	return http.ReadResponse(bufio.NewReader(buf), r)
}

然后是我们引导程序的主要功能。我们会设置一个计时器来清除缓存存储,这样我们就可以向服务器发出请求,这使我们能够查看缓存或原始服务器正在为哪些请求提供服务。

func main() {

	//client/main/go

	cachedTransport := newTransport()

	//Create a custom client so we can make use of our RoundTripper
	//If you make use of http.Get(), the default http client located at http.DefaultClient is used instead
	//Since we have special needs, we have to make use of our own http.RoundTripper implementation
	client := &http.Client{
		Transport: cachedTransport,
		Timeout:   time.Second * 5,
	}

	// Time to clear the cache store so we can make request to the original server rather than fetch from the cache store
	// This is to replicate real expiration of data in a cache store
	cacheClearTicker := time.NewTicker(time.Second * 5)


	//Make a new request every second
	//This would help demonstrate if the response is coming from the real server or the cache
	reqTicker := time.NewTicker(time.Second * 1)

	terminateChannel := make(chan os.Signal, 1)

	signal.Notify(terminateChannel, syscall.SIGTERM, syscall.SIGHUP)

	req, err := http.NewRequest(http.MethodGet, "http://localhost:8000", strings.NewReader(""))

	if err != nil {
		panic("Whoops")
	}

	for {
		select {
		case <-cacheClearTicker.C:
			// Clear the cache so we can hit the original server
			cachedTransport.Clear()

		case <-terminateChannel:
			cacheClearTicker.Stop()
			reqTicker.Stop()
			return

		case <-reqTicker.C:

			resp, err := client.Do(req)

			if err != nil {
				log.Printf("An error occurred.... %v", err)
				continue
			}

			buf, err := ioutil.ReadAll(resp.Body)

			if err != nil {
				log.Printf("An error occurred.... %v", err)
				continue
			}

			fmt.Printf("The body of the response is \"%s\" \n\n", string(buf))
		}
	}
}

为了测试这一点,我们必须构建两个程序 -client/main.go和server/main.go. ./client使用和在各自的目录中运行它们./server。你应该得到这样的东西

并观察打印到终端的内容,您会注意到有些地方说“从缓存中获取”,而有些地方说“从服务器中获取”。最有趣的部分是如果您查看 的实现server/main.go,我们有一个fmt.Println只有在调用服务器时才会执行,您会注意到只有在客户端打印“从服务器获取”时才会看到它。

另一件需要注意的事情是,无论我们是否访问服务器,响应的主体都会保持不变。

 

go-instrumented-roundtripper:用于 Go http.Client 的检测 RoundTripper 示例 Go http.Client 的 RoundTripper 示例 通过创建包装默认 http.Transport 和 net.Dialer 的自定义 http.RoundTripper 来检测 http.Client 的非常简单的示例。 立即下载

相关推荐

GROMACS新手必看:从PDB文件到完整分子动力学模拟的保姆级教程

本文提供GROMACS分子动力学模拟的保姆级教程,从PDB文件处理到完整模拟流程全覆盖。详细讲解结构预处理、系统构建、能量最小化、生产模拟等关键步骤,并分享性能优化和结果分析技巧,帮助新手快速掌握分子动力学模拟的核心操作。

yhn456789的博客 763

深入分析 http.RoundTripper

1、RoundTripper 接口 // RoundTripper is an interface representing the ability to execute a // single HTTP transaction, obtaining the Response for a given Request. // // A RoundTripper must be safe for concurrent use by multiple // goroutines. type RoundTr

hero_java的专栏 2574

新版本大疆智图4.5 永久许可摄影测量建模软件

大疆智图支持常见格式的成果输出,包括 TIFF 正射影像和高程模型、LAS 格式点云,以及 B3DM、OSGB、OBJ 等六种常用格式的三维模型,还包括 DXF 和 SHP 等格式的等高线。大疆智图 DJI Terra是领先的三维重建软件,提供先进的技术和简单的操作,一站式解决方案,从航线规划到数据的获取,以及模型重建、数据分析等都提供独一无二的支持,优化项目工程,提供自动化优势,获得更有效智能的决策和行动,最终用户可以获得理想的结果,减轻工作压力,提高作业效率。质量报告和剖面查看功能有助于分析点云质量。

qq_62821232的博客 6897

Golang net/http 包中的 RoundTripper 接口详解

RoundTripper 是 net/http 包中的一个接口,定义了处理 HTTP 请求返回和响应的方法,是 http.Client 结构体中执行 http 请求的核心部分。只定义了 RoundTrip 一个方法,用于执行单个 HTTP 事务,发送请求数据并返回对应的响应数据。RoundTrip 不应该去解析响应数据,特别是如果获得了响应数据后,RoundTrip 必须返回值为 nil 的 error(不管响应的HTTP状态码是什么)。

路多辛的所思所想 1227

单细胞测序数据分析——细胞通讯 CellChat

cellchat

G2530051386的博客 1万+

Matlab Arduino 硬件支持包安装 MATLAB_Support_Package_for_Arduino_Hardware

Matlab Arduino硬件支持包离线安装

weixin_43283734的博客 2841

Golang Http RoundTrip解析

概要 RoundTrip是发送和接收一次http请求的底层实现逻辑 我们可以从中学习到 1、如何去实现一个长链接 2、如果去提升性能 3、如何考虑完备各种边界情况 4、http协议 请求和连接的关系 一个请求肯定会使用一个或者多个连接,通常是一个,但如果异常情况,如刚好连接在使用过程中失效了,则不排除使用多个连接 一个连接可以在一次请求后就丢弃(关闭),但是对于长链接的情况,连接可以被放回到闲置连接池中,这样后续的请求到来时,就可以不必再次创建新的连接,而是复用之前的连接。要是到tcp的三

zhanglehes的专栏 5701

海康DS-7604N-K1/RTB固件包

【摘要】海康DS-7604N-K1/RTB萤石云设备发布V4.30.091版本升级程序(2022.09.19),仅适用于指定型号。升级前需先升级至V3.4.106基础版本,刷机会重置设备为未激活状态,需重新设置admin密码。该机型支持密码/手势登录,无密码时可通过U盘升级。注意事项:升级存在风险,需确认程序适用性;升级过程严禁断电;必须阅读压缩包内的刷机说明。此为定制机型,非必要不建议升级。(150字)

海康威视技术支持 1575

Go 语言编程 — net/http — HTTP 客户端

目录 文章目录目录net/http net/http

烟云的计算 4504

基于STM32设计的智能井盖(Lora组网+4G上云)_423

设计并实现了一种基于 STM32 与 Lora 通信技术的智能井盖监测系统。系统采用 STM32F103C8T6 作为核心控制器,在井盖节点端集成水位检测、有害气体检测、倾斜角度检测以及 GPS 定位等功能模块,对井盖内部环境状态和姿态变化进行实时监测。各井盖节点通过 Lora 无线通信方式组成网络,将采集的数据发送至主机(网关)进行汇聚处理。

719

OpenClaw实战系列10(完结篇):企业级应用实战案例——从0到1搭建智能客服系统

本文以一家跨境电商公司为例,从需求分析、系统设计、分步搭建到上线运营,完整呈现智能客服系统的落地全过程。所有配置命令和截图均来自实测环境,你可以直接复制使用。

PeterPan的博客 1306

go net/http 源码解读

在 go 中启动一个 http server 只需短短几行代码。

讨论与分享 891

华为FusionCompute之计算虚拟化

华为FusionCompute之虚拟化

jks212454的博客 2452

go标准库---net/http客户端

首先介绍下NewRequest这个函数,主要是构造Request实例,其中调用的是NewRequestWithContext这个函数,先检查请求的方法,如果为空默认是Get方法,接着检验方法的合法性,并格式化处理地址,最终返回一个Request实例。(2) Transport是一个RoundTripper的接口类型,该接口类型实现一个RoundTrip方法,该方法负责实现传入请求,返回请求对应的响应,是客户端请求的核心。(3) Request是请求的结构体,包含了请求方法、请求体、请求地址,协议等信息。

yuan123890的博客 799

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

HTTP客户端实现:深入理解Go的net/http包

通过这篇深度解析,我们从Gonet/http包的基础架构出发,逐步深入到高级特性、实际应用和常见陷阱。

sinat_27016095的博客 942

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【Web框架】1.go的net/http标准库

调用链:Post()->Client.Post()->Client.Do()->Client.send()->Transport.RoundTrip()若无可用连接,通过 queueForDial 异步创建一个新的连接,并通过接收一个ready channel的方式,等待连接创建完成。Transport.dialConn:调用 Transport.dial 创建 tcp 连接,并启动异步。persistConn.roundTrip:通过 tcp 连接完成与服务端的交互。for + Accept 阻塞。

lixiaonan0318的博客 1052

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

理清Golang中标准库net/http的结构

参考:https://golang.org/pkg/net/http/ 参考:https://draveness.me/golang/docs/part4-advanced/ch09-stdlib/golang-net-http/ 一. 简介 http模块提供了HTTP客户端和服务端的实现 二. 两个关键结构体: server和client结构体 clientHTTP客户端 介绍 Client的Transport通常具有内部状态(缓存的TCP连接) 客户端对于多个goroutine并发使用是.

weixin_38371073的博客 529

go net/http 学习笔记

在使用 Go 开发时几乎都会用到net/http标准库。但是,对库的内部实现不了解,仅限于会用。长连接和短连接有什么区别?具体什么实现原理?net/http如何处理并发请求?net/http有用到缓存吗?缓存用来干什么?......单个问题各个击破,不如深入梳理下net/http标准库一网打尽。本文将深入学习net/http标准库,力图做到知其然知其所以然。

chengxuyuanlaow的博客 27

7天玩转 Golang 标准库之 http/net

有时,我们需要对HTTP请求进行更细粒度的控制,比如设置超时时间、添加请求头等等。这个时候,我们可以使用和来自定义我们的请求行为。= nil {以上,我们简洁明了地介绍了如何使用Golang中的net/http标准库进行HTTP请求的发送、服务器的创建以及请求行为的自定义。但是,net/http库的功能远不止这些。

青玉白露的博客 1323

Go语言标准库之net/http(三) —— Client

一般情况下,我们并不需要自定义来控制控制HTTP请求的行为和配置,使用net/http包中默认的即可,在client.go可以看出默认的并没有设置Client任何属性值,但是如果我们需要设置HTTP请求的超时时间、代理、连接池等选项,可能就需要我们自己去定义和创建了。根据Client结构体内容,我们知道拥有TransportJarTimeout四个属性字段,详细介绍如下:Timeout它用于设置HTTP客户端的超时时间,是一个类型的值,表示客户端在发送请求后等待服务器响应的最大时间。

xjx的博客 2849

Golang http之client源码详解

http客户端介绍 http 是典型的 C/S 架构,客户端向服务端发送请求(request),服务端做出应答(response)。本文章主要介绍Golang中http客户端的相关源码,源码主要在net/http/client.go中。源码版本号为1.10.3 一个简单的http 客户端请求例子: package main import ( "log" "fmt"...

skh2015java的博客 5980

go net/http Client使用——长连接客户端的使用

go net/http Client使用总结 Client数据结构 // A Client is an HTTP client. Its zero value (DefaultClient) is a // usable client that uses DefaultTransport. // // The Client's Transport typically has inte

路漫漫其修远兮 2万+

Go网络编程之HTTP编程

     HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,定义了客户端和服务端之间请求和响应的传输标准。     Go语言标准库内建提供了net/http包,涵盖了HTTP客户端和服务端的具体实现。使用net/http包,我们可以很方便地编写HTTP客户端或服务端的程序。   1.HTTP客户端    net/http...

冷月醉雪的博客 2698

golang net/http 用法

1. 前言http包包含http客户端和服务端的实现,利用Get,Head,Post,以及PostForm实现HTTP或者HTTPS的请求.2. 本文分析内容安排函数结构3. 函数3.1 服务端函数func Handle(pattern string, handler Handler)将handler按照指定的格式注册到DefaultServeMux,ServeMux解释了模...

weixin_33704591的博客 311
上一篇: fission使用指南
下一篇: k8s部署fabric 2.4
cbmljs
博客等级 码龄10年 329粉丝 130原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值