You are viewing a plain text version of this content. The canonical link for it is here.
Posted to httpclient-users@hc.apache.org by Michael Osipov <mi...@apache.org> on 2015/01/23 23:36:27 UTC

Caching stateful HTTP clients in a pool

Hi folks,

I am looking for a design decision to cache session information for a 
request workflow embedded in a SOAP/REST-based webapp.

Here is a little background how the workflow is structured and what I am 
trying to solve:

Note: I have read the entire tutorial and am somewhat overwhelmed by the 
amount of features the HttpClient has to offer, so I beg your patience.

Workflow:
1. Client issues a request either with SOAP or REST with an object id
2. Server accepts request and does the following:
  2.1. Create a client with a cookie manager
  2.2. Login to a backend server A (HTTP request, server A)
  2.3. Read object information (HTTP request, server A)
  2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request, 
server A)
  2.5. Download file behind object id to disk from remote store (HTTP 
request, server B)
  2.6. perform a logout (HTTP request, server A)
  2.7. Close the client
3. Respond to client with object information and the stream from the 
saved file

The entire communication with the server A is stateful with a session 
cookie, all responses are fully consumed either with a response handler 
or with a try-with-resources clause.

The problem: the login/logout requests consume around 10 seconds, 
sometimes even more. I'd like to avoid this every time.

My idea was to come around this with Commons Pool 2 which I have already 
used for some other session pooling successfully.

Requirements: it is not only necessary to maintain the cookie store but 
a client id header which has a randomly generated integer assigned plus 
a request counter. The maintained session has to be used by one thread 
at the same time, same applies for the header otherwise server A would 
queue all requests. The pool would guarantee that.

Two possible solutions come to my mind:

1. Save the client id and cookie store in the pool and always create a 
new client.
2. Save the client id along with the authenticated HttpClient and reuse it.

Both solutions will always increment the request counter of course.

 From HttpClient's view which makes more sense here? What if I go with 
option 2, will I have stale connections, clients, etc.?

Thanks,

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Oleg Kalnichevski <ol...@apache.org>.
On Wed, 2015-02-11 at 19:36 +0100, Michael Osipov wrote:
> Am 2015-01-26 um 10:18 schrieb Oleg Kalnichevski:
> > On Sun, 2015-01-25 at 22:24 +0100, Michael Osipov wrote:
> >> Am 2015-01-25 um 18:12 schrieb Oleg Kalnichevski:
> >>> On Sat, 2015-01-24 at 16:50 +0100, Michael Osipov wrote:
> >>>> Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
> >>>>> On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
> >>>>>> Hi folks,
> >>>>>>
> >>>>>> I am looking for a design decision to cache session information for a
> >>>>>> request workflow embedded in a SOAP/REST-based webapp.
> >>>>>>
> >>>>>> Here is a little background how the workflow is structured and what I am
> >>>>>> trying to solve:
> >>>>>>
> >>>>>> Note: I have read the entire tutorial and am somewhat overwhelmed by the
> >>>>>> amount of features the HttpClient has to offer, so I beg your patience.
> >>>>>>
> >>>>>> Workflow:
> >>>>>> 1. Client issues a request either with SOAP or REST with an object id
> >>>>>> 2. Server accepts request and does the following:
> >>>>>>      2.1. Create a client with a cookie manager
> >>>>>>      2.2. Login to a backend server A (HTTP request, server A)
> >>>>>>      2.3. Read object information (HTTP request, server A)
> >>>>>>      2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
> >>>>>> server A)
> >>>>>>      2.5. Download file behind object id to disk from remote store (HTTP
> >>>>>> request, server B)
> >>>>>>      2.6. perform a logout (HTTP request, server A)
> >>>>>>      2.7. Close the client
> >>>>>> 3. Respond to client with object information and the stream from the
> >>>>>> saved file
> >>>>>>
> >>>>>> The entire communication with the server A is stateful with a session
> >>>>>> cookie, all responses are fully consumed either with a response handler
> >>>>>> or with a try-with-resources clause.
> >>>>>>
> >>>>>> The problem: the login/logout requests consume around 10 seconds,
> >>>>>> sometimes even more. I'd like to avoid this every time.
> >>>>>>
> >>>>>> My idea was to come around this with Commons Pool 2 which I have already
> >>>>>> used for some other session pooling successfully.
> >>>>>>
> >>>>>> Requirements: it is not only necessary to maintain the cookie store but
> >>>>>> a client id header which has a randomly generated integer assigned plus
> >>>>>> a request counter. The maintained session has to be used by one thread
> >>>>>> at the same time, same applies for the header otherwise server A would
> >>>>>> queue all requests. The pool would guarantee that.
> >>>>>>
> >>>>>> Two possible solutions come to my mind:
> >>>>>>
> >>>>>> 1. Save the client id and cookie store in the pool and always create a
> >>>>>> new client.
> >>>>>> 2. Save the client id along with the authenticated HttpClient and reuse it.
> >>>>>>
> >>>>>> Both solutions will always increment the request counter of course.
> >>>>>>
> >>>>>>     From HttpClient's view which makes more sense here? What if I go with
> >>>>>> option 2, will I have stale connections, clients, etc.?
> >>>>>>
> >>>>>> Thanks,
> >>>>>>
> >>>>>> Michael
> >>>>>>
> >>>>>
> >>>>> Hi Michael
> >>>>>
> >>>>> How about a slightly different approach? Consider using one HttpClient
> >>>>> for all sessions and maintaining a pool of HttpContext instances with a
> >>>>> cookie store / session id.
> >>>>
> >>>> Hi,
> >>>>
> >>>> thanks for this tip, you are proposing a very interesting approach which
> >>>> I have not considered due to missing knowledge.
> >>>>
> >>>> I would have to do the following/solve following questions:
> >>>>
> >>>> 1. Create a vanilla HttpClient in my beans.xml as a singleton. Please
> >>>> note that the web application can run for days if not even for weeks and
> >>>> the HttpClient would remain open. That is not a problem?
> >>>
> >>> No, it should not be. You should however consider employing a connection
> >>> evictor thread as described here:
> >>>
> >>> http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e405
> >>
> >> If I won't I might end up in stale connections right? This is at least
> >> what the docs tell me.
> >>
> >
> > No matter what you do there is always a possibility of a connection
> > getting 'stale' immediately after passing a staleness check. A
> > combination of a reasonable eviction policy enforced by a background
> > thread and a reasonable recovery (request retrial) policy should be
> > sufficient to ensure near 100% reliability of request execution in my
> > opinion.
> 
> Hi Oleg,
> 
> first of all, thank you for all the advisories you have given me. They 
> helped a lot. My implementation is no working and my consumers will 
> start their tests shortly. I have even achieved to pipe the request 
> directly from the consumer to the backend server w/o caching as file on 
> the machine. I am tremendously satisfied by the features HttpClient offers.
> 

