diff --git a/benchmark/pom.xml b/benchmark/pom.xml index a337237a..30e834ac 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -27,13 +27,34 @@ com.netflix.feign - feign-jaxrs + feign-okhttp ${project.version} - javax.ws.rs - jsr311-api - 1.1.1 + com.squareup.okhttp + mockwebserver + 2.3.0 + + + org.bouncycastle + bcprov-jdk15on + + + + + io.reactivex + rxnetty + 0.4.8 + + + io.reactivex + rxjava + 1.0.9 + + + io.netty + netty-codec-http + 4.0.26.Final org.openjdk.jmh diff --git a/benchmark/src/main/java/feign/benchmark/ContractBenchmarks.java b/benchmark/src/main/java/feign/benchmark/ContractBenchmarks.java deleted file mode 100644 index 4ac6c112..00000000 --- a/benchmark/src/main/java/feign/benchmark/ContractBenchmarks.java +++ /dev/null @@ -1,46 +0,0 @@ -package feign.benchmark; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; - -import java.util.concurrent.TimeUnit; - -import feign.Contract; -import feign.jaxrs.JAXRSContract; - -@Measurement(iterations = 5, time = 1) -@Warmup(iterations = 5, time = 1) -@Fork(3) -@BenchmarkMode(Mode.Throughput) -@OutputTimeUnit(TimeUnit.SECONDS) -@State(Scope.Thread) -public class ContractBenchmarks { - - private Contract feignContract; - private Contract jaxrsContract; - - @Setup - public void setup() { - feignContract = new Contract.Default(); - jaxrsContract = new JAXRSContract(); - } - - @Benchmark - public void parseFeign() { - feignContract.parseAndValidatateMetadata(FeignTestInterface.class); - } - - @Benchmark - public void parseJAXRS() { - jaxrsContract.parseAndValidatateMetadata(JAXRSTestInterface.class); - } - -} diff --git a/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java b/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java index d2063119..bfe66619 100644 --- a/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java +++ b/benchmark/src/main/java/feign/benchmark/FeignTestInterface.java @@ -2,8 +2,6 @@ package feign.benchmark; import java.util.List; -import javax.ws.rs.HeaderParam; - import feign.Body; import feign.Headers; import feign.Param; @@ -35,5 +33,5 @@ interface FeignTestInterface { @RequestLine("POST /") @Headers({"Happy: sad", "Auth-Token: {authToken}"}) - void headers(@HeaderParam("authToken") String token); + void headers(@Param("authToken") String token); } diff --git a/benchmark/src/main/java/feign/benchmark/JAXRSTestInterface.java b/benchmark/src/main/java/feign/benchmark/JAXRSTestInterface.java deleted file mode 100644 index 5889f963..00000000 --- a/benchmark/src/main/java/feign/benchmark/JAXRSTestInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package feign.benchmark; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; - -import feign.Headers; -import feign.Response; - -@Consumes("application/json") -interface JAXRSTestInterface { - - @GET - @Path("/?Action=GetUser&Version=2010-05-08&limit=1") - Response query(); - - @GET - @Path("/domains/{domainId}/records") - Response mixedParams(@PathParam("domainId") int id, @QueryParam("name") String nameFilter, - @QueryParam("type") String typeFilter); - - @PATCH - Response customMethod(); - - @Target({ElementType.METHOD}) - @Retention(RetentionPolicy.RUNTIME) - @HttpMethod("PATCH") - @interface PATCH { - - } - - @PUT - @Produces("application/json") - void bodyParam(List body); - - @POST - void form(@FormParam("customer_name") String customer, @FormParam("user_name") String user, - @FormParam("password") String password); - - @POST - @Headers("Happy: sad") - void headers(@HeaderParam("Auth-Token") String token); -} diff --git a/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java b/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java new file mode 100644 index 00000000..fdbd401f --- /dev/null +++ b/benchmark/src/main/java/feign/benchmark/RealRequestBenchmarks.java @@ -0,0 +1,85 @@ +package feign.benchmark; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import feign.Feign; +import feign.Response; +import io.netty.buffer.ByteBuf; +import io.reactivex.netty.RxNetty; +import io.reactivex.netty.protocol.http.server.HttpServer; +import io.reactivex.netty.protocol.http.server.HttpServerRequest; +import io.reactivex.netty.protocol.http.server.HttpServerResponse; +import io.reactivex.netty.protocol.http.server.RequestHandler; + +@Measurement(iterations = 5, time = 1) +@Warmup(iterations = 10, time = 1) +@Fork(3) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Benchmark) +public class RealRequestBenchmarks { + + private static final int SERVER_PORT = 8765; + private HttpServer server; + private OkHttpClient client; + private FeignTestInterface okFeign; + private Request queryRequest; + + @Setup + public void setup() { + server = RxNetty.createHttpServer(SERVER_PORT, new RequestHandler() { + public rx.Observable handle(HttpServerRequest request, + HttpServerResponse response) { + return response.flush(); + } + }); + server.start(); + client = new OkHttpClient(); + client.setRetryOnConnectionFailure(false); + okFeign = Feign.builder() + .client(new feign.okhttp.OkHttpClient(client)) + .target(FeignTestInterface.class, "http://localhost:" + SERVER_PORT); + queryRequest = new Request.Builder() + .url("http://localhost:" + SERVER_PORT + "/?Action=GetUser&Version=2010-05-08&limit=1") + .build(); + } + + @TearDown + public void tearDown() throws InterruptedException { + server.shutdown(); + } + + /** + * How fast can we execute get commands synchronously? + */ + @Benchmark + public com.squareup.okhttp.Response query_baseCaseUsingOkHttp() throws IOException { + com.squareup.okhttp.Response result = client.newCall(queryRequest).execute(); + result.body().close(); + return result; + } + + /** + * How fast can we execute get commands synchronously using Feign? + */ + @Benchmark + public Response query_feignUsingOkHttp() { + return okFeign.query(); + } +} diff --git a/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java b/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java new file mode 100644 index 00000000..239e7b75 --- /dev/null +++ b/benchmark/src/main/java/feign/benchmark/WhatShouldWeCacheBenchmarks.java @@ -0,0 +1,109 @@ +package feign.benchmark; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import feign.Client; +import feign.Contract; +import feign.Feign; +import feign.MethodMetadata; +import feign.Request; +import feign.Response; +import feign.Target.HardCodedTarget; + +@Measurement(iterations = 5, time = 1) +@Warmup(iterations = 10, time = 1) +@Fork(3) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Thread) +public class WhatShouldWeCacheBenchmarks { + + private Contract feignContract; + private Contract cachedContact; + private Client fakeClient; + private Feign cachedFakeFeign; + private FeignTestInterface cachedFakeApi; + + @Setup + public void setup() { + feignContract = new Contract.Default(); + cachedContact = new Contract() { + private final List cached = + new Default().parseAndValidatateMetadata(FeignTestInterface.class); + + public List parseAndValidatateMetadata(Class declaring) { + return cached; + } + }; + fakeClient = new Client() { + public Response execute(Request request, Request.Options options) throws IOException { + Map> headers = new LinkedHashMap>(); + return Response.create(200, "ok", headers, (byte[]) null); + } + }; + cachedFakeFeign = Feign.builder().client(fakeClient).build(); + cachedFakeApi = cachedFakeFeign.newInstance( + new HardCodedTarget(FeignTestInterface.class, "http://localhost")); + } + + /** + * How fast is parsing an api interface? + */ + @Benchmark + public List parseFeignContract() { + return feignContract.parseAndValidatateMetadata(FeignTestInterface.class); + } + + /** + * How fast is creating a feign instance for each http request, without considering network? + */ + @Benchmark + public Response buildAndQuery_fake() { + return Feign.builder().client(fakeClient) + .target(FeignTestInterface.class, "http://localhost").query(); + } + + /** + * How fast is creating a feign instance for each http request, without considering network, and + * without re-parsing the annotated http api? + */ + @Benchmark + public Response buildAndQuery_fake_cachedContract() { + return Feign.builder().contract(cachedContact).client(fakeClient) + .target(FeignTestInterface.class, "http://localhost").query(); + } + + /** + * How fast re-parsing the annotated http api for each http request, without considering network? + */ + @Benchmark + public Response buildAndQuery_fake_cachedFeign() { + return cachedFakeFeign.newInstance( + new HardCodedTarget(FeignTestInterface.class, "http://localhost")) + .query(); + } + + /** + * How fast is our advice to use a cached api for each http request, without considering network? + */ + @Benchmark + public Response buildAndQuery_fake_cachedApi() { + return cachedFakeApi.query(); + } +}