Skip to content

Commit a1a0d23

Browse files
authored
Merge pull request #741 from commercetools/optimize-okhttp3-client
optimize OkHttp3 client
2 parents fcac4c6 + 99faf0e commit a1a0d23

File tree

19 files changed

+783
-275
lines changed

19 files changed

+783
-275
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ plugins {
1717
id 'io.github.gradle-nexus.publish-plugin' version '1.3.0'
1818
id 'com.github.jk1.dependency-license-report' version '2.0'
1919

20-
id "me.champeau.jmh" version "0.7.2"
20+
id "me.champeau.jmh" version "0.6.8"
2121
id "jacoco"
2222
}
2323

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
dependencies {
22
api "javax.money:money-api:1.1"
3-
api group: 'org.javamoney.moneta', name: 'moneta-core', version: '1.4.4'
3+
api('org.javamoney.moneta:moneta-core:1.4.4') {
4+
exclude group: "com.squareup.okhttp3", module: "okhttp"
5+
}
46
}

commercetools/commercetools-okhttp-client3/build.gradle

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
apply plugin: "me.champeau.jmh"
2+
3+
jmh {
4+
iterations = 5
5+
benchmarkMode = ['thrpt']
6+
threads = 25
7+
fork = 3
8+
timeOnIteration = '1s'
9+
profilers = ['gc']
10+
}
111

