Go:http request cancelled 服务端感知


1.背景

今天查问题的时候,偶然发现github上一个有意思的问题,记录下来。

原问题:https://github.com/golang/go/issues/23262


2.问题

首先,我们思考一个问题:

当HTTP请求被客户端cancel(例如,TCP链路断开),HTTP服务端能否感知?

通常来说(有一定“条件”),是可以感知的。

环境版本:

$ go version
go version go1.13.6 linux/amd64

服务实现:

package mainimport ("fmt""net/http""time"
)func Handler(w http.ResponseWriter, req *http.Request) {go func() {<-req.Context().Done()fmt.Println("done")}()time.Sleep(10 * time.Second)
}func main() {http.ListenAndServe(":9080", http.HandlerFunc(Handler))
}

编译运行后,使用curl发起测试:

curl -v --trace-time http://127.0.0.1:9080/
21:42:34.943320 * About to connect() to 127.0.0.1 port 9080 (#0)
21:42:34.943526 *   Trying 127.0.0.1...
21:42:34.943711 * Connected to 127.0.0.1 (127.0.0.1) port 9080 (#0)
21:42:34.943882 > GET / HTTP/1.1
21:42:34.943882 > User-Agent: curl/7.29.0
21:42:34.943882 > Host: 127.0.0.1:9080
21:42:34.943882 > Accept: */*
21:42:34.943882 >
^C(立刻Ctrl+C)

此时,服务端立刻、实时输出(done):

$ go run main.go
done

当客户端(curl)断开链路(取消请求)时,服务端是可以实时感知的:Request.Context将Done。


3.服务端是否总能感知呢?

答案:并不是。

例如:使用curl做客户端发起POST请求,并携带请求正文数据:

curl -v --trace-time http://127.0.0.1:9080/ -X POST -d '123' -H 'Content-Type: application/text'
21:46:21.723542 * About to connect() to 127.0.0.1 port 9080 (#0)
21:46:21.723743 *   Trying 127.0.0.1...
21:46:21.723939 * Connected to 127.0.0.1 (127.0.0.1) port 9080 (#0)
21:46:21.724147 > POST / HTTP/1.1
21:46:21.724147 > User-Agent: curl/7.29.0
21:46:21.724147 > Host: 127.0.0.1:9080
21:46:21.724147 > Accept: */*
21:46:21.724147 > Content-Type: application/text
21:46:21.724147 > Content-Length: 3
21:46:21.724147 >
21:46:21.724829 * upload completely sent off: 3 out of 3 bytes
^C
$ go run main.go
done(在运行main.go十秒之后输出结束,而不是在curl执行Ctrl+C后即时输出)

4.原因

Go需要应用层(即Handler实现者)先消费http.Request.Body完毕,才能感知到TCP链路上的RST、EOF。

例如:

客户端一口气发送了1GB的数据到服务端,然后立刻断开链路,内核发送RST包:

服务端需读完链路上堆积的数据(在Handler中读取Request.Body完毕),才能感知Request.Context已经Done:

参考:https://github.com/golang/go/issues/23262
Go doesn’t read the Request.Body by default.Your Handler has to do that.
Only once your Handler has reached the framed EOF (framed by Content-Length or chunking), then Go’s server takes over and does the long background read waiting for the next request or the connection to die.
We don’t do the automatic read by default, because that’d waste bandwidth (not applying any backpressure by filling up TCP buffers) if the request body is unwanted.

一定要应用层消费读取链路上的数据完毕(无论是Content-Length或chunk),Go底层才可感知。

理由是防止浪费带宽。

我认为这样做是不合理的:使得应用层感知请求被取消变得复杂、增加更多不必要开销。


5.消耗请求Body版

io.Copy(ioutil.Discard, req.Body)

func Handler(w http.ResponseWriter, req *http.Request) {io.Copy(ioutil.Discard, req.Body)go func() {<-req.Context().Done()fmt.Println("done")}()time.Sleep(10 * time.Second)
}

或者:req.Body.Close()

func Handler(w http.ResponseWriter, req *http.Request) {req.Body.Close()go func() {<-req.Context().Done()fmt.Println("done")}()time.Sleep(10 * time.Second)
}

无论请求是否携带数据,当HTTP请求被取消时,服务端总能立刻感知。


6.后续

可能Go会在后续的版本优化这一实现,不需要依赖应用层的任何操作就可以感知请求被cancelled。

Go:http request cancelled 服务端感知相关推荐

  1. 基于半同步/半反应堆线程池实现的HTTP解析服务端程序

    简介: 半同步/半反应堆线程池是通过一个线程往工作队列添加任务T,然后工作线程竞争工作队列获得任务T.HTTP请求解析服务端程序:逐行解析客户端发送来的HTTP请求然后作出HTTP回答.采用线程池就是 ...

  2. 使用WebSocket实现服务端和客户端的通信

    开发中经常会有这样的使用场景.如某个用户在一个数据上做了xx操作, 与该数据相关的用户在线上的话,需要实时接收到一条信息. 这种可以使用WebSocket来实现. 另外,对于消息,可以定义一个类进行固 ...

  3. 使用 Netty 实现服务端和客户端的点对多点通信——聊天室功能

    一 需求 1 监听所有客户端的上线和下线. 2 将某一个客户端的上线和离线情况,转告给其他客户端"客户端XX上/下线" 3 客户端先将消息发送给服务端,服务端再将此消息转发给所有客 ...

  4. Request对象 --web浏览器向web服务端的请求

    参考文档:http://blog.csdn.net/u012986057/article/details/50570325 一]Request对象常用方法         1)StringBuffer ...

  5. 浏览器及时感知服务端数据变化的方式

    需求 在公司,有一个需求,是浏览器实时获取服务端数据变化,然后根据变化做相应的动作.需求场景如下:手机端扫描二维码,然后获取待取件订单列表,点击取件,进行取件,同时远端打印机打印出取件小票. 技术难点 ...

  6. 客户端向服务端传送特殊字符解决方法(检测到有潜在危险的 Request.Form 值)

    当客户端向服务端传输特殊字符时报错,错误信息如下图: 在asp.net中Request提交时出现有html代码或javascript等字符串时,程序系统会认为其具有潜在危险的值.环境配置会报出&quo ...

  7. 如何在golang http服务端程序中读取2次Request Body?(转)

    转自知乎:如何在golang http服务端程序中读取2次Request Body? - 知乎 提问: 在golang http服务端程序中,我想在真正处理Request Body之前将Body中的内 ...

  8. 客户端异常断网断电,服务端该如何感知?

    本文只针对于web项目.开始想的是使用websocket进行长连接服务,但有个问题就来了,客户端异常断电.异常断网,比如说我现在把电脑炸了,网线掐了,服务端是不知道的,所以无法触发oncolse方法, ...

  9. 如果input标签中有runat=”server”,则在服务端,Request.Form接收不到

    如果input标签中有runat="server",则在服务端,Request.Form接收不到 转载于:https://www.cnblogs.com/street-boy/p/ ...

  10. 服务端编程(四)- 背景知识 - request response 深入介绍

    服务端编程专栏正在持续更新 敬请关注:) 文章目录 前言 ´・ᴗ・` HTTP request 请求方法 request method 附加信息 addtional info response 状态码 ...

最新文章

  1. 基于Tacotron汉语语音合成的开源实践
  2. Part2_4 Sqlite基础操作
  3. 小森生活一直服务器维护,《小森生活》怎么处理断线黑屏的问题 连接不上服务器解决办法...
  4. jenkins中通过git发版操作记录
  5. MySQL8 重置改root密码及开放远程访问
  6. 1025 反转链表 (25分)(最详细最简便)(套路模板)
  7. 关于'$ router' 的 undefined问题 ,是不是很痛点?!
  8. File类的用法总结,及文件过滤器的介绍。
  9. linux 性能调优
  10. 语音识别结合应用场景 各位大咖也有一些精彩论点
  11. ajax hacking,Ajax Hacking
  12. Java程序猿从笨鸟到菜鸟之(九十二)深入java虚拟机(一)——java虚拟机底层结构具体解释...
  13. html模仿登陆页面,登陆注册页面html代码(仿知乎)
  14. windows安装补丁慢 360安全卫士和腾讯电脑管家安装同样卡住 解决办法
  15. Cannot find module ‘vite-plugin-compression‘ or its corresponding type declarations
  16. 升降横移式立体车库设计(设计说明书+CAD图纸+开题报告+任务书+外文翻译+答辩相关材料)
  17. 《Android开发从零开始》——3.第一个Android程序
  18. 三星4k3d电视测试软件,技术篇 | 央视4K超高清频道怎么看?三星电视让您大饱眼福!...
  19. 偏态分布的均值与中位数关系
  20. 格斗机器人制造图纸_轮式格斗机器人的制作方法

热门文章

  1. HDU 2017 字符串统计(水~)
  2. linux下ftp客户端主动模式设置
  3. 怎么样用计算机打字,如何使用电脑键盘练习打字【拼音打字】
  4. 测试额外任务而撒旦法 速度速度发撒旦法
  5. 前端JSON格式化显示
  6. error C1083 错误
  7. 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
  8. Windows10无法修改图标字体和菜单栏字体问题 —— regedit注册表修改
  9. iOS 动画(基于Lottie封装)
  10. android 如何进入安全模式,手机怎么进入安全模式