Skip to content

Instantly share code, notes, and snippets.

@pmlopes
Created January 8, 2019 15:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pmlopes/648b35d85d789345ba50fca299e3ad15 to your computer and use it in GitHub Desktop.
Save pmlopes/648b35d85d789345ba50fca299e3ad15 to your computer and use it in GitHub Desktop.
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();
}
});
}
}
<?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>
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