212
dependencies {
313
api project(":rmf:rmf-java-base")
414

5-
api "com.squareup.okio:okio:3.9.0"
15+
implementation "com.squareup.okio:okio:3.9.0"
616
api "com.squareup.okhttp3:okhttp:3.14.9" version {
717
strictly "[3.0,4.0["
818
prefer "3.14.9"
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
2+
package com.commercetools.http.okhttp3;
3+
4+
import java.io.ByteArrayInputStream;
5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.nio.charset.StandardCharsets;
8+
import java.util.zip.GZIPOutputStream;
9+
10+
import io.vrap.rmf.base.client.ApiHttpResponse;
11+
12+
import org.assertj.core.api.Assertions;
13+
import org.openjdk.jmh.annotations.*;
14+
15+
import okhttp3.*;
16+
import okio.Okio;
17+
18+
public class UnzipBenchmark {
19+
20+
@State(Scope.Benchmark)
21+
public static class InterceptorState {
22+
private CtOkHttp3Client.UnzippingInterceptor interceptor;
23+
24+
@Setup(Level.Trial)
25+
public void init() {
26+
interceptor = new CtOkHttp3Client.UnzippingInterceptor();
27+
printUsedMemory();
28+
29+
}
30+
31+
@TearDown(Level.Trial)
32+
public void tearDown() {
33+
printUsedMemory();
34+
}
35+
}
36+
@Benchmark
37+
public void unzip(InterceptorState state) throws IOException {
38+
39+
ByteArrayOutputStream os = new ByteArrayOutputStream();
40+
GZIPOutputStream gzipOs = new GZIPOutputStream(os);
41+
byte[] buffer = "Sample Text".getBytes();
42+
gzipOs.write(buffer, 0, buffer.length);
43+
gzipOs.close();
44+
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
45+
46+
Response gzipped = new Response.Builder().request(new Request.Builder().url("http://localhost").build())
47+
.protocol(Protocol.HTTP_1_1)
48+
.addHeader("content-encoding", "gzip")
49+
.addHeader("content-type", "application/json")
50+
.code(200)
51+
.message("Ok")
52+
.body(ResponseBody.create(MediaType.parse("application/json"), -1L,
53+
Okio.buffer(Okio.source(inputStream))))
54+
.build();
55+
Assertions.assertThat(gzipped.body().source().isOpen()).isTrue();
56+
57+
Response unzipped = state.interceptor.unzip(gzipped);
58+
59+
Assertions.assertThat(gzipped.body().source().isOpen()).isTrue();
60+
Assertions.assertThat(unzipped.body().source().isOpen()).isTrue();
61+
Assertions.assertThat(inputStream.available()).isEqualTo(31);
62+
63+
ApiHttpResponse<byte[]> response = CtOkHttp3Client.toResponse(unzipped);
64+
65+
Assertions.assertThat(gzipped.body().source().isOpen()).isFalse();
66+
Assertions.assertThat(unzipped.body().source().isOpen()).isFalse();
67+
Assertions.assertThat(inputStream.available()).isEqualTo(0);
68+
69+
String text = new String(response.getBody(), StandardCharsets.UTF_8);
70+
Assertions.assertThat(text).isEqualTo("Sample Text");
71+
}
72+
73+
public static void printUsedMemory() {
74+
long _usedMem;
75+
long _total;
76+
long _total2;
77+
long _count = -1;
78+
System.out.flush();
79+
// loop to get a stable reading, since memory may be resized between the method calls
80+
do {
81+
_count++;
82+
_total = Runtime.getRuntime().totalMemory();
83+
try {
84+
Thread.sleep(12);
85+
}
86+
catch (Exception ignore) {
87+
}
88+
long _free = Runtime.getRuntime().freeMemory();
89+
_total2 = Runtime.getRuntime().totalMemory();
90+
_usedMem = _total - _free;
91+
} while (_total != _total2);
92+
System.out.println("before GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total);
93+
try {
94+
Runtime.getRuntime().gc();
95+
Thread.sleep(55);
96+
Runtime.getRuntime().gc();
97+
Thread.sleep(55);
98+
Runtime.getRuntime().gc();
99+
Thread.sleep(55);
100+
Runtime.getRuntime().gc();
101+
Thread.sleep(55);
102+
}
103+
catch (Exception ignore) {
104+
}
105+
// loop to get a stable reading, since memory may be resized between the method calls
106+
do {
107+
_count++;
108+
_total = Runtime.getRuntime().totalMemory();
109+
try {
110+
Thread.sleep(12);
111+
}
112+
catch (Exception ignore) {
113+
}
114+
long _free = Runtime.getRuntime().freeMemory();
115+
_total2 = Runtime.getRuntime().totalMemory();
116+
_usedMem = _total - _free;
117+
} while (_total != _total2);
118+
System.out.println("after GC: used=" + _usedMem + ", loopCount=" + _count + ", total=" + _total);
119+
}
120+
}

commercetools/commercetools-okhttp-client3/src/main/java/com/commercetools/http/okhttp3/CtOkHttp3Client.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public CompletableFuture<ApiHttpResponse<byte[]>> execute(final ApiHttpRequest r
107107

108108
}
109109

110-
private static ApiHttpResponse<byte[]> toResponse(final okhttp3.Response response) {
110+
static ApiHttpResponse<byte[]> toResponse(final okhttp3.Response response) {
111111
final ApiHttpHeaders apiHttpHeaders = new ApiHttpHeaders(response.headers()
112112
.toMultimap()
113113
.entrySet()
@@ -120,7 +120,7 @@ private static ApiHttpResponse<byte[]> toResponse(final okhttp3.Response respons
120120
.map(Utils.wrapToCompletionException(okhttp3.ResponseBody::bytes))
121121
.orElse(null),
122122
response.message());
123-
if (apiHttpResponse.getBody() != null) {
123+
if (response.body() != null) {
124124
response.body().close();
125125
}
126126
return apiHttpResponse;
@@ -194,17 +194,15 @@ public okhttp3.Response intercept(Chain chain) throws IOException {
194194
return unzip(response);
195195
}
196196

197-
private okhttp3.Response unzip(final okhttp3.Response response) throws IOException {
197+
okhttp3.Response unzip(final okhttp3.Response response) {
198198
if (!"gzip".equalsIgnoreCase(response.header("Content-Encoding"))) {
199199
return response;
200200
}
201201

202-
okhttp3.ResponseBody responseBody = response.body();
203-
if (responseBody == null) {
202+
if (response.body() == null) {
204203
return response;
205204
}
206205

207-
GzipSource gzipSource = new GzipSource(responseBody.source());
208206
okhttp3.Headers strippedHeaders = response.headers()
209207
.newBuilder()
210208
.removeAll("Content-Encoding")
@@ -213,8 +211,8 @@ private okhttp3.Response unzip(final okhttp3.Response response) throws IOExcepti
213211
String contentType = response.header("Content-Type");
214212
return response.newBuilder()
215213
.headers(strippedHeaders)
216-
.body(
217-
okhttp3.ResponseBody.create(okhttp3.MediaType.parse(contentType), -1L, Okio.buffer(gzipSource)))
214+
.body(okhttp3.ResponseBody.create(okhttp3.MediaType.parse(contentType), -1L,
215+
Okio.buffer(new GzipSource(response.body().source()))))
218216
.build();
219217
}
220218
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,63 @@
11

22
package com.commercetools.http.okhttp3;
33

4+
import java.io.ByteArrayInputStream;
5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.nio.charset.StandardCharsets;
8+
import java.util.zip.GZIPOutputStream;
9+
10+
import io.vrap.rmf.base.client.ApiHttpResponse;
411
import io.vrap.rmf.base.client.HttpClientSupplier;
512

613
import org.assertj.core.api.Assertions;
714
import org.junit.jupiter.api.Test;
815

16+
import okhttp3.*;
17+
import okio.Okio;
18+
919
public class SupplierTest {
1020
@Test
1121
public void testCreate() {
1222
Assertions.assertThat(HttpClientSupplier.of().get()).isInstanceOf(CtOkHttp3Client.class);
1323
}
24+
25+
@Test
26+
public void testUnzip() throws IOException {
27+
28+
ByteArrayOutputStream os = new ByteArrayOutputStream();
29+
GZIPOutputStream gzipOs = new GZIPOutputStream(os);
30+
byte[] buffer = "Sample Text".getBytes();
31+
gzipOs.write(buffer, 0, buffer.length);
32+
gzipOs.close();
33+
ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray());
34+
35+
CtOkHttp3Client.UnzippingInterceptor interceptor = new CtOkHttp3Client.UnzippingInterceptor();
36+
37+
Response gzipped = new Response.Builder().request(new Request.Builder().url("http://localhost").build())
38+
.protocol(Protocol.HTTP_1_1)
39+
.addHeader("content-encoding", "gzip")
40+
.addHeader("content-type", "application/json")
41+
.code(200)
42+
.message("Ok")
43+
.body(ResponseBody.create(MediaType.parse("application/json"), -1L,
44+
Okio.buffer(Okio.source(inputStream))))
45+
.build();
46+
Assertions.assertThat(gzipped.body().source().isOpen()).isTrue();
47+
48+
Response unzipped = interceptor.unzip(gzipped);
49+
50+
Assertions.assertThat(gzipped.body().source().isOpen()).isTrue();
51+
Assertions.assertThat(unzipped.body().source().isOpen()).isTrue();
52+
Assertions.assertThat(inputStream.available()).isEqualTo(31);
53+
54+
ApiHttpResponse<byte[]> response = CtOkHttp3Client.toResponse(unzipped);
55+
56+
Assertions.assertThat(gzipped.body().source().isOpen()).isFalse();
57+
Assertions.assertThat(unzipped.body().source().isOpen()).isFalse();
58+
Assertions.assertThat(inputStream.available()).isEqualTo(0);
59+
60+
String text = new String(response.getBody(), StandardCharsets.UTF_8);
61+
Assertions.assertThat(text).isEqualTo("Sample Text");
62+
}
1463
}

commercetools/commercetools-okhttp-client4/build.gradle

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
apply plugin: "me.champeau.jmh"
2+
3+
jmh {
4+
iterations = 5
5+
benchmarkMode = ['thrpt']
6+
threads = 25
7+
fork = 3
8+
timeOnIteration = '1s'
9+
profilers = ['gc']
10+
}
111

212
dependencies {
313
api project(":rmf:rmf-java-base")

0 commit comments

Comments
 (0)