Fix high mem usage due to many OkHttpClient instances. (#373)

This commit is contained in:
Artur Souza 2020-10-19 15:23:41 -07:00 committed by GitHub
parent 604eff8cd4
commit eb7763f126
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 25 deletions

View File

@ -9,6 +9,7 @@ import io.dapr.config.Properties;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import java.time.Duration; import java.time.Duration;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* A builder for the DaprHttp. * A builder for the DaprHttp.
@ -16,21 +17,30 @@ import java.time.Duration;
public class DaprHttpBuilder { public class DaprHttpBuilder {
/** /**
* Read timeout for http calls. * Singleton OkHttpClient.
*/ */
private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(60); private static final AtomicReference<OkHttpClient> OK_HTTP_CLIENT = new AtomicReference<>();
/**
* Static lock object.
*/
private static final Object LOCK = new Object();
/** /**
* Read timeout used to build object. * Read timeout used to build object.
*/ */
private Duration readTimeout = DEFAULT_READ_TIMEOUT; private Duration readTimeout = Duration.ofSeconds(Properties.HTTP_CLIENT_READTIMEOUTSECONDS.get());
/** /**
* Sets the read timeout duration for the instance to be built. * Sets the read timeout duration for the instance to be built.
* *
* <p>Instead, set environment variable "DAPR_HTTP_CLIENT_READTIMEOUTSECONDS",
* or system property "dapr.http.client.readtimeoutseconds".
*
* @param duration Read timeout duration. * @param duration Read timeout duration.
* @return Same builder instance. * @return Same builder instance.
*/ */
@Deprecated
public DaprHttpBuilder withReadTimeout(Duration duration) { public DaprHttpBuilder withReadTimeout(Duration duration) {
this.readTimeout = duration; this.readTimeout = duration;
return this; return this;
@ -52,9 +62,17 @@ public class DaprHttpBuilder {
* @return Instance of {@link DaprHttp} * @return Instance of {@link DaprHttp}
*/ */
private DaprHttp buildDaprHttp() { private DaprHttp buildDaprHttp() {
if (OK_HTTP_CLIENT.get() == null) {
synchronized (LOCK) {
if (OK_HTTP_CLIENT.get() == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder(); OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.readTimeout(this.readTimeout); builder.readTimeout(this.readTimeout);
OkHttpClient okHttpClient = builder.build(); OkHttpClient okHttpClient = builder.build();
return new DaprHttp(Properties.SIDECAR_IP.get(), Properties.HTTP_PORT.get(), okHttpClient); OK_HTTP_CLIENT.set(okHttpClient);
}
}
}
return new DaprHttp(Properties.SIDECAR_IP.get(), Properties.HTTP_PORT.get(), OK_HTTP_CLIENT.get());
} }
} }

View File

@ -38,6 +38,11 @@ public class Properties {
*/ */
private static final Charset DEFAULT_STRING_CHARSET = StandardCharsets.UTF_8; private static final Charset DEFAULT_STRING_CHARSET = StandardCharsets.UTF_8;
/**
* Dapr's default timeout in seconds for HTTP client reads.
*/
private static final Integer DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS = 60;
/** /**
* IP for Dapr's sidecar. * IP for Dapr's sidecar.
*/ */
@ -86,4 +91,12 @@ public class Properties {
"DAPR_STRING_CHARSET", "DAPR_STRING_CHARSET",
DEFAULT_STRING_CHARSET, DEFAULT_STRING_CHARSET,
(s) -> Charset.forName(s)); (s) -> Charset.forName(s));
/**
* Dapr's timeout in seconds for HTTP client reads.
*/
public static final Property<Integer> HTTP_CLIENT_READTIMEOUTSECONDS = new IntegerProperty(
"dapr.http.client.readtimeoutseconds",
"DAPR_HTTP_CLIENT_READTIMEOUTSECONDS",
DEFAULT_HTTP_CLIENT_READTIMEOUTSECONDS);
} }

View File

@ -9,37 +9,26 @@ import okhttp3.OkHttpClient;
import org.junit.Test; import org.junit.Test;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.Duration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
public class DaprHttpBuilderTest { public class DaprHttpBuilderTest {
@Test @Test
public void withReadTimeout() throws Exception { public void singletonOkHttpClient() throws Exception {
DaprHttpBuilder daprHttpBuilder = new DaprHttpBuilder(); DaprHttp daprHttp = new DaprHttpBuilder().build();
Duration duration = Duration.ofSeconds(999); DaprHttp anotherDaprHttp = new DaprHttpBuilder().build();
daprHttpBuilder.build();
DaprHttpBuilder dapr = daprHttpBuilder.withReadTimeout(duration);
assertNotNull(dapr);
DaprHttp daprHttp = daprHttpBuilder.build(); assertSame(getOkHttpClient(daprHttp), getOkHttpClient(anotherDaprHttp));
assertOKHttpPropertyValue(daprHttp, "readTimeoutMillis", (int)duration.toMillis());
} }
private static final void assertOKHttpPropertyValue(DaprHttp daprHttp, String propertyName, Object expectedValue) throws Exception { private static OkHttpClient getOkHttpClient(DaprHttp daprHttp) throws Exception {
// First, get okHttpClient.
Field httpClientField = DaprHttp.class.getDeclaredField("httpClient"); Field httpClientField = DaprHttp.class.getDeclaredField("httpClient");
httpClientField.setAccessible(true); httpClientField.setAccessible(true);
OkHttpClient okHttpClient = (OkHttpClient) httpClientField.get(daprHttp); OkHttpClient okHttpClient = (OkHttpClient) httpClientField.get(daprHttp);
assertNotNull(okHttpClient); assertNotNull(okHttpClient);
return okHttpClient;
Field propertyField = OkHttpClient.class.getDeclaredField(propertyName);
propertyField.setAccessible(true);
Object value = propertyField.get(okHttpClient);
assertNotNull(value);
assertEquals(expectedValue, value);
} }
} }