Skip to content

Instantly share code, notes, and snippets.

@renatoathaydes
Last active May 17, 2020 16:50
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 renatoathaydes/9dac781c33f10865d21d8b5dd439e667 to your computer and use it in GitHub Desktop.
Save renatoathaydes/9dac781c33f10865d21d8b5dd439e667 to your computer and use it in GitHub Desktop.
Taking Project Loom for a spin
package loom;
import rawhttp.core.RawHttp;
import rawhttp.core.RawHttpRequest;
import rawhttp.core.RawHttpResponse;
import rawhttp.core.client.TcpRawHttpClient;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Client {
private static RawHttpRequest createRequest() throws IOException {
return new RawHttp().parseRequest("GET http://localhost:8080/\nAccept: */*").eagerly();
}
static void run(int threads) throws IOException, InterruptedException {
System.err.println("Starting client with " + threads + " Threads");
var latch = new CountDownLatch(threads);
var request = createRequest();
for (int i = 1; i <= threads; i++) {
if (i % 100 == 0) System.err.println("Starting client Thread " + i);
final var index = i;
Thread.startVirtualThread(() -> {
var start = System.nanoTime();
Thread.startVirtualThread(() -> System.out.println(start));
try (var client = new TcpRawHttpClient(new ClientOptions())) {
for (int n = 0; n < 10; n++) {
var response = client.send(request);
verifyResponse(index, response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
});
}
latch.await();
System.err.println("All " + threads + " client Threads done");
}
private static void verifyResponse(int index, RawHttpResponse<?> response) {
if (response.getStatusCode() != 200) {
System.err.println(index + ": Wrong status code - " + response);
} else {
response.getBody().ifPresentOrElse(body -> {
try {
var text = body.decodeBodyToString(StandardCharsets.UTF_8);
if (!text.equals("hello world")) {
System.err.println(index + ": Wrong body - '" + text + "'");
}
} catch (IOException e) {
e.printStackTrace();
}
}, () -> System.err.println(index + ": Missing body"));
}
}
}
class ClientOptions extends TcpRawHttpClient.DefaultOptions {
@Override
public ExecutorService getExecutorService() {
return Executors.newUnboundedVirtualThreadExecutor();
}
}
package loom;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
if (args.length > 0 && args[0].equals("client")) {
System.err.println("Hit any key to continue (time to prepare your profiling tools)...");
System.in.read();
var threads = args.length == 2 ? Integer.parseInt(args[1]) : 100;
Client.run(threads);
} else {
Server.run();
}
}
}
<?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>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
</properties>
<groupId>com.athaydes</groupId>
<artifactId>loom-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<transformers>
<transformer implementation=
"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>loom.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.athaydes.rawhttp</groupId>
<artifactId>rawhttp-core</artifactId>
<version>2.2.2</version>
</dependency>
</dependencies>
</project>
package loom;
import rawhttp.core.HttpVersion;
import rawhttp.core.RawHttpHeaders;
import rawhttp.core.RawHttpResponse;
import rawhttp.core.StatusLine;
import rawhttp.core.body.StringBody;
import rawhttp.core.server.TcpRawHttpServer;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Server {
private static final RawHttpResponse<?> responseOK = new RawHttpResponse<>(
null, null, new StatusLine(HttpVersion.HTTP_1_1, 200, "OK"),
RawHttpHeaders.empty(), null);
public static void run() {
var server = new TcpRawHttpServer(new ServerOptions());
final var connections = new AtomicInteger();
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
System.out.println("Live threads: " + connections.get());
}, 500, 1000, TimeUnit.MILLISECONDS);
server.start((req) -> {
connections.incrementAndGet();
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
return Optional.of(responseOK.withBody(
new StringBody("hello world", "text/plain")));
} finally {
connections.decrementAndGet();
}
});
}
}
class ServerOptions implements TcpRawHttpServer.TcpRawHttpServerOptions {
@Override
public ServerSocket getServerSocket() throws IOException {
return new ServerSocket(8080);
}
@Override
public ExecutorService createExecutorService() {
return Executors.newUnboundedVirtualThreadExecutor();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment