我对 Erlang inets 的 keep-alive 功能以及 ibrowse http 客户端(最新版本)感到困惑。根据RFC:
8.1.2.2 Pipelining
A client that supports persistent connections MAY "pipeline" its
requests (i.e., send multiple requests without waiting for each
response). A server MUST send its responses to those requests in the
same order that the requests were received.
Clients which assume persistent connections and pipeline immediately
after connection establishment SHOULD be prepared to retry their
connection if the first pipelined attempt fails. If a client does
such a retry, it MUST NOT pipeline before it knows the connection is
persistent. Clients MUST also be prepared to resend their requests if
the server closes the connection before sending all of the
corresponding responses.
Clients SHOULD NOT pipeline requests using non-idempotent methods or
non-idempotent sequences of methods (see section 9.1.2). Otherwise, a
premature termination of the transport connection could lead to
indeterminate results. A client wishing to send a non-idempotent
request SHOULD wait to send that request until it has received the
response status for the previous request.
有两种模式,'pipeline' 和 'keep-alive',基于持久连接,它们的区别在于当使用 'pipeline' 时,可以在同一个连接上发送请求,而无需等待前一个的响应. 换句话说,当使用“keep-alive”时,我们应该在同一连接上发送其他请求之前等待每个响应。
结果,我认为“keep-alive”实现应该是这样的:
%% each httpc_handler(gen_server) maintains a persistent connection
%% with type of keep-alive
httpc_handler:handle_request(Request, keepalive) ->
%% check if there is request not finished yet
case is_there_old_request() of
true -> queue:in(RQueue, Request);
%% then send the request
false -> gen_tcp:send(Request)
end.
httpc_handler:handle_response(Response) ->
send_back(Response),
case queue:out(RQueue) of
undefined -> ...;
%% send the next request
Request -> gen_tcp:send(Request), ...
end.
但实际上 inets 和 ibrowse 的实现是这样的:
httpc_handler:handle_request(Request, keepalive) ->
%% send without check
gen_tcp:send(Request),
case is_there_old_request() of
true -> queue:in(RQueue, Request);
false -> ...
end.
httpc_handler:handle_response(Response) ->
send_back(Response),
case queue:out(RQueue) of
undefined -> ...;
Request -> receive_next_response
end.
我认为这实际上是没有幂等限制的“管道”模式。
那么,我错过了代码中的某些内容,或者只是误解了 RFC?