当前位置: 代码网 > it编程>前端脚本>Golang > go进行http请求偶发EOF问题分析

go进行http请求偶发EOF问题分析

2025年02月14日 Golang 我要评论
简介go使用连接池进行http请求,一般都能请求成功,但偶然会出现请求失败返回eof错误的情况;类似java的org.apache.http.nohttpresponseexception分析客户端通

简介

go使用连接池进行http请求,一般都能请求成功,但偶然会出现请求失败返回eof错误的情况;类似java的org.apache.http.nohttpresponseexception

分析

客户端通过keep alive机制保障性能,简单理解就是复用tcp五元会话,用于进行多次http请求;但如果服务端的空闲保活时间是10s,在第一次请求完的10s进行了第二次请求,此时客户端认为连接仍然有效继续发起请求,但服务端发出了fin报文不再对此连接进行响应,从而导致客户端请求失败并出现eof错误。

偶发就是因为两个时间要恰好碰到一起才可能触发这个问题

  • 服务器发送了fin报文,但是客户端还没有收到,但是客户端已经发送了请求数据包
  • 如果在服务器超时前发起了请求,那连接此时还可用,正常
  • 如果在服务器超时后发起了请求,那连接已经完成fin关闭流程,请求会触发新的会话,正常

解决方式:

  • 在出现eof的时候,进行重试,此时会触发新的五元组连接进行请求(推荐)
  • 设置客户端的空闲保活时间小于服务端的空闲保活时间
    • idleconntimeout 此时客户端会在超时时主动向服务端发送rst进行连接重置

代码

package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"time"
)

func main() {
	// 创建自定义的 transport,设置连接池参数
	tr := &http.transport{
		tlsclientconfig: &tls.config{
			insecureskipverify: true, // 忽略 tls 证书验证
		},
		maxidleconns:        1,                // 限制最大空闲连接数为1
		maxidleconnsperhost: 1,                // 限制每个host最大空闲连接数为1
		idleconntimeout:     20 * time.second, // 本地空闲连接超时设置为20s
		disablekeepalives:   false,            // 启用 keep-alive
		maxconnsperhost:     1,                // 限制每个host的最大连接数为1,强制复用连接
		forceattempthttp2:   false,            // 禁用 http/2
	}

	// 创建 http 客户端
	client := &http.client{
		transport: tr,
		timeout:   5 * time.second, // 设置请求超时时间
	}

	// 准备请求参数
	url := "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977"
	cookie := "session-id=f416b188c91bc72a06853b362d5cb7b3a6b68a43"

	// 准备请求体数据
	requestbody := map[string]string{
		"sys_name": "n-guard",
	}
	// 将 map 转换为 json
	jsonbody, err := json.marshal(requestbody)
	if err != nil {
		fmt.printf("json 编码失败: %v\n", err)
	}

	// 循环发送请求,模拟使用已关闭的连接
	for i := 0; i < 5; i++ { // 只测试两次请求即可

		// 每次请求都创建新的 bytes.buffer,确保 body 可以重复读取
		bodyreader := bytes.newbuffer(jsonbody)

		req, err := http.newrequest("put", url, bodyreader)
		if err != nil {
			fmt.printf("创建请求失败: %v\n", err)
			continue
		}

		// 设置 content-length
		req.contentlength = int64(len(jsonbody))
		// 设置请求头
		req.header.set("cookie", cookie)
		req.header.set("content-type", "application/json")

		fmt.printf("发送第 %d 个请求...\n", i+1)
		// 发送请求
		resp, err := client.do(req)
		if err != nil {
			fmt.printf("请求失败: %v\n", err)
			if errors.is(err, io.eof) {
				fmt.printf("连接不再可用: 重试:新的五元重新发起连接\n")
				bodyreader := bytes.newbuffer(jsonbody)

				reqretry, err := http.newrequest("put", url, bodyreader)
				if err != nil {
					fmt.printf("创建请求失败: %v\n", err)
					continue
				}

				// 设置 content-length
				reqretry.contentlength = int64(len(jsonbody))
				// 设置请求头
				reqretry.header.set("cookie", cookie)
				reqretry.header.set("content-type", "application/json")

				resp, err = client.do(reqretry)
				if err != nil {
					fmt.printf("err:\n", err)
					continue
				}
			} else {
				continue
			}
		}

		// 读取响应
		body, err := ioutil.readall(resp.body)
		if err != nil {
			fmt.printf("读取响应失败: %v\n", err)
		}
		resp.body.close()

		fmt.printf("请求 %d - 状态码: %d, 响应: %s\n", i+1, resp.statuscode, string(body))

		fmt.println("等待10秒后发送第二个请求...")
		time.sleep(10 * time.second) // 等待10秒,此时服务端已经关闭连接(10s)
		time.sleep(500 * time.millisecond)
	}
}

运行

[xiaofeng@localhost httpkeepalive]$ go run main.go 
发送第 1 个请求...
请求 1 - 状态码: 200, 响应: {"code":0,"result":"0","message":"成功"}
等待10秒后发送第二个请求...
发送第 2 个请求...
请求 2 - 状态码: 200, 响应: {"code":0,"result":"0","message":"成功"}
等待10秒后发送第二个请求...
发送第 3 个请求...
请求失败: put "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977": eof
连接不再可用: 重试:新的五元重新发起连接
请求 3 - 状态码: 200, 响应: {"code":0,"result":"0","message":"成功"}
等待10秒后发送第二个请求...
发送第 4 个请求...
请求失败: put "https://192.168.24.70:2018/api/zguard/sysmng/syscfg/basecfg/sysname/651d5b9f-225b-4c8c-9f06-80bfad3fa977": eof
连接不再可用: 重试:新的五元重新发起连接
请求 4 - 状态码: 200, 响应: {"code":0,"result":"0","message":"成功"}
等待10秒后发送第二个请求...
发送第 5 个请求...
请求 5 - 状态码: 200, 响应: {"code":0,"result":"0","message":"成功"}
等待10秒后发送第二个请求...

报文

到此这篇关于go进行http请求偶发eof问题分析的文章就介绍到这了,更多相关go http请求偶发eof内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com