Skip to content

Commit 21b4047

Browse files
feat(client): add connection pooling option
1 parent f56884d commit 21b4047

4 files changed

Lines changed: 149 additions & 0 deletions

File tree

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,25 @@ OrbClient client = OrbOkHttpClient.builder()
457457
.build();
458458
```
459459

460+
### Connection pooling
461+
462+
To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:
463+
464+
```java
465+
import com.withorb.api.client.OrbClient;
466+
import com.withorb.api.client.okhttp.OrbOkHttpClient;
467+
import java.time.Duration;
468+
469+
OrbClient client = OrbOkHttpClient.builder()
470+
.fromEnv()
471+
// If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.
472+
.maxIdleConnections(10)
473+
.keepAliveDuration(Duration.ofMinutes(2))
474+
.build();
475+
```
476+
477+
If both options are unset, OkHttp's default connection pool settings are used.
478+
460479
### HTTPS
461480

462481
> [!NOTE]

orb-java-client-okhttp/src/main/kotlin/com/withorb/api/client/okhttp/OkHttpClient.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import java.time.Duration
1616
import java.util.concurrent.CancellationException
1717
import java.util.concurrent.CompletableFuture
1818
import java.util.concurrent.ExecutorService
19+
import java.util.concurrent.TimeUnit
1920
import javax.net.ssl.HostnameVerifier
2021
import javax.net.ssl.SSLSocketFactory
2122
import javax.net.ssl.X509TrustManager
2223
import okhttp3.Call
2324
import okhttp3.Callback
25+
import okhttp3.ConnectionPool
2426
import okhttp3.Dispatcher
2527
import okhttp3.HttpUrl.Companion.toHttpUrl
2628
import okhttp3.MediaType
@@ -200,6 +202,8 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
200202

201203
private var timeout: Timeout = Timeout.default()
202204
private var proxy: Proxy? = null
205+
private var maxIdleConnections: Int? = null
206+
private var keepAliveDuration: Duration? = null
203207
private var dispatcherExecutorService: ExecutorService? = null
204208
private var sslSocketFactory: SSLSocketFactory? = null
205209
private var trustManager: X509TrustManager? = null
@@ -211,6 +215,28 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
211215

212216
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
213217

218+
/**
219+
* Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
220+
*
221+
* If this is set, then [keepAliveDuration] must also be set.
222+
*
223+
* If unset, then OkHttp's default is used.
224+
*/
225+
fun maxIdleConnections(maxIdleConnections: Int?) = apply {
226+
this.maxIdleConnections = maxIdleConnections
227+
}
228+
229+
/**
230+
* Sets the keep-alive duration for idle connections in the underlying [ConnectionPool].
231+
*
232+
* If this is set, then [maxIdleConnections] must also be set.
233+
*
234+
* If unset, then OkHttp's default is used.
235+
*/
236+
fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
237+
this.keepAliveDuration = keepAliveDuration
238+
}
239+
214240
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
215241
this.dispatcherExecutorService = dispatcherExecutorService
216242
}
@@ -240,6 +266,22 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
240266
.apply {
241267
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
242268

269+
val maxIdleConnections = maxIdleConnections
270+
val keepAliveDuration = keepAliveDuration
271+
if (maxIdleConnections != null && keepAliveDuration != null) {
272+
connectionPool(
273+
ConnectionPool(
274+
maxIdleConnections,
275+
keepAliveDuration.toNanos(),
276+
TimeUnit.NANOSECONDS,
277+
)
278+
)
279+
} else {
280+
check((maxIdleConnections != null) == (keepAliveDuration != null)) {
281+
"Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set"
282+
}
283+
}
284+
243285
val sslSocketFactory = sslSocketFactory
244286
val trustManager = trustManager
245287
if (sslSocketFactory != null && trustManager != null) {

orb-java-client-okhttp/src/main/kotlin/com/withorb/api/client/okhttp/OrbOkHttpClient.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class OrbOkHttpClient private constructor() {
4949
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
5050
private var dispatcherExecutorService: ExecutorService? = null
5151
private var proxy: Proxy? = null
52+
private var maxIdleConnections: Int? = null
53+
private var keepAliveDuration: Duration? = null
5254
private var sslSocketFactory: SSLSocketFactory? = null
5355
private var trustManager: X509TrustManager? = null
5456
private var hostnameVerifier: HostnameVerifier? = null
@@ -77,6 +79,46 @@ class OrbOkHttpClient private constructor() {
7779
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
7880
fun proxy(proxy: Optional<Proxy>) = proxy(proxy.getOrNull())
7981

82+
/**
83+
* The maximum number of idle connections kept by the underlying OkHttp connection pool.
84+
*
85+
* If this is set, then [keepAliveDuration] must also be set.
86+
*
87+
* If unset, then OkHttp's default is used.
88+
*/
89+
fun maxIdleConnections(maxIdleConnections: Int?) = apply {
90+
this.maxIdleConnections = maxIdleConnections
91+
}
92+
93+
/**
94+
* Alias for [Builder.maxIdleConnections].
95+
*
96+
* This unboxed primitive overload exists for backwards compatibility.
97+
*/
98+
fun maxIdleConnections(maxIdleConnections: Int) =
99+
maxIdleConnections(maxIdleConnections as Int?)
100+
101+
/**
102+
* Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
103+
*/
104+
fun maxIdleConnections(maxIdleConnections: Optional<Int>) =
105+
maxIdleConnections(maxIdleConnections.getOrNull())
106+
107+
/**
108+
* The keep-alive duration for idle connections in the underlying OkHttp connection pool.
109+
*
110+
* If this is set, then [maxIdleConnections] must also be set.
111+
*
112+
* If unset, then OkHttp's default is used.
113+
*/
114+
fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
115+
this.keepAliveDuration = keepAliveDuration
116+
}
117+
118+
/** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
119+
fun keepAliveDuration(keepAliveDuration: Optional<Duration>) =
120+
keepAliveDuration(keepAliveDuration.getOrNull())
121+
80122
/**
81123
* The socket factory used to secure HTTPS connections.
82124
*
@@ -338,6 +380,8 @@ class OrbOkHttpClient private constructor() {
338380
OkHttpClient.builder()
339381
.timeout(clientOptions.timeout())
340382
.proxy(proxy)
383+
.maxIdleConnections(maxIdleConnections)
384+
.keepAliveDuration(keepAliveDuration)
341385
.dispatcherExecutorService(dispatcherExecutorService)
342386
.sslSocketFactory(sslSocketFactory)
343387
.trustManager(trustManager)

orb-java-client-okhttp/src/main/kotlin/com/withorb/api/client/okhttp/OrbOkHttpClientAsync.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class OrbOkHttpClientAsync private constructor() {
4949
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
5050
private var dispatcherExecutorService: ExecutorService? = null
5151
private var proxy: Proxy? = null
52+
private var maxIdleConnections: Int? = null
53+
private var keepAliveDuration: Duration? = null
5254
private var sslSocketFactory: SSLSocketFactory? = null
5355
private var trustManager: X509TrustManager? = null
5456
private var hostnameVerifier: HostnameVerifier? = null
@@ -77,6 +79,46 @@ class OrbOkHttpClientAsync private constructor() {
7779
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
7880
fun proxy(proxy: Optional<Proxy>) = proxy(proxy.getOrNull())
7981

82+
/**
83+
* The maximum number of idle connections kept by the underlying OkHttp connection pool.
84+
*
85+
* If this is set, then [keepAliveDuration] must also be set.
86+
*
87+
* If unset, then OkHttp's default is used.
88+
*/
89+
fun maxIdleConnections(maxIdleConnections: Int?) = apply {
90+
this.maxIdleConnections = maxIdleConnections
91+
}
92+
93+
/**
94+
* Alias for [Builder.maxIdleConnections].
95+
*
96+
* This unboxed primitive overload exists for backwards compatibility.
97+
*/
98+
fun maxIdleConnections(maxIdleConnections: Int) =
99+
maxIdleConnections(maxIdleConnections as Int?)
100+
101+
/**
102+
* Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
103+
*/
104+
fun maxIdleConnections(maxIdleConnections: Optional<Int>) =
105+
maxIdleConnections(maxIdleConnections.getOrNull())
106+
107+
/**
108+
* The keep-alive duration for idle connections in the underlying OkHttp connection pool.
109+
*
110+
* If this is set, then [maxIdleConnections] must also be set.
111+
*
112+
* If unset, then OkHttp's default is used.
113+
*/
114+
fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
115+
this.keepAliveDuration = keepAliveDuration
116+
}
117+
118+
/** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
119+
fun keepAliveDuration(keepAliveDuration: Optional<Duration>) =
120+
keepAliveDuration(keepAliveDuration.getOrNull())
121+
80122
/**
81123
* The socket factory used to secure HTTPS connections.
82124
*
@@ -338,6 +380,8 @@ class OrbOkHttpClientAsync private constructor() {
338380
OkHttpClient.builder()
339381
.timeout(clientOptions.timeout())
340382
.proxy(proxy)
383+
.maxIdleConnections(maxIdleConnections)
384+
.keepAliveDuration(keepAliveDuration)
341385
.dispatcherExecutorService(dispatcherExecutorService)
342386
.sslSocketFactory(sslSocketFactory)
343387
.trustManager(trustManager)

0 commit comments

Comments
 (0)