Created
January 8, 2019 15:25
-
-
Save pmlopes/648b35d85d789345ba50fca299e3ad15 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.vertx.benchmark; | |
import io.reactiverse.pgclient.*; | |
import io.vertx.core.AbstractVerticle; | |
import io.vertx.core.Vertx; | |
import io.vertx.core.http.HttpServerRequest; | |
import io.vertx.core.json.*; | |
import java.time.ZonedDateTime; | |
import java.time.format.DateTimeFormatter; | |
import java.util.concurrent.ThreadLocalRandom; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
import io.vertx.ext.web.Router; | |
public class App extends AbstractVerticle { | |
public static void main(String[] args) { | |
Vertx.vertx().deployVerticle(new App()); | |
} | |
private final String server = "vertx-svm"; | |
private String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now()); | |
/** | |
* Returns the value of the "queries" getRequest parameter, which is an integer | |
* bound between 1 and 500 with a default value of 1. | |
* | |
* @param request the current HTTP request | |
* @return the value of the "queries" parameter | |
*/ | |
private static int getQueries(HttpServerRequest request) { | |
String param = request.getParam("queries"); | |
if (param == null) { | |
return 1; | |
} | |
try { | |
int parsedValue = Integer.parseInt(param); | |
return Math.min(500, Math.max(1, parsedValue)); | |
} catch (NumberFormatException e) { | |
return 1; | |
} | |
} | |
/** | |
* Returns a random integer that is a suitable value for both the {@code id} | |
* and {@code randomNumber} properties of a world object. | |
* | |
* @return a random world number | |
*/ | |
private static int randomWorld() { | |
return 1 + ThreadLocalRandom.current().nextInt(10000); | |
} | |
@Override | |
public void start() { | |
final Router app = Router.router(vertx); | |
vertx.setPeriodic(1000, handler -> date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())); | |
/* | |
* This test exercises the framework fundamentals including keep-alive support, request routing, request header | |
* parsing, object instantiation, JSON serialization, response header generation, and request count throughput. | |
*/ | |
app.get("/json").handler(ctx -> { | |
ctx.response() | |
.putHeader("Server", server) | |
.putHeader("Date", date) | |
.putHeader("Content-Type", "application/json") | |
.end(new JsonObject().put("message", "Hello, World!").toBuffer()); | |
}); | |
final String SELECT_WORLD = "SELECT id, randomnumber from WORLD where id=$1"; | |
PgClient client = PgClient.pool( | |
vertx, | |
new PgPoolOptions() | |
.setHost("tfb-database") | |
.setUser("benchmarkdbuser") | |
.setPassword("benchmarkdbpass") | |
.setDatabase("hello_world")); | |
/* | |
* This test exercises the framework's object-relational mapper (ORM), random number generator, database driver, | |
* and database connection pool. | |
*/ | |
app.get("/db").handler(ctx -> { | |
client.preparedQuery(SELECT_WORLD, Tuple.of(randomWorld()), res -> { | |
if (res.succeeded()) { | |
PgIterator resultSet = res.result().iterator(); | |
if (!resultSet.hasNext()) { | |
ctx.fail(404); | |
return; | |
} | |
Row row = resultSet.next(); | |
ctx.response() | |
.putHeader("Server", server) | |
.putHeader("Date", date) | |
.putHeader("Content-Type", "application/json") | |
.end(new JsonObject().put("id", row.getInteger(0)).put("randomNumber", row.getInteger(1)).toBuffer()); | |
} else { | |
ctx.fail(res.cause()); | |
} | |
}); | |
}); | |
/* | |
* This test is a variation of Test #2 and also uses the World table. Multiple rows are fetched to more dramatically | |
* punish the database driver and connection pool. At the highest queries-per-request tested (20), this test | |
* demonstrates all frameworks' convergence toward zero requests-per-second as database activity increases. | |
*/ | |
app.get("/queries").handler(ctx -> { | |
final AtomicBoolean failed = new AtomicBoolean(false); | |
JsonArray worlds = new JsonArray(); | |
final int queries = getQueries(ctx.request()); | |
for (int i = 0; i < queries; i++) { | |
client.preparedQuery(SELECT_WORLD, Tuple.of(randomWorld()), ar -> { | |
if (!failed.get()) { | |
if (ar.failed()) { | |
failed.set(true); | |
ctx.fail(ar.cause()); | |
return; | |
} | |
// we need a final reference | |
final Row row = ar.result().iterator().next(); | |
worlds.add(new JsonObject().put("id", row.getInteger(0)).put("randomNumber", row.getInteger(1))); | |
// stop condition | |
if (worlds.size() == queries) { | |
ctx.response() | |
.putHeader("Server", server) | |
.putHeader("Date", date) | |
.putHeader("Content-Type", "application/json") | |
.end(worlds.toBuffer()); | |
} | |
} | |
}); | |
} | |
}); | |
/* | |
* This test is an exercise of the request-routing fundamentals only, designed to demonstrate the capacity of | |
* high-performance platforms in particular. Requests will be sent using HTTP pipelining. The response payload is | |
* still small, meaning good performance is still necessary in order to saturate the gigabit Ethernet of the test | |
* environment. | |
*/ | |
app.get("/plaintext").handler(ctx -> { | |
ctx.response() | |
.putHeader("Server", server) | |
.putHeader("Date", date) | |
.putHeader("Content-Type", "text/plain") | |
.end("Hello, World!"); | |
}); | |
vertx | |
.createHttpServer() | |
.requestHandler(app) | |
.listen(8080, "0.0.0.0", listen -> { | |
if (listen.succeeded()) { | |
System.out.println("Server listening at: http://localhost:8080/"); | |
} else { | |
listen.cause().printStackTrace(); | |
} | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<project xmlns="http://maven.apache.org/POM/4.0.0" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>io.vertx</groupId> | |
<artifactId>vertx-web-benchmark</artifactId> | |
<version>3.6.0-SNAPSHOT</version> | |
<properties> | |
<!-- the main class --> | |
<main.verticle>io.vertx.benchmark.App</main.verticle> | |
</properties> | |
<profiles> | |
<profile> | |
<id>native-image</id> | |
<dependencies> | |
<dependency> | |
<groupId>com.oracle</groupId> | |
<artifactId>substrate</artifactId> | |
<version>1.0.0-rc8</version> | |
<scope>system</scope> | |
<systemPath>${java.home}/lib/svm/builder/svm.jar</systemPath> | |
</dependency> | |
</dependencies> | |
<build> | |
<defaultGoal>package exec:exec</defaultGoal> | |
<plugins> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>build-helper-maven-plugin</artifactId> | |
<version>3.0.0</version> | |
<executions> | |
<execution> | |
<phase>generate-sources</phase> | |
<goals> | |
<goal>add-source</goal> | |
</goals> | |
<configuration> | |
<sources> | |
<source>src/main/svm</source> | |
</sources> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
<plugin> | |
<groupId>org.codehaus.mojo</groupId> | |
<artifactId>exec-maven-plugin</artifactId> | |
<version>1.6.0</version> | |
<executions> | |
<execution> | |
<goals> | |
<goal>exec</goal> | |
</goals> | |
</execution> | |
</executions> | |
<configuration> | |
<executable>${java.home}/bin/native-image</executable> | |
<arguments> | |
<argument>--no-server</argument> | |
<argument>--enable-all-security-services</argument> | |
<argument>--delay-class-initialization-to-runtime=io.netty.handler.codec.http.HttpObjectEncoder,io.netty.handler.codec.http2.Http2CodecUtil,io.netty.handler.codec.http2.DefaultHttp2FrameWriter,io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder,io.netty.handler.ssl.JdkNpnApplicationProtocolNegotiator,io.netty.handler.ssl.ReferenceCountedOpenSslEngine</argument> | |
<argument>-Dvertx.disableDnsResolver=true</argument> | |
<argument>-H:Path=${project.build.directory}</argument> | |
<argument>-H:IncludeResources=(META-INF/vertx|META-INF/services|static|webroot|template)/.*</argument> | |
<argument>-H:+ReportUnsupportedElementsAtRuntime</argument> | |
<argument>-H:ReflectionConfigurationFiles=${project.basedir}/src/main/svm/reflection.json</argument> | |
<argument>-jar</argument> | |
<argument>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</argument> | |
<argument>-H:Name=${project.name}</argument> | |
</arguments> | |
</configuration> | |
</plugin> | |
</plugins> | |
</build> | |
</profile> | |
</profiles> | |
<dependencies> | |
<dependency> | |
<groupId>io.vertx</groupId> | |
<artifactId>vertx-core</artifactId> | |
<version>3.6.0-SNAPSHOT</version> | |
</dependency> | |
<dependency> | |
<groupId>io.vertx</groupId> | |
<artifactId>vertx-web</artifactId> | |
<version>3.6.0-SNAPSHOT</version> | |
</dependency> | |
<dependency> | |
<groupId>io.reactiverse</groupId> | |
<artifactId>reactive-pg-client</artifactId> | |
<version>0.10.5</version> | |
</dependency> | |
</dependencies> | |
<build> | |
<pluginManagement> | |
<plugins> | |
<!-- We specify the Maven compiler plugin as we need to set it to Java 1.8 --> | |
<plugin> | |
<artifactId>maven-compiler-plugin</artifactId> | |
<version>3.1</version> | |
<configuration> | |
<source>1.8</source> | |
<target>1.8</target> | |
</configuration> | |
</plugin> | |
</plugins> | |
</pluginManagement> | |
<!-- | |
You only need the part below if you want to build your application into a fat executable jar. | |
This is a jar that contains all the dependencies required to run it, so you can just run it with | |
java -jar | |
--> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.maven.plugins</groupId> | |
<artifactId>maven-shade-plugin</artifactId> | |
<version>2.3</version> | |
<executions> | |
<execution> | |
<phase>package</phase> | |
<goals> | |
<goal>shade</goal> | |
</goals> | |
<configuration> | |
<transformers> | |
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | |
<manifestEntries> | |
<Main-Class>${main.verticle}</Main-Class> | |
</manifestEntries> | |
</transformer> | |
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> | |
<resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> | |
</transformer> | |
</transformers> | |
<artifactSet> | |
</artifactSet> | |
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile> | |
</configuration> | |
</execution> | |
</executions> | |
</plugin> | |
</plugins> | |
</build> | |
</project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.oracle.svm.core.annotate.*; | |
import org.graalvm.nativeimage.*; | |
import io.netty.handler.codec.compression.*; | |
import io.netty.util.internal.logging.InternalLoggerFactory; | |
import io.netty.util.internal.logging.JdkLoggerFactory; | |
import io.vertx.core.Vertx; | |
import io.vertx.core.dns.AddressResolverOptions; | |
import io.vertx.core.impl.resolver.DefaultResolverProvider; | |
import io.vertx.core.spi.resolver.ResolverProvider; | |
/** | |
* This substitution avoid having loggers added to the build | |
*/ | |
@TargetClass(className = "io.netty.util.internal.logging.InternalLoggerFactory") | |
final class TargetInternalLoggerFactory { | |
@Substitute | |
private static InternalLoggerFactory newDefaultFactory(String name) { | |
return JdkLoggerFactory.INSTANCE; | |
} | |
} | |
/** | |
* This substitution allows the usage of platform specific code to do low level buffer related tasks | |
*/ | |
@TargetClass(className = "io.netty.util.internal.CleanerJava6") | |
final class TargetCleanerJava6 { | |
@Alias | |
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClassName = "java.nio.DirectByteBuffer", name = "cleaner") | |
private static long CLEANER_FIELD_OFFSET; | |
} | |
/** | |
* This substitution allows the usage of platform specific code to do low level buffer related tasks | |
*/ | |
@TargetClass(className = "io.netty.util.internal.PlatformDependent0") | |
final class TargetPlatformDependent0 { | |
@Alias | |
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FieldOffset, declClassName = "java.nio.Buffer", name = "address") | |
private static long ADDRESS_FIELD_OFFSET; | |
} | |
/** | |
* This substitution allows the usage of platform specific code to do low level buffer related tasks | |
*/ | |
@TargetClass(className = "io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess") | |
final class TargetUnsafeRefArrayAccess { | |
@Alias | |
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.ArrayIndexShift, declClass = Object[].class) | |
public static int REF_ELEMENT_SHIFT; | |
} | |
/** | |
* This substitution avoid having jcraft zlib added to the build | |
*/ | |
@TargetClass(className = "io.netty.handler.codec.compression.ZlibCodecFactory") | |
final class TargetZlibCodecFactory { | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(int compressionLevel) { | |
return new JdkZlibEncoder(compressionLevel); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper) { | |
return new JdkZlibEncoder(wrapper); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { | |
return new JdkZlibEncoder(wrapper, compressionLevel); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { | |
return new JdkZlibEncoder(wrapper, compressionLevel); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(byte[] dictionary) { | |
return new JdkZlibEncoder(dictionary); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(int compressionLevel, byte[] dictionary) { | |
return new JdkZlibEncoder(compressionLevel, dictionary); | |
} | |
@Substitute | |
public static ZlibEncoder newZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { | |
return new JdkZlibEncoder(compressionLevel, dictionary); | |
} | |
@Substitute | |
public static ZlibDecoder newZlibDecoder() { | |
return new JdkZlibDecoder(true); | |
} | |
@Substitute | |
public static ZlibDecoder newZlibDecoder(ZlibWrapper wrapper) { | |
return new JdkZlibDecoder(wrapper, true); | |
} | |
@Substitute | |
public static ZlibDecoder newZlibDecoder(byte[] dictionary) { | |
return new JdkZlibDecoder(dictionary); | |
} | |
} | |
/** | |
* This substitution forces the usage of the blocking DNS resolver | |
*/ | |
@TargetClass(className = "io.vertx.core.spi.resolver.ResolverProvider") | |
final class TargetResolverProvider { | |
@Substitute | |
public static ResolverProvider factory(Vertx vertx, AddressResolverOptions options) { | |
return new DefaultResolverProvider(); | |
} | |
} | |
@AutomaticFeature | |
class RuntimeReflectionRegistrationFeature implements Feature { | |
public void beforeAnalysis(BeforeAnalysisAccess access) { | |
try { | |
RuntimeReflection.register(java.util.LinkedHashMap.class.getDeclaredConstructor()); | |
} catch (NoSuchMethodException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment