从 Go 到 CouchDB 的大型 PUT 请求
Large PUT requests from Go to CouchDB
我一直在使用 CouchDB 和 Golang 遇到一个令人烦恼的问题。当向 Body 大小相对较大的 CouchDB 发送 POST/PUT 请求时(阈值似乎约为 8000 字节左右),连接超时并且我从 Go 收到 "tcp: use of closed network connection" 错误。
最终(一两秒后),CouchDB 发送一个 500 响应以及一个:
{"error":"unknown_error", "reason": "noproc"}
在body。 couchdb 日志也有写入的堆栈跟踪:
Stacktrace: [{couch_db,collect_results,3,
[{file,"couch_db.erl"},{line,833}]},
{couch_db,write_and_commit,4,
[{file,"couch_db.erl"},{line,845}]},
{couch_db,update_docs,4,
[{file,"couch_db.erl"},{line,782}]},
{couch_db,update_doc,4,
[{file,"couch_db.erl"},{line,426}]},
{couch_httpd_db,update_doc,6,
[{file,"couch_httpd_db.erl"},{line,753}]},
{couch_httpd_db,do_db_req,2,
[{file,"couch_httpd_db.erl"},{line,234}]},
{couch_httpd,handle_request_int,5,
[{file,"couch_httpd.erl"},{line,318}]},
{mochiweb_http,headers,5,
[{file,"mochiweb_http.erl"},{line,94}]}]
所以我写了一个快速单元测试来重现这个问题(并确保不是我的包装器导致了问题)。我是这样提出请求的:
client := &http.Client{}
req, err := http.NewRequest(
"PUT",
"http://localhost:5984/unittestdb/testdocid1",
bytes.NewReader(testBody1), //testBody1 is 10000 bytes of json object
)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
这重复了这个问题...所以我尝试将一个大的 json 文档卷曲到 couchdb。 那 奏效了。
因此,我启动了 wireshark 并检查了我的代码向 CouchDB 发出的请求,并将其与从 curl 发送的请求进行了比较。我在 Curl 请求中注意到一个额外的 header:
Expect: 100-continue
我不得不查一下那个,因为我不记得以前见过(或者也许我只是不必处理)header。然后我做了一个 google 搜索以查看 Golang http 客户端是否支持 Expect/Continue 功能,并找到了这个:https://github.com/golang/go/issues/3665
所以 Go 不支持它,至少在 1.6 之前不会。所以我认为它一定是一个模糊的东西,而不是我的问题的根源。我花了几个小时随机尝试 http.Client、交通工具等
的其他内容最终,我在 Go 中的 http 请求中手动设置了 "Expect: 100-continue" header(如果 body 大小超过了某个大小),并且...成功了。没有超时,没有错误,没有 couchdb 将堆栈跟踪记录到日志中。
现在我很困惑,如果 Go 不支持这个,它是如何工作的?我这样做只是在掩盖问题吗?或者我可以耸耸肩继续前进吗?
我怀疑 CouchDB 方面存在问题,也许我缺少某个配置设置?
我不知道答案,但我认为对您来说关键的规格是 RFC 2616 section 8.2.3。整个部分都很有趣,尤其是这个:
"There is an exception to this rule: for compatibility with RFC 2068, a server MAY send a 100 (Continue) status in response to an HTTP/1.1 PUT or POST request that does not include an Expect request-header field with the "100-continue" expectation. This exception, the purpose of which is to minimize any client processing delays associated with an undeclared wait for 100 (Continue) status, applies only to HTTP/1.1 requests, and not to requests with any other HTTP-version value."
听起来您陷入了 Go 缺少的 100 次连续实现和 CouchDB 方面的某种极端情况之间。 (无论客户端的行为如何,我都不认为 500 Internal error 是来自服务器的适当响应。)
如果 Go 客户端允许您设置,我会尝试 HTTP/1.0 作为解决方法。