Skip to content

Commit 274e5f6

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 ed289f4 commit 274e5f6

6 files changed

Lines changed: 220 additions & 110 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 & 46 deletions
This file was deleted.

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import java.util.List;
44

5-
import javax.ws.rs.HeaderParam;
6-
75
import feign.Body;
86
import feign.Headers;
97
import feign.Param;
@@ -35,5 +33,5 @@ void form(@Param("customer_name") String customer, @Param("user_name") String us
3533

3634
@RequestLine("POST /")
3735
@Headers({"Happy: sad", "Auth-Token: {authToken}"})
38-
void headers(@HeaderParam("authToken") String token);
36+
void headers(@Param("authToken") String token);
3937
}

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

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

0 commit comments

Comments
 (0)