I am glad I was of some help.

> Prior to this, I have implemented everything as a JMeter testplan where 
> HttpClient did a great job.
> 
> I have promised to share my approach, this will happen shortly. 
> Everyone's invited to comment on then.
> 
> I am still unclear about the stale conn evitor thread exampled in the 
> tutorial [1]. Why is this sample code even mentioned if there is already 
> an evitor [2], [3] built-in?

Some people might still want to enforce a slightly different eviction
policy or settings. This naturally requites a custom evitor thread.    

>  The tutorial implies to me that I need to 
> write and hook in that background thread.
> 

One can always add more content to the tutorial.

Oleg



---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Michael Osipov <mi...@apache.org>.
Am 2015-01-26 um 10:18 schrieb Oleg Kalnichevski:
> On Sun, 2015-01-25 at 22:24 +0100, Michael Osipov wrote:
>> Am 2015-01-25 um 18:12 schrieb Oleg Kalnichevski:
>>> On Sat, 2015-01-24 at 16:50 +0100, Michael Osipov wrote:
>>>> Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
>>>>> On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
>>>>>> Hi folks,
>>>>>>
>>>>>> I am looking for a design decision to cache session information for a
>>>>>> request workflow embedded in a SOAP/REST-based webapp.
>>>>>>
>>>>>> Here is a little background how the workflow is structured and what I am
>>>>>> trying to solve:
>>>>>>
>>>>>> Note: I have read the entire tutorial and am somewhat overwhelmed by the
>>>>>> amount of features the HttpClient has to offer, so I beg your patience.
>>>>>>
>>>>>> Workflow:
>>>>>> 1. Client issues a request either with SOAP or REST with an object id
>>>>>> 2. Server accepts request and does the following:
>>>>>>      2.1. Create a client with a cookie manager
>>>>>>      2.2. Login to a backend server A (HTTP request, server A)
>>>>>>      2.3. Read object information (HTTP request, server A)
>>>>>>      2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
>>>>>> server A)
>>>>>>      2.5. Download file behind object id to disk from remote store (HTTP
>>>>>> request, server B)
>>>>>>      2.6. perform a logout (HTTP request, server A)
>>>>>>      2.7. Close the client
>>>>>> 3. Respond to client with object information and the stream from the
>>>>>> saved file
>>>>>>
>>>>>> The entire communication with the server A is stateful with a session
>>>>>> cookie, all responses are fully consumed either with a response handler
>>>>>> or with a try-with-resources clause.
>>>>>>
>>>>>> The problem: the login/logout requests consume around 10 seconds,
>>>>>> sometimes even more. I'd like to avoid this every time.
>>>>>>
>>>>>> My idea was to come around this with Commons Pool 2 which I have already
>>>>>> used for some other session pooling successfully.
>>>>>>
>>>>>> Requirements: it is not only necessary to maintain the cookie store but
>>>>>> a client id header which has a randomly generated integer assigned plus
>>>>>> a request counter. The maintained session has to be used by one thread
>>>>>> at the same time, same applies for the header otherwise server A would
>>>>>> queue all requests. The pool would guarantee that.
>>>>>>
>>>>>> Two possible solutions come to my mind:
>>>>>>
>>>>>> 1. Save the client id and cookie store in the pool and always create a
>>>>>> new client.
>>>>>> 2. Save the client id along with the authenticated HttpClient and reuse it.
>>>>>>
>>>>>> Both solutions will always increment the request counter of course.
>>>>>>
>>>>>>     From HttpClient's view which makes more sense here? What if I go with
>>>>>> option 2, will I have stale connections, clients, etc.?
>>>>>>
>>>>>> Thanks,
>>>>>>
>>>>>> Michael
>>>>>>
>>>>>
>>>>> Hi Michael
>>>>>
>>>>> How about a slightly different approach? Consider using one HttpClient
>>>>> for all sessions and maintaining a pool of HttpContext instances with a
>>>>> cookie store / session id.
>>>>
>>>> Hi,
>>>>
>>>> thanks for this tip, you are proposing a very interesting approach which
>>>> I have not considered due to missing knowledge.
>>>>
>>>> I would have to do the following/solve following questions:
>>>>
>>>> 1. Create a vanilla HttpClient in my beans.xml as a singleton. Please
>>>> note that the web application can run for days if not even for weeks and
>>>> the HttpClient would remain open. That is not a problem?
>>>
>>> No, it should not be. You should however consider employing a connection
>>> evictor thread as described here:
>>>
>>> http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e405
>>
>> If I won't I might end up in stale connections right? This is at least
>> what the docs tell me.
>>
>
> No matter what you do there is always a possibility of a connection
> getting 'stale' immediately after passing a staleness check. A
> combination of a reasonable eviction policy enforced by a background
> thread and a reasonable recovery (request retrial) policy should be
> sufficient to ensure near 100% reliability of request execution in my
> opinion.

Hi Oleg,

first of all, thank you for all the advisories you have given me. They 
helped a lot. My implementation is no working and my consumers will 
start their tests shortly. I have even achieved to pipe the request 
directly from the consumer to the backend server w/o caching as file on 
the machine. I am tremendously satisfied by the features HttpClient offers.

Prior to this, I have implemented everything as a JMeter testplan where 
HttpClient did a great job.

I have promised to share my approach, this will happen shortly. 
Everyone's invited to comment on then.

I am still unclear about the stale conn evitor thread exampled in the 
tutorial [1]. Why is this sample code even mentioned if there is already 
an evitor [2], [3] built-in? The tutorial implies to me that I need to 
write and hook in that background thread.


[1] 
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e405
[2] 
https://github.com/apache/httpclient/blob/5a192e2bf46bc749d8771a6f16cd40bc34146f24/httpclient/src/main/java/org/apache/http/impl/client/IdleConnectionEvictor.java
[3] 
https://github.com/apache/httpclient/blob/529e1373a5a7cc8483862fbfd453f2c567b6362d/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java#L1167-L1188

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Oleg Kalnichevski <ol...@apache.org>.
On Sun, 2015-01-25 at 22:24 +0100, Michael Osipov wrote:
> Am 2015-01-25 um 18:12 schrieb Oleg Kalnichevski:
> > On Sat, 2015-01-24 at 16:50 +0100, Michael Osipov wrote:
> >> Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
> >>> On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
> >>>> Hi folks,
> >>>>
> >>>> I am looking for a design decision to cache session information for a
> >>>> request workflow embedded in a SOAP/REST-based webapp.
> >>>>
> >>>> Here is a little background how the workflow is structured and what I am
> >>>> trying to solve:
> >>>>
> >>>> Note: I have read the entire tutorial and am somewhat overwhelmed by the
> >>>> amount of features the HttpClient has to offer, so I beg your patience.
> >>>>
> >>>> Workflow:
> >>>> 1. Client issues a request either with SOAP or REST with an object id
> >>>> 2. Server accepts request and does the following:
> >>>>     2.1. Create a client with a cookie manager
> >>>>     2.2. Login to a backend server A (HTTP request, server A)
> >>>>     2.3. Read object information (HTTP request, server A)
> >>>>     2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
> >>>> server A)
> >>>>     2.5. Download file behind object id to disk from remote store (HTTP
> >>>> request, server B)
> >>>>     2.6. perform a logout (HTTP request, server A)
> >>>>     2.7. Close the client
> >>>> 3. Respond to client with object information and the stream from the
> >>>> saved file
> >>>>
> >>>> The entire communication with the server A is stateful with a session
> >>>> cookie, all responses are fully consumed either with a response handler
> >>>> or with a try-with-resources clause.
> >>>>
> >>>> The problem: the login/logout requests consume around 10 seconds,
> >>>> sometimes even more. I'd like to avoid this every time.
> >>>>
> >>>> My idea was to come around this with Commons Pool 2 which I have already
> >>>> used for some other session pooling successfully.
> >>>>
> >>>> Requirements: it is not only necessary to maintain the cookie store but
> >>>> a client id header which has a randomly generated integer assigned plus
> >>>> a request counter. The maintained session has to be used by one thread
> >>>> at the same time, same applies for the header otherwise server A would
> >>>> queue all requests. The pool would guarantee that.
> >>>>
> >>>> Two possible solutions come to my mind:
> >>>>
> >>>> 1. Save the client id and cookie store in the pool and always create a
> >>>> new client.
> >>>> 2. Save the client id along with the authenticated HttpClient and reuse it.
> >>>>
> >>>> Both solutions will always increment the request counter of course.
> >>>>
> >>>>    From HttpClient's view which makes more sense here? What if I go with
> >>>> option 2, will I have stale connections, clients, etc.?
> >>>>
> >>>> Thanks,
> >>>>
> >>>> Michael
> >>>>
> >>>
> >>> Hi Michael
> >>>
> >>> How about a slightly different approach? Consider using one HttpClient
> >>> for all sessions and maintaining a pool of HttpContext instances with a
> >>> cookie store / session id.
> >>
> >> Hi,
> >>
> >> thanks for this tip, you are proposing a very interesting approach which
> >> I have not considered due to missing knowledge.
> >>
> >> I would have to do the following/solve following questions:
> >>
> >> 1. Create a vanilla HttpClient in my beans.xml as a singleton. Please
> >> note that the web application can run for days if not even for weeks and
> >> the HttpClient would remain open. That is not a problem?
> >
> > No, it should not be. You should however consider employing a connection
> > evictor thread as described here:
> >
> > http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e405
> 
> If I won't I might end up in stale connections right? This is at least 
> what the docs tell me.
> 

No matter what you do there is always a possibility of a connection
getting 'stale' immediately after passing a staleness check. A
combination of a reasonable eviction policy enforced by a background
thread and a reasonable recovery (request retrial) policy should be
sufficient to ensure near 100% reliability of request execution in my
opinion.

Oleg


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Michael Osipov <mi...@apache.org>.
Am 2015-01-25 um 18:12 schrieb Oleg Kalnichevski:
> On Sat, 2015-01-24 at 16:50 +0100, Michael Osipov wrote:
>> Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
>>> On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
>>>> Hi folks,
>>>>
>>>> I am looking for a design decision to cache session information for a
>>>> request workflow embedded in a SOAP/REST-based webapp.
>>>>
>>>> Here is a little background how the workflow is structured and what I am
>>>> trying to solve:
>>>>
>>>> Note: I have read the entire tutorial and am somewhat overwhelmed by the
>>>> amount of features the HttpClient has to offer, so I beg your patience.
>>>>
>>>> Workflow:
>>>> 1. Client issues a request either with SOAP or REST with an object id
>>>> 2. Server accepts request and does the following:
>>>>     2.1. Create a client with a cookie manager
>>>>     2.2. Login to a backend server A (HTTP request, server A)
>>>>     2.3. Read object information (HTTP request, server A)
>>>>     2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
>>>> server A)
>>>>     2.5. Download file behind object id to disk from remote store (HTTP
>>>> request, server B)
>>>>     2.6. perform a logout (HTTP request, server A)
>>>>     2.7. Close the client
>>>> 3. Respond to client with object information and the stream from the
>>>> saved file
>>>>
>>>> The entire communication with the server A is stateful with a session
>>>> cookie, all responses are fully consumed either with a response handler
>>>> or with a try-with-resources clause.
>>>>
>>>> The problem: the login/logout requests consume around 10 seconds,
>>>> sometimes even more. I'd like to avoid this every time.
>>>>
>>>> My idea was to come around this with Commons Pool 2 which I have already
>>>> used for some other session pooling successfully.
>>>>
>>>> Requirements: it is not only necessary to maintain the cookie store but
>>>> a client id header which has a randomly generated integer assigned plus
>>>> a request counter. The maintained session has to be used by one thread
>>>> at the same time, same applies for the header otherwise server A would
>>>> queue all requests. The pool would guarantee that.
>>>>
>>>> Two possible solutions come to my mind:
>>>>
>>>> 1. Save the client id and cookie store in the pool and always create a
>>>> new client.
>>>> 2. Save the client id along with the authenticated HttpClient and reuse it.
>>>>
>>>> Both solutions will always increment the request counter of course.
>>>>
>>>>    From HttpClient's view which makes more sense here? What if I go with
>>>> option 2, will I have stale connections, clients, etc.?
>>>>
>>>> Thanks,
>>>>
>>>> Michael
>>>>
>>>
>>> Hi Michael
>>>
>>> How about a slightly different approach? Consider using one HttpClient
>>> for all sessions and maintaining a pool of HttpContext instances with a
>>> cookie store / session id.
>>
>> Hi,
>>
>> thanks for this tip, you are proposing a very interesting approach which
>> I have not considered due to missing knowledge.
>>
>> I would have to do the following/solve following questions:
>>
>> 1. Create a vanilla HttpClient in my beans.xml as a singleton. Please
>> note that the web application can run for days if not even for weeks and
>> the HttpClient would remain open. That is not a problem?
>
> No, it should not be. You should however consider employing a connection
> evictor thread as described here:
>
> http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e405

