Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I shutdown and reconfigure an AsyncHttpClient that is using NettyAsyncHttpProvider

I'm constructing an AsyncHttpClient like this:

public AsyncHttpClient getAsyncHttpClient() {
    AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder()
                    .setProxyServer(makeProxyServer())
                    .setRequestTimeoutInMs((int) Duration.create(ASYNC_HTTP_REQUEST_TIMEOUT_MIN, TimeUnit.MINUTES).toMillis())
                    .build();

    return new AsyncHttpClient(new NettyAsyncHttpProvider(config), config);
}

This gets called once at startup, and then the return value is passed around and used in various places. makeProxyServer() is my own function to take my proxy settings an return a ProxyServer object. What I need to do is be able to change the proxy server settings and then recreate the AsyncHttpClient object. But, I don't know how to shut it down cleanly. A bit of searching on leads me to believe that close() isn't gracefull. I'm worried about spinning up a whole new executor and set of threads every time the proxy settings change. This won't be often, but my application is very long-running.

I know I can use RequestBuilder.setProxyServer() for each request, but I'd like to have it set in one spot so that all callers of my asyncHttpClient instance obey the system-wide proxy settings without each developer having to remember to do it.

What's the right way to re-configure or teardown and rebuild a Netty-based AsyncHttpClient?

like image 492
kbyrd Avatar asked Nov 13 '13 22:11

kbyrd


2 Answers

The problem with using AsyncHttpClient.close() is that it shuts down the thread pool executor used by the provider, then there is no way to re-use the client without re-building it, because as per documentation, the executor instance cannot be reused once ts is shutdown. So, there is no way but re-build the client if you go that way (unless you implement your own ExecutorService that would have another shutdown logic, but it is a long way to go, IMHO).

However, from looking into the implementation of NettyAsyncHttpProvider, I can see that it stores the reference to the given AsyncHttpClientConfiginstance and calls its getProxyServerSelector() to get the proxy settings for every new NettyAsyncHttpProvider.execute(Request...) invocation (i.e. for every request executed by AsyncHttpClient).

Then, if we could make the getProxyServerSelector() return the configurable instance of ProxyServerSelector, that would do the thing.

Unfortunately, AsyncHttpClientConfig is designed to be a read-only container, instantiated by AsyncHttpClientConfig.Builder. To overcome this limitation, we would have to hack it, using, say, "wrap/delegate" approach:

  • Create a new class, derived from AsyncHttpClientConfig. The class should wrap the given separate AsyncHttpClientConfig instance and implement the delegation of the AsyncHttpClientConfig getters to that instance.

  • To be able to return the proxy selector we want at any given point of time, we make this setting mutable in a this wrapper class and expose the setter for it.

Example:

public class MyAsyncHttpClientConfig extends AsyncHttpClientConfig
{
  private final AsyncHttpClientConfig config;
  private ProxyServerSelector proxyServerSelector;

  public MyAsyncHttpClientConfig(AsyncHttpClientConfig config)
  {
    this.config = config;
  }

  @Override
  public int getMaxTotalConnections() { return config.maxTotalConnections; }

  @Override
  public int getMaxConnectionPerHost() { return config.maxConnectionPerHost; }

  // delegate the others but getProxyServerSelector()

  ...

  @Override
  public ProxyServerSelector getProxyServerSelector()
  { 
    return proxyServerSelector == null 
      ? config.getProxyServerSelector()
      : proxyServerSelector; 
  }

  public void setProxyServerSelector(ProxyServerSelector proxyServerSelector) 
  { 
    this.proxyServerSelector = proxyServerSelector;
  }
}
  • Now, in your example, wrap your AsyncHttpClient config instance with our new wrapper and use it to configure the AsyncHttpClient:

Example:

MyAsyncHttpClientConfig myConfig = new MyAsyncHttpClientConfig(config); 
return new AsyncHttpClient(new NettyAsyncHttpProvider(myConfig), myConfig);
  • Whenever you invoke myConfig.setProxyServerSelector(newSelector), the new request executed by NettyAsyncHttpProvider instance in your client will use the new proxy server settings.

A few hints/warnings:

  • This approach relies on the internal implementation of NettyAsyncHttpProvider; therefore make your own judgement on maintainability, future Netty libraries versions upgrade strategy etc. You could always look at the Netty source code before upgrading to the new version. At the current point, I personally think it is unlikely to change too much to invalidate this implementation.

  • You could get ProxyServerSelector for ProxyServer by using com.ning.http.util.ProxyUtils.createProxyServerSelector(proxyServer) - that's exactly what AsyncHttpClientConfig.Builder does.

  • The given example has no synchronization logic for accessing proxyServerSelector; you may want to add some as your application logic needs.

  • Maybe it is a good idea to submit a feature request for AsyncHttpClient to be able to setup a "configuration factory" for the AsyncHttpProvider so all these complications would vanish :-)

like image 158
NSH Avatar answered Nov 03 '22 19:11

NSH


You should be holding a RequestHandle instance for all your unfinished requests. When you want to shut down, you can loop through and call isFinished() on all of them until they are all done. Then you know you can safely close it and no pending requests will be killed.

Once it's closed, just build a new one. Don't try to reuse the existing one. If you have references to it around, change those to reference a Factory that will return the current one.

like image 39
Ted Bigham Avatar answered Nov 03 '22 19:11

Ted Bigham