1

我对 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?

4

2 回答 2

2

这个问题已经被inets httpc: Corrected separation of pipelining and ..Erlang/OTP R16 中的 commit 解决了。

于 2013-03-13T14:26:24.960 回答
0

看了最新的OTP源码,幂等逻辑其实在httpc_manager:select_session. 如果请求不是幂等的,则返回no_connection,提示系统创建新连接。整个过程中还散布着更多逻辑,但我确实确定了用于处理流水线和保持活动的单独函数httpc_handler

于 2013-01-24T04:16:24.960 回答