技术|常见HTTP错误码模拟

最近发现部分同学虽然知道HTTP错误码,但对产生的具体原因并不清楚,所以我打算对比较常见的错误码进行模拟,帮助大家理解。
环境搭建 首先我们先搭建一个用于模拟的环境,公司架构一般分为负载均衡层和服务层,负载均衡我们使用nginx,服务使用Go。
Go
本次使用Go代码,所有代码位于:https://github.com/shidawuhen/asap/blob/master/controller/various/httpcode.go
服务监听端口为:8082
访问:http://127.0.0.1:8082/ping 可看到返回
技术|常见HTTP错误码模拟
文章图片

Nginx
安装 Mac下安装Nginx命令:brew install nginx
Nginx安装位置为:/usr/local/etc/nginx
启动 命令行执行:nginx。没有报错表示执行成功。
访问 http://localhost:8080/
技术|常见HTTP错误码模拟
文章图片

配置 修改Nginx的config文件,将请求代理到Go服务。

location / {#请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。 proxy_passhttp://127.0.0.1:8082; #请求转向mysvr 定义的服务器列表 proxy_set_header Host $host; proxy_set_header Connection ""; #设置Connection为长连接(默认为no) proxy_connect_timeout 30; #与upstream server的连接超时时间 proxy_read_timeout 60s; #nginx会等待多长时间来获得请求的响应 proxy_send_timeout 12s; #发送请求给upstream服务器的超时时间 proxy_http_version 1.1; }

Nginx加载新的配置:nginx -s reload
请求ping,可看到请求转发到Go服务。
技术|常见HTTP错误码模拟
文章图片

技术|常见HTTP错误码模拟
文章图片

200-OK
我们先看一下正常情况。
请求
http://localhost:8080/httpcode/code200
代码
对应的Go代码为:
func Code200(c *gin.Context) {c.String(http.StatusOK, "ok") }

返回
技术|常见HTTP错误码模拟
文章图片

500-Internal Server Error
说明
500 (服务器内部错误):服务器遇到错误,无法完成请求。
内部服务错误:服务器遭遇到了一个预料之外的情况,这个情况阻止了它完成请求的处理。一般是语法错误或者服务panic。
请求
http://localhost:8080/httpcode/code500
代码
func Code500(c *gin.Context) {panic("1") c.String(http.StatusOK, "ok") }

返回
技术|常见HTTP错误码模拟
文章图片

技术|常见HTTP错误码模拟
文章图片

原因
主要是因为Gin里使用了默认Recover中间件,panic后将状态设置为500
func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine }func defaultHandleRecovery(c *Context, err interface{ }) {c.AbortWithStatus(http.StatusInternalServerError) }

504-Gateway Time-out 说明
504 (网关超时): 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
网关超时:服务器,当作为一个网关或代理工作时,没有从上游服务器接收到 为了完成请求 所需访问的 及时的响应(数据)。也就是说,nginx作为网关,为了完成请求,它必须获取到上游服务器的数据,但是上游服务器在规定时间内没有给到这些数据,所以nginx无法access到这些数据。也就是上游服务器响应超时了。
网关收到请求后,要调用其它服务器完成工作,其它服务器是上游服务器。
请求
http://localhost:8080/httpcode/code504
代码
func Code504(c *gin.Context) {time.Sleep(time.Second * 100) c.String(http.StatusOK, "ok") }

返回
技术|常见HTTP错误码模拟
文章图片

原因
Nginx配置的超时时间为60s,但Go服务在100s后才能响应,所以60s后,Nginx没有收到响应,就直接返回504。
502-Bad Gateway 说明
502 (错误网关):服务器作为网关或代理,从上游服务器收到无效响应。
网关错误:服务器,当它作为一个网关或者代理去工作,尝试着处理请求时,它从它所进入的到达的 上游服务器 处,接收到了一个非法、无效的响应。
所谓的非法、无效,是指预期之外的响应。
请求
http://localhost:8080/httpcode/code502
代码
Go服务down掉 对于这种情况,Nginx直接返回502。
Go主动关闭连接 方案一:新起服务 这个操作需要服务端收到请求后,立即将conn Close掉:
package mainimport ( "fmt" "net" )func main() {ln, err := net.Listen("tcp", "127.0.0.1:8082") if err != nil {return } go func() {for {c, err := ln.Accept() fmt.Println("Accept") if err != nil {break } c.Close() } }() select { } }