If I won't I might end up in stale connections right? This is at least 
what the docs tell me.

>> 2. To make the client above usable concurrently, would I need to create
>> a connection pool? I do not intend to use long-lived connections to
>> server A.
>
> You do not have to create a connection pool. HttpClient uses a pool of
> connections by default.

Yes, but it is limited to two concurrent connections by default. I have 
to tweak it anyany.

>> 3. Before login is called, I assign a cookie store to the HttpContext [1]?
>> So I would still need to carry on the cookie store plus increment the
>> request counter and supply the client id to the HTTP request?
>
> You just need to create a separate CookieStore per HttpContext.
>
>> I am having trouble to understand the difference between HttpContext and
>> HttpClientContext.
>
> HttpContext is effectively just a hash map. HttpClientContext is merely
> convenience class that makes that map behave more like a POJO.
>

By using it, you are probably referring to [1].

Thanks for the advisory, highly appreciated. I think, I have now the 
insight how this has to/can be coded. I'll try this in February and will 
respond how the outcome will look like.

Michael


[1] 
http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/protocol/HttpClientContext.html#setCookieStore(org.apache.http.client.CookieStore)


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Oleg Kalnichevski <ol...@apache.org>.
On Sat, 2015-01-24 at 16:50 +0100, Michael Osipov wrote:
> Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
> > On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
> >> Hi folks,
> >>
> >> I am looking for a design decision to cache session information for a
> >> request workflow embedded in a SOAP/REST-based webapp.
> >>
> >> Here is a little background how the workflow is structured and what I am
> >> trying to solve:
> >>
> >> Note: I have read the entire tutorial and am somewhat overwhelmed by the
> >> amount of features the HttpClient has to offer, so I beg your patience.
> >>
> >> Workflow:
> >> 1. Client issues a request either with SOAP or REST with an object id
> >> 2. Server accepts request and does the following:
> >>    2.1. Create a client with a cookie manager
> >>    2.2. Login to a backend server A (HTTP request, server A)
> >>    2.3. Read object information (HTTP request, server A)
> >>    2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
> >> server A)
> >>    2.5. Download file behind object id to disk from remote store (HTTP
> >> request, server B)
> >>    2.6. perform a logout (HTTP request, server A)
> >>    2.7. Close the client
> >> 3. Respond to client with object information and the stream from the
> >> saved file
> >>
> >> The entire communication with the server A is stateful with a session
> >> cookie, all responses are fully consumed either with a response handler
> >> or with a try-with-resources clause.
> >>
> >> The problem: the login/logout requests consume around 10 seconds,
> >> sometimes even more. I'd like to avoid this every time.
> >>
> >> My idea was to come around this with Commons Pool 2 which I have already
> >> used for some other session pooling successfully.
> >>
> >> Requirements: it is not only necessary to maintain the cookie store but
> >> a client id header which has a randomly generated integer assigned plus
> >> a request counter. The maintained session has to be used by one thread
> >> at the same time, same applies for the header otherwise server A would
> >> queue all requests. The pool would guarantee that.
> >>
> >> Two possible solutions come to my mind:
> >>
> >> 1. Save the client id and cookie store in the pool and always create a
> >> new client.
> >> 2. Save the client id along with the authenticated HttpClient and reuse it.
> >>
> >> Both solutions will always increment the request counter of course.
> >>
> >>   From HttpClient's view which makes more sense here? What if I go with
> >> option 2, will I have stale connections, clients, etc.?
> >>
> >> Thanks,
> >>
> >> Michael
> >>
> >
> > Hi Michael
> >
> > How about a slightly different approach? Consider using one HttpClient
> > for all sessions and maintaining a pool of HttpContext instances with a
> > cookie store / session id.
> 
> Hi,
> 
> thanks for this tip, you are proposing a very interesting approach which 
> I have not considered due to missing knowledge.
> 
> I would have to do the following/solve following questions:
> 
> 1. Create a vanilla HttpClient in my beans.xml as a singleton. Please 
> note that the web application can run for days if not even for weeks and 
> the HttpClient would remain open. That is not a problem?

No, it should not be. You should however consider employing a connection
evictor thread as described here:

http://hc.apache.org/httpcomponents-client-4.3.x/tutorial/html/connmgmt.html#d5e405


> 2. To make the client above usable concurrently, would I need to create 
> a connection pool? I do not intend to use long-lived connections to 
> server A.

