Skip to content

Commit b269cf0

Browse files
author
Adrian Cole
committed
Demonstrates impact of advice to cache Feign.newInstance
Shows relative performance of caching various points of construction of a Feign Api. Adds a mesobenchmark of actually performing http requests, so that caching performance can be placed in context. See OpenFeign#214
1 parent edb3c06 commit b269cf0

6 files changed

Lines changed: 222 additions & 106 deletions

File tree

benchmark/pom.xml

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,34 @@
2727
</dependency>
2828
<dependency>
2929
<groupId>com.netflix.feign</groupId>
30-
<artifactId>feign-jaxrs</artifactId>
30+
<artifactId>feign-okhttp</artifactId>
3131
<version>${project.version}</version>
3232
</dependency>
3333
<dependency>
34-
<groupId>javax.ws.rs</groupId>
35-
<artifactId>jsr311-api</artifactId>
36-
<version>1.1.1</version>
34+
<groupId>com.squareup.okhttp</groupId>
35+
<artifactId>mockwebserver</artifactId>
36+
<version>2.3.0</version>
37+
<exclusions>
38+
<exclusion>
39+
<groupId>org.bouncycastle</groupId>
40+
<artifactId>bcprov-jdk15on</artifactId>
41+
</exclusion>
42+
</exclusions>
43+
</dependency>
44+
<dependency>
45+
<groupId>io.reactivex</groupId>
46+
<artifactId>rxnetty</artifactId>
47+
<version>0.4.8</version>
48+
</dependency>
49+
<dependency>
50+
<groupId>io.reactivex</groupId>
51+
<artifactId>rxjava</artifactId>
52+
<version>1.0.9</version>
53+
</dependency>
54+
<dependency>
55+
<groupId>io.netty</groupId>
56+
<artifactId>netty-codec-http</artifactId>
57+
<version>4.0.26.Final</version>
3758
</dependency>
3859
<dependency>
3960
<groupId>org.openjdk.jmh</groupId>

benchmark/src/main/java/feign/benchmark/ContractBenchmarks.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

benchmark/src/main/java/feign/benchmark/FeignTestInterface.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import feign.RequestLine;
77
import feign.Response;
88
import java.util.List;
9-
import javax.ws.rs.HeaderParam;
109