方案二:调整writetimeout main函数中不使用run,使用自己构建的server,并设置WriteTimeout为1s。
//r.Run(":8082") server := http.Server{Addr:":8082", WriteTimeout: time.Second * 1, ReadTimeout:time.Second * 10, IdleTimeout:time.Second * 10, Handler:r, }server.ListenAndServe()

同时让函数休眠2s。
func Code502(c *gin.Context) {//方案二 time.Sleep(time.Second * 2) c.String(http.StatusOK, "ok") }

返回
技术|常见HTTP错误码模拟
文章图片

503-Service Unavailable
说明
503 (服务不可用):服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
一般来说,出现503错误多半是因为网站访问量大,造成了流量超限或者并发数大引起的资源超限出现的错误。
模拟这种情况,需要改很多系统参数,风险较大,本次不做模拟。如果大家有别的模拟方法,可以告诉我。
499-CLIENT CLOSED REQUEST 说明
499(客户端关闭请求):一个被nginx引入的非标准状态码,对应的场景是,当nginx正在处理请求时,客户端关闭了HTTP连接。
引申出来,就是当HTTP请求到达Nginx后,该请求还在被处理的状态时,浏览器的请求超时时间到了,主动关闭了连接。但是,此状态码在浏览器请求时几乎不可见,因为浏览器默认的超时时间会很长。多见于服务之间的调用,在业务架构中常常会分层设计,拆分为不同的子系统或者微服务,这样系统之间就会常常通过http方式来请求,并且会设置每次请求的超时时间,当请求在请求时间内所调用的上游服务无返回,则会主动关闭连接,上游服务日志中会记录一条499。
请求
因为浏览器超时时间较长,所以使用curl命令,设置三秒超时:
curl -i -m 3 http://127.0.0.1:8080/httpcode/code499
代码
func Code499(c *gin.Context) {time.Sleep(time.Second * 100) c.String(http.StatusOK, "ok") }

返回
通过nginx -V查看Nginx日志位置,找到access.log:
127.0.0.1 - - [28/Nov/2021:23:13:09 +0800] "GET /httpcode/code499 HTTP/1.1" 499 0 "-" "curl/7.64.1"

总结 虽然这次模拟了各种情况,但更加详细的原因在Nginx源码和Go源码中,大家有兴趣的话可以深入了解。
另外,在模拟过程中,使用的例子只是导致出现对应状态码的其中一些原因,还有其它的一些原因,大家可以寻找。
在模拟的过程中,自己也发现了很多新的知识点,还是很有趣的。
资料
  1. 常见HTTP错误代码大全
  2. http 5**系列状态码详解
  3. HTTP状态码 499 / 500 / 502 / 504
  4. Golang之HTTP server 502问题分析
  5. Gin设置Timeout
  6. HTTP CODE 状态码500|502|504分析
  7. mac下安装nginx
  8. Golang之HTTP server 502问题分析
  9. 记录一次线上502排查过程
  10. Golang 优化之路——HTTP长连接
  11. 客户端主动断开连接_Go实现客户端和服务器抓包分析TCP三次握手和断开操作
  12. 服务端主动终止连接的情况分析
  13. Gin IdleTimeout本地验证
  14. Nginx 503错误总结
  15. 低配终端环境下如何模拟大规模负载
最后 大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
技术|常见HTTP错误码模拟
文章图片

往期文章回顾:
  1. 设计模式
  2. 招聘
  3. 思考
  4. 存储
  5. 算法系列
  6. 读书笔记
  7. 小工具
  8. 架构
  9. 网络
  10. 【技术|常见HTTP错误码模拟】Go语言

    推荐阅读