You do not have to create a connection pool. HttpClient uses a pool of
connections by default.

> 3. Before login is called, I assign a cookie store to the HttpContext [1]?
> So I would still need to carry on the cookie store plus increment the 
> request counter and supply the client id to the HTTP request?

You just need to create a separate CookieStore per HttpContext.

>  Maybe even 
> with an interceptor [2]?
> 

Quite likely.

> I am having trouble to understand the difference between HttpContext and 
> HttpClientContext.

HttpContext is effectively just a hash map. HttpClientContext is merely
convenience class that makes that map behave more like a POJO.

>  Especially moving the bits off the client to the 
> requests itself though the manual is really good on this one.
> 
> Moreover, I am also inclined to create a seperate client for server B 
> because this one is stateless or doesn't it make a difference because 
> the connecion pool is configurable per target host?
> 

In my opinion this should not make a difference.

Cheers

Oleg



---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Michael Osipov <mi...@apache.org>.
Am 2015-01-24 um 12:29 schrieb Oleg Kalnichevski:
> On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
>> Hi folks,
>>
>> I am looking for a design decision to cache session information for a
>> request workflow embedded in a SOAP/REST-based webapp.
>>
>> Here is a little background how the workflow is structured and what I am
>> trying to solve:
>>
>> Note: I have read the entire tutorial and am somewhat overwhelmed by the
>> amount of features the HttpClient has to offer, so I beg your patience.
>>
>> Workflow:
>> 1. Client issues a request either with SOAP or REST with an object id
>> 2. Server accepts request and does the following:
>>    2.1. Create a client with a cookie manager
>>    2.2. Login to a backend server A (HTTP request, server A)
>>    2.3. Read object information (HTTP request, server A)
>>    2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request,
>> server A)
>>    2.5. Download file behind object id to disk from remote store (HTTP
>> request, server B)
>>    2.6. perform a logout (HTTP request, server A)
>>    2.7. Close the client
>> 3. Respond to client with object information and the stream from the
>> saved file
>>
>> The entire communication with the server A is stateful with a session
>> cookie, all responses are fully consumed either with a response handler
>> or with a try-with-resources clause.
>>
>> The problem: the login/logout requests consume around 10 seconds,
>> sometimes even more. I'd like to avoid this every time.
>>
>> My idea was to come around this with Commons Pool 2 which I have already
>> used for some other session pooling successfully.
>>
>> Requirements: it is not only necessary to maintain the cookie store but
>> a client id header which has a randomly generated integer assigned plus
>> a request counter. The maintained session has to be used by one thread
>> at the same time, same applies for the header otherwise server A would
>> queue all requests. The pool would guarantee that.
>>
>> Two possible solutions come to my mind:
>>
>> 1. Save the client id and cookie store in the pool and always create a
>> new client.
>> 2. Save the client id along with the authenticated HttpClient and reuse it.
>>
>> Both solutions will always increment the request counter of course.
>>
>>   From HttpClient's view which makes more sense here? What if I go with
>> option 2, will I have stale connections, clients, etc.?
>>
>> Thanks,
>>
>> Michael
>>
>
> Hi Michael
>
> How about a slightly different approach? Consider using one HttpClient
> for all sessions and maintaining a pool of HttpContext instances with a
> cookie store / session id.

Hi,

thanks for this tip, you are proposing a very interesting approach which 
I have not considered due to missing knowledge.

I would have to do the following/solve following questions:

1. Create a vanilla HttpClient in my beans.xml as a singleton. Please 
note that the web application can run for days if not even for weeks and 
the HttpClient would remain open. That is not a problem?
2. To make the client above usable concurrently, would I need to create 
a connection pool? I do not intend to use long-lived connections to 
server A.
3. Before login is called, I assign a cookie store to the HttpContext [1]?
So I would still need to carry on the cookie store plus increment the 
request counter and supply the client id to the HTTP request? Maybe even 
with an interceptor [2]?

I am having trouble to understand the difference between HttpContext and 
HttpClientContext. Especially moving the bits off the client to the 
requests itself though the manual is really good on this one.

Moreover, I am also inclined to create a seperate client for server B 
because this one is stateless or doesn't it make a difference because 
the connecion pool is configurable per target host?

