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