Golang使用Gin框架实现http分块传输
作者:Go学堂 发布时间:2024-02-08 20:15:18
今天,跟大家聊聊gin框架中是如何实现分片输出的。主要分以下4点:
分片输出的效果图
gin实现分片传输代码
http分片传输的基础:transfer-encoding
gin实现分片传输原理
效果图
首先看下分片输出的效果图:
gin分片传输实现代码
上面的效果图中,网页中的内容不断的输出。在gin中是主要是利用了Flush函数实现的。如下代码:
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test_stream", func(c *gin.Context) {
w := c.Writer
header := w.Header()
header.Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`
<html>
<body>
`))
w.(http.Flusher).Flush()
// 这里对每次循环输出都进行Flush刷新输出
for i := 0; i < 10; i++ {
w.Write([]byte(fmt.Sprintf(`
<h3>%d</h3>
`, i)))
//w.Flush()
w.(http.Flusher).Flush()
time.Sleep(time.Duration(1)*time.Second)
}
w.Write([]byte(`
</body>
</html>
`))
w.(http.Flusher).Flush()
})
r.Run("127.0.0.1:8080")
}
这里主要就是利用了第22行中的w.(http.Flusher).Flush()。 这里的Flush本质上就是将Write的内容立即输出到客户端的意思。
那么,为什么通过Flush就能实现上述效果呢?
分块传输的基础:http的 transfer-encoding:chunked 协议
分块传输的基础就是http中的transfer-encoding:chunked协议。在http响应报文中用头字段“Transfer-Encoding: chunked”,表示响应中的body不是一次性发送完毕,而是分成了许多的块(chunk)逐个发送,直到发送完毕。
分块传输的编码规则如下: 1)每个分块包含两个部分,<长度头>和&<数据块> 2) <长度头>是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度 3) <数据块>紧跟在<长度头>后,最后也用 CRLF 结尾,但数据不包含 CRLF 4)最后用一个长度为 0 的块表示数据传输结束,即“0\r\n\r\n”。
为什么通过Flush函数就能实现分块传输
到了本篇的核心部分了,为什么在gin中通过Flush函数就能实现分块传输了呢?首先,在gin框架中正常的输出是通过Context.Writer.Write函数进行输出的。而Writer是net/http包中的response对象,该response对象包含了本次http的连接对象conn。以下是从Context.Writer对象到conn对象的一个层级关系,如下:
Context.Writer对象指向了response对象,response对象中包含一个缓冲的Writer对象w,w的底层输出对象时chunkWriter对象cw,cw又指向了本次的http连接对象response.conn。
那么,基于这个层级结构,Context.Writer.Write的写入过程如下:
我们简化一些,就是Context.Writer.Write先将内容写入到缓冲区w中,然后等本次请求逻辑处理完毕,再调用缓存区w的Flush功能,将缓冲区w中的内容写入到cw中,然后调用cw的flush功能,这时就写入了http的响应头Content-Length为写入数据的长度,并且将内容通过conn.bufw.flush输出给客户端。
简化一下gin的输出过程:内容先写入到缓冲区,最后将缓冲区的内容一次性全部输出给客户端。
划重点,Content-Length头部的输出是和分块传输的主要区别。
接下来再看分块输出。
其实现的思想就是通过http的Transfer-Encoding: chunked头告诉客户端,服务端的内容要分块传输了。然后服务端就将内容先写入缓冲区,然后立即使用Flush函数将缓冲区的内容输出到客户端。这就是一个块的输出。然后依次循环写入,Flush刷新输出这个过程。
下图是gin中分块传输的流程图:
在分块输出的时候,在response.cw.flush阶段,可以判定到该请求还未处理完毕(在net/http包中,本次请求处理完毕才会调用一个finishRequest的函数以标识本次请求处理完毕),所以会自动写入一个http的头信息: Transfer-Encoding: chunked。当客户端收到该响应时,检测到header中的chunked,就表示本次响应还未结束,会继续接收后续的响应内容。
简化一下gin的分块传输流程如下:
来源:https://juejin.cn/post/7233196954402439226


猜你喜欢
- 生产定制一个彩条标签。首先导入:import matplotlib.pyplot as pltimport numpy as npfrom
- 本文实例讲述了Python实现的十进制小数与二进制小数相互转换功能。分享给大家供大家参考,具体如下:十进制小数 ⇒ 二进制小数乘2取整对十进
- 所有文件都包含在各个不同的目录下,不过Python也能轻松处理。os模块有许多方法能帮你创建,删除和更改目录。mkdir()方法可以使用os
- 这篇文章主要介绍了python Opencv计算图像相似度过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- 今天逛论坛时看到有朋友问,是否有专门教Javascript的学校,这里想想把自己的一点建议和自己3年来的前端Javascript开发的经验跟
- 通过手动输入数据,将数据分成几部分存入数组中import osimport sysdef test(): bric
- 每次遇到pandas的dataframe某列日期格式问题总会哉坑,下面记录一下常用时间日期函数....1、字符串转化为日期 str—>
- 本文实例讲述了Python使用matplotlib绘制正弦和余弦曲线的方法。分享给大家供大家参考,具体如下:一 介绍关键词:绘图库官网:ht
- 一、requests库requests是使用Apache2 licensed 许可证的HTTP库。比urllib模块更简洁。Request支
- Python是一门高级编程语言,其拥有多种循环方式,如for循环、while循环、do-while循环等。在编写程序时,需要根据不同的场景和
- 1. 日志的意义日志是个好东西,但却并不是所有人都愿意记,直到出了问题才追悔莫及,长叹一声,当初要是记日志就好了。但记日志却是个技术活,不能
- 问题你写了一个装饰器作用在某个函数上,但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。解决方案任何时候你定义装饰器的
- SQL2005安装安装步骤安装Microsoft SQL Server 2005 数据库步骤:第一步:将Microsoft SQL Serv
- 最近看了下go发送smtp邮件,于是总结一下简单示例 先上一个最简单的代码 (网上搂的代码改了改)package mainimport (
- 好想在2014结束前再赶出个10篇博文来,~(>_<)~,不写博客真不是一个好兆头,至少说明对学习的欲望和对知识的研究都不是那么
- 前言这周和大家分享如何用python识别图像里的条码。用到的库可以是zbar。希望西瓜6辛苦码的代码不要被盗了。(zxing的话,我一直没有
- <script> Function.prototype.$bind=function(object) {  
- 一个简单的php文件下载源代码,虽不支持断点续传等,但是可以满足一些常用的需求了。php下载文件其实用一个a标签就能实现,比如 <a
- 前言numpy.random 模块对 Python 内置的 random 进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数,如正
- *和&的区别 :& 是取地址符号 , 即取得某个变量的地址 , 如 ; &a*是指针运算符 , 可以表示一个变量是指