[1] 
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/statemgmt.html#d5e574
[2] 
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#protocol_interceptors

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Oleg Kalnichevski <ol...@apache.org>.
On Fri, 2015-01-23 at 23:36 +0100, Michael Osipov wrote:
> Hi folks,
> 
> I am looking for a design decision to cache session information for a 
> request workflow embedded in a SOAP/REST-based webapp.
> 
> Here is a little background how the workflow is structured and what I am 
> trying to solve:
> 
> Note: I have read the entire tutorial and am somewhat overwhelmed by the 
> amount of features the HttpClient has to offer, so I beg your patience.
> 
> Workflow:
> 1. Client issues a request either with SOAP or REST with an object id
> 2. Server accepts request and does the following:
>   2.1. Create a client with a cookie manager
>   2.2. Login to a backend server A (HTTP request, server A)
>   2.3. Read object information (HTTP request, server A)
>   2.4. Obtain a read ticket for a HTTP-based remote store (HTTP request, 
> server A)
>   2.5. Download file behind object id to disk from remote store (HTTP 
> request, server B)
>   2.6. perform a logout (HTTP request, server A)
>   2.7. Close the client
> 3. Respond to client with object information and the stream from the 
> saved file
> 
> The entire communication with the server A is stateful with a session 
> cookie, all responses are fully consumed either with a response handler 
> or with a try-with-resources clause.
> 
> The problem: the login/logout requests consume around 10 seconds, 
> sometimes even more. I'd like to avoid this every time.
> 
> My idea was to come around this with Commons Pool 2 which I have already 
> used for some other session pooling successfully.
> 
> Requirements: it is not only necessary to maintain the cookie store but 
> a client id header which has a randomly generated integer assigned plus 
> a request counter. The maintained session has to be used by one thread 
> at the same time, same applies for the header otherwise server A would 
> queue all requests. The pool would guarantee that.
> 
> Two possible solutions come to my mind:
> 
> 1. Save the client id and cookie store in the pool and always create a 
> new client.
> 2. Save the client id along with the authenticated HttpClient and reuse it.
> 
> Both solutions will always increment the request counter of course.
> 
>  From HttpClient's view which makes more sense here? What if I go with 
> option 2, will I have stale connections, clients, etc.?
> 
> Thanks,
> 
> Michael
> 

Hi Michael

How about a slightly different approach? Consider using one HttpClient
for all sessions and maintaining a pool of HttpContext instances with a
cookie store / session id.

Oleg 



---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Caching stateful HTTP clients in a pool

Posted by Michael Osipov <mi...@apache.org>.
Hi folks,

TL;DR: here is -- as promised -- a possible solution how to pool HTTP 
sessions with a single HttpClient:

First, I had to create the custom object for holding that session 
information:

public class RawSession {

   private CookieStore cookieStore;
   private MutableInt requestId;
   private String clientId;
   private String serverId;

   public RawSession(CookieStore cookieStore, MutableInt requestId, 
String clientId, String serverId) {
     this.cookieStore = cookieStore;
     this.requestId = requestId;
     this.clientId = clientId;
     this.serverId = serverId;
   }
...
   // hashCode and equals generated by Eclipse with the unique id
   ...
}

Followed by a shim to the underlying pool:
public class RawSessionPool implements DisposableBean {

   private GenericObjectPool<RawSession> objectPool;

   public RawSessionPool(RawSessionPooledObjectFactory factory, 
GenericObjectPoolConfig config) {
     Validate.notNull(factory, "RawSessionPooledObjectFactory cannot be 
null");
     Validate.notNull(config, "GenericObjectPoolConfig cannot be null");
     this.objectPool = new GenericObjectPool<RawSession>(factory, config);
   }

   public RawSession acquire() throws Exception {
     return objectPool.borrowObject();
   }

   public void release(RawSession session) throws Exception {
     objectPool.returnObject(session);
   }

   public void invalidate(RawSession session) throws Exception {
     objectPool.invalidateObject(session);
   }

   @Override
   public void destroy() throws Exception {
     objectPool.close();
   }

}

and now the factory for the sessions:
public class RawSessionPooledObjectFactory extends 
BasePooledObjectFactory<RawSession> {

   @Autowired
   @Qualifier("httpClient")
   CloseableHttpClient httpClient;

   @Override
   public RawSession create() throws Exception {
     CookieStore cookieStore = new BasicCookieStore();

     MutableInt requestId = new MutableInt();
     ResponseHandler<CustomResponse> responseHandler = new 
CustomResponseHandler();

     String clientId = requestUtils.generateClientId();

     // Login
     requestId.increment();
     HttpPost login = new HttpPost(<login-url>);
     String loginBody = ...;
     HttpEntity entity = requestUtils.mergeAndCreate(loginBody, <vars>);
     login.setEntity(entity);

     HttpClientContext httpContext = HttpClientContext.create();
     httpContext.setCookieStore(cookieStore);
     CustomResponse response = httpClient.execute(login, 
responseHandler, httpContext);
     ...

     RawSession session = new RawSession(cookieStore, requestId, 
clientId, serverId);

     return session;
   }

   @Override
   public PooledObject<RawSession> wrap(RawSession session) {
     return new DefaultPooledObject<RawSession>(session);
   }

   @Override
   public void destroyObject(PooledObject<RawSession> p) throws Exception {
     RawSession session = p.getObject();
     MutableInt requestId = session.getRequestId();
     String clientId = session.getClientId();
     String serverId = session.getServerId();

     CookieStore cookieStore = session.getCookieStore();

     ResponseHandler<CustomResponse> responseHandler = new 
CustomResponseHandler();

     requestId.increment();
     // Logout
     HttpPost logout = new HttpPost(<logout-url>);
     String logoutBody = ...;

     HttpEntity entity = requestUtils.mergeAndCreate(logoutBody, <vars>);

     logout.setEntity(entity);

     HttpClientContext httpContext = HttpClientContext.create();
     httpContext.setCookieStore(cookieStore);
     CustomResponse response = httpClient.execute(logout, 
responseHandler, httpContext);
     ...
   }

}

After all this has been done, we need to wire this now with Spring:
<beans:bean id="rawSessionPool"
     class="RawSessionPool">
     <beans:constructor-arg>
       <beans:bean
         class="RawSessionPooledObjectFactory" />
     </beans:constructor-arg>
     <beans:constructor-arg>
       <beans:bean 
class="org.apache.commons.pool2.impl.GenericObjectPoolConfig"
         p:maxTotal="15" p:timeBetweenEvictionRunsMillis="3600000"
         p:minEvictableIdleTimeMillis="1800000" p:maxWaitMillis="15000"
         p:minIdle="0" p:maxIdle="10" />
     </beans:constructor-arg>
   </beans:bean>