1110
@Headers("Accept: application/json")
1211
interface FeignTestInterface {
@@ -38,5 +37,5 @@ void form(
3837

3938
@RequestLine("POST /")
4039
@Headers({"Happy: sad", "Auth-Token: {authToken}"})
41-
void headers(@HeaderParam("authToken") String token);
40+
void headers(@Param("authToken") String token);
4241
}

benchmark/src/main/java/feign/benchmark/JAXRSTestInterface.java

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package feign.benchmark;
2+
3+
import com.squareup.okhttp.OkHttpClient;
4+
import com.squareup.okhttp.Request;
5+
import feign.Feign;
6+
import feign.Response;
7+
import io.netty.buffer.ByteBuf;
8+
import io.reactivex.netty.RxNetty;
9+
import io.reactivex.netty.protocol.http.server.HttpServer;
10+
import io.reactivex.netty.protocol.http.server.HttpServerRequest;
11+
import io.reactivex.netty.protocol.http.server.HttpServerResponse;
12+
import io.reactivex.netty.protocol.http.server.RequestHandler;
13+
import java.io.IOException;
14+
import java.util.concurrent.TimeUnit;
15+
import org.openjdk.jmh.annotations.Benchmark;
16+
import org.openjdk.jmh.annotations.BenchmarkMode;
17+
import org.openjdk.jmh.annotations.Fork;
18+
import org.openjdk.jmh.annotations.Measurement;
19+
import org.openjdk.jmh.annotations.Mode;
20+
import org.openjdk.jmh.annotations.OutputTimeUnit;
21+
import org.openjdk.jmh.annotations.Scope;
22+
import org.openjdk.jmh.annotations.Setup;
23+
import org.openjdk.jmh.annotations.State;
24+
import org.openjdk.jmh.annotations.TearDown;
25+
import org.openjdk.jmh.annotations.Warmup;
26+
27+
@Measurement(iterations = 5, time = 1)
28+
@Warmup(iterations = 10, time = 1)
29+
@Fork(3)
30+
@BenchmarkMode(Mode.Throughput)
31+
@OutputTimeUnit(TimeUnit.SECONDS)
32+
@State(Scope.Benchmark)
33+
public class RealRequestBenchmarks {
34+
35+
private static final int SERVER_PORT = 8765;
36+
private HttpServer<ByteBuf, ByteBuf> server;
37+
private OkHttpClient client;
38+
private FeignTestInterface okFeign;
39+
private Request queryRequest;
40+
41+
@Setup
42+
public void setup() {
43+
server =
44+
RxNetty.createHttpServer(
45+
SERVER_PORT,
46+
new RequestHandler<ByteBuf, ByteBuf>() {
47+
public rx.Observable handle(
48+
HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) {
49+
return response.flush();
50+
}
51+
});
52+
server.start();
53+
client = new OkHttpClient();
54+
client.setRetryOnConnectionFailure(false);
55+
okFeign =
56+
Feign.builder()
57+
.client(new feign.okhttp.OkHttpClient(client))
58+
.target(FeignTestInterface.class, "http://localhost:" + SERVER_PORT);
59+
queryRequest =
60+
new Request.Builder()
61+
.url("http://localhost:" + SERVER_PORT + "/?Action=GetUser&Version=2010-05-08&limit=1")
62+
.build();
63+
}
64+
65+
@TearDown
66+
public void tearDown() throws InterruptedException {
67+
server.shutdown();
68+
}
69+
70+
/** How fast can we execute get commands synchronously? */
71+
@Benchmark
72+
public com.squareup.okhttp.Response query_baseCaseUsingOkHttp() throws IOException {
73+
com.squareup.okhttp.Response result = client.newCall(queryRequest).execute();
74+
result.body().close();
75+
return result;
76+
}
77+
78+
/** How fast can we execute get commands synchronously using Feign? */
79+
@Benchmark
80+
public Response query_feignUsingOkHttp() {
81+
return okFeign.query();
82+
}
83+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package feign.benchmark;
2+
3+
import feign.Client;
4+
import feign.Contract;
5+
import feign.Feign;
6+
import feign.MethodMetadata;
7+
import feign.Request;
8+
import feign.Response;
9+
import feign.Target.HardCodedTarget;
10+
import java.io.IOException;
11+
import java.util.Collection;
12+
import java.util.LinkedHashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.concurrent.TimeUnit;
16+
import org.openjdk.jmh.annotations.Benchmark;
17+
import org.openjdk.jmh.annotations.BenchmarkMode;
18+
import org.openjdk.jmh.annotations.Fork;
19+
import org.openjdk.jmh.annotations.Measurement;
20+
import org.openjdk.jmh.annotations.Mode;
21+
import org.openjdk.jmh.annotations.OutputTimeUnit;
22+
import org.openjdk.jmh.annotations.Scope;
23+
import org.openjdk.jmh.annotations.Setup;
24+
import org.openjdk.jmh.annotations.State;
25+
import org.openjdk.jmh.annotations.Warmup;
26+
27+
@Measurement(iterations = 5, time = 1)
28+
@Warmup(iterations = 10, time = 1)
29+
@Fork(3)
30+
@BenchmarkMode(Mode.Throughput)
31+
@OutputTimeUnit(TimeUnit.SECONDS)
32+
@State(Scope.Thread)
33+
public class WhatShouldWeCacheBenchmarks {
34+
35+
private Contract feignContract;
36+
private Contract cachedContact;
37+
private Client fakeClient;
38+
private Feign cachedFakeFeign;
39+
private FeignTestInterface cachedFakeApi;
40+
41+
@Setup
42+
public void setup() {
43+
feignContract = new Contract.Default();
44+
cachedContact =
45+
new Contract() {
46+
private final List<MethodMetadata> cached =
47+
new Default().parseAndValidatateMetadata(FeignTestInterface.class);
48+
49+
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> declaring) {
50+
return cached;
51+
}
52+
};
53+
fakeClient =
54+
new Client() {
55+
public Response execute(Request request, Request.Options options) throws IOException {
56+
Map<String, Collection<String>> headers =
57+
new LinkedHashMap<String, Collection<String>>();
58+
return Response.create(200, "ok", headers, (byte[]) null);
59+
}
60+
};
61+
cachedFakeFeign = Feign.builder().client(fakeClient).build();
62+
cachedFakeApi =
63+
cachedFakeFeign.newInstance(
64+
new HardCodedTarget<FeignTestInterface>(FeignTestInterface.class, "http://localhost"));
65+
}
66+
67+
/** How fast is parsing an api interface? */
68+
@Benchmark
69+
public List<MethodMetadata> parseFeignContract() {
70+
return feignContract.parseAndValidatateMetadata(FeignTestInterface.class);
71+
}
72+
73+
/** How fast is creating a feign instance for each http request, without considering network? */
74+
@Benchmark
75+
public Response buildAndQuery_fake() {
76+
return Feign.builder()
77+
.client(fakeClient)
78+
.target(FeignTestInterface.class, "http://localhost")
79+
.query();
80+
}
81+
82+
/**
83+
* How fast is creating a feign instance for each http request, without considering network, and
84+
* without re-parsing the annotated http api?
85+
*/
86+
@Benchmark
87+
public Response buildAndQuery_fake_cachedContract() {
88+
return Feign.builder()
89+
.contract(cachedContact)
90+
.client(fakeClient)
91+
.target(FeignTestInterface.class, "http://localhost")
92+
.query();
93+
}
94+
95+
/**
96+
* How fast re-parsing the annotated http api for each http request, without considering network?
97+
*/
98+
@Benchmark
99+
public Response buildAndQuery_fake_cachedFeign() {
100+
return cachedFakeFeign
101+
.newInstance(
102+
new HardCodedTarget<FeignTestInterface>(FeignTestInterface.class, "http://localhost"))
103+
.query();
104+
}
105+
106+
/**
107+
* How fast is our advice to use a cached api for each http request, without considering network?
108+
*/
109+
@Benchmark
110+
public Response buildAndQuery_fake_cachedApi() {
111+
return cachedFakeApi.query();
112+
}
113+
}

0 commit comments

Comments
 (0)