   <beans:bean id="httpClientBuilder"
     class="org.apache.http.impl.client.HttpClientBuilder" 
factory-method="create">
     <beans:property name="connectionManager">
       <beans:bean
 
class="org.apache.http.impl.conn.PoolingHttpClientConnectionManager"
         c:timeToLive="10" c:tunit="SECONDS" p:maxTotal="30"
         p:defaultMaxPerRoute="10" p:validateAfterInactivity="5000" />
     </beans:property>
   </beans:bean>

   <beans:bean id="httpClient" factory-bean="httpClientBuilder" 
factory-method="build" />

All of the above will guarentee you that you will sessions pooled and 
freed in time and virtually
no stale HTTP connections will arise.

Lets wire up our beans in the repo impl now:
public class DefaultRepository implements Repository {

   @Autowired
   private RawSessionPool sessionPool;

   @Autowired
   @Qualifier("httpClient")
   CloseableHttpClient httpClient;

   @Override
   public CustomObject createObject(@Valid CustomInput input)
       throws IOException {
     RawSession session = sessionPool.acquire();

     MutableInt requestId = session.getRequestId();
     String clientId = session.getClientId();
     String serverId = session.getServerId();
     HttpClientContext httpContext = HttpClientContext.create();
     httpContext.setCookieStore(session.getCookieStore());

     ResponseHandler<CustomResponse> responseHandler = new 
CustomResponseHandler();

     // Create Object
     requestId.increment();
     HttpPost createObject = new HttpPost(<create-object-url>);
     String createObjectBody = ...;
     createObject.setEntity(entity);

     CustomResponse response = httpClient.execute(createObject, 
responseHandler,
         httpContext);
     ...

     sessionPool.release(session);

     return createdObject;
   }

That's it. Exception handling with finally blocks have been left out for 
brevity.

After a few tests with curl and GNU parallel, this scales very well. 
Session creation is about 10 s
(that is why the pool was necessary), requests on their own in general a 
few hundred milliseconds.
Please bear in mind that the code isn't perfect, especially the request 
preparation with the session
information.

All of this runs on a Tomcat 6 which serves requests as an intermediate 
between a backend server
and several SOAP and REST clients. Needless to say that I have developed 
the workflow in JMeter first
with the HttpClient4 interface and used this as template for the Java 
implementation.

Interesting to say what I have achieved in particular with this:
There was a requirement to create a lightweight interface to a backend 
file store. To put a file on
that store requires several requests to server A first, then put to 
server B (file store) and finalize
back in server A. I have reduced this for us in the frontend to one 
request. In a way that the
client streams his file, may be gigabytes big, to the REST endpoint, I 
take that input stream and pass
it on to HttpClient. The other way around is trickier. The input stream 
from the CloseableHttpResponse
cannot simply be returned to the client without the response going out 
of scope and causing a
resource leak. The idea was to wrap that with a HttpResponseInputStream:

public class HttpResponseInputStream extends InputStream {

   CloseableHttpResponse response;

   public HttpResponseInputStream(CloseableHttpResponse response) {
     super();
     this.response = response;
   }

   @Override
   public int read() throws IOException {
     return this.response.getEntity().getContent().read();
   }

   @Override
   public int available() throws IOException {
     return this.response.getEntity().getContent().available();
   }

   @Override
   public void close() throws IOException {
     try {
       this.response.getEntity().getContent().close();
     } finally {
       response.close();
     }
   }

}

This approach makes it possible to downstream gigabytes without loading 
the data off to local disk at all.

Thanks to all who made this great piece of software possible, especially 
Oleg with his knowledge and
guidance.

Improvement advises and comments are welcome!

Michael

---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org