Choosing Quasar Fibers for High-Concurrency Jetty Services: Decision Guide
Learn when to adopt Quasar fibers in a Jetty web service, compare alternatives, understand trade‑offs, and see a runnable example with verification steps.
07 Oct 2025, 09:33 UTC

Decision and Constraints
Adopt Quasar fibers when you need to handle thousands of concurrent HTTP requests with a small number of OS threads, while keeping the familiar blocking‑style code flow. This decision assumes:
- Java 8 or later.
- Jetty 9.4+ (embedded or standalone).
- Quasar core library and the Quasar Maven/Gradle plugin for bytecode instrumentation.
- Ability to enable the
@Suspendableannotation on methods that may yield.
If any of these constraints cannot be met, consider the alternatives described below.
Comparison of Options
| Option | Concurrency Model | h>OverheadCode Changes | Instrumentation Needed? | Typical Use | |
|---|---|---|---|---|---|
| A) Traditional Jetty thread pool | One OS thread per request | High (thread stack, context switch) | None | No | Low‑to‑moderate traffic, simple services |
| B) Quasar fibers with Jetty continuation | Many fibers share a small carrier‑thread pool | Near‑zero context‑switch cost | Mark suspendable methods with @Suspendable |
Yes (bytecode weaving) | High concurrency, blocking‑style code desired |
| C) Quasar actors via Comsat | Mailbox‑driven actor model | Mailbox allocation overhead | Actor‑style classes, message passing | Yes (weaving) | Decoupled workflows, event‑driven services |
| D) Reactive frameworks (Vert.x, Spring WebFlux) | Async‑non‑reactive pipelines | Low thread usage, but async‑style code | Reactive chains, callbacks or Mono/Flux |
No | When you can embrace async APIs end‑to‑end |
Trade‑offs
Quasar fibers (Option B) give the lowest context‑switch cost and let you keep familiar blocking I/O (e.g., Thread.sleep, Jetty continuations, Quasar‑provided locks). The trade‑off is the requirement for bytecode instrumentation; if the @Suspendable annotation is missing or the plugin is misconfigured, the JVM throws IllegalStateException: method not suspendable. Additionally, only certain blocking APIs are fiber‑aware; using standard java.util.concurrent locks without marking them suspendable will block the underlying carrier thread, negating the benefit.
Actors (Option C) avoid deep stack weaving but introduce mailbox overhead and require a shift to message‑passing style. Reactive frameworks (Option D) eliminate weaving entirely but demand an asynchronous programming model throughout the stack, which can be a larger migration effort.
Concrete Implementation and Validation
The following steps show how to create a minimal embedded Jetty service that uses a Quasar fiber for each request.
1. Add Quasar Maven plugin
Place this inside the <build><plugins> section of your pom.xml (adjust versions as needed):
<plugin>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-maven-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<goals>
<goal>instrument</goal>
</goals>
</execution>
</executions>
<configuration>
<invokeStatic>true</invokeStatic>
<skip>false</skip>
</configuration>
</plugin>
Also add the Quasar core dependency:
<dependency>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>0.8.0</version>
</dependency>
2. Create a Jetty handler that runs in a fiber
import co.paralleluniverse.fibers.Suspendable;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class FiberHandler extends AbstractHandler {
@Override
@Suspendable
public void handle(String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// Simulate work that yields the fiber
Thread.sleep(100); // 100 ms blocking sleep, safe inside a fiber
response.setContentType("text/plain;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().write("OK\n");
baseRequest.setHandled(true);
}
}
3. Bootstrap an embedded Jetty server
public class Main {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
server.setHandler(new FiberHandler());
server.start();
server.join();
}
}
4. Build and run
From a terminal with Maven installed:
mvn clean package
java -jar target/your-artifact.jar
Ensure the user running the command can bind to port 8080 (choose a higher port if you lack privileged access).
5. Verify low carrier‑thread usage
While the server is running, generate load with a tool such as wrk or ab:
wrk -t12 -c5000 -d30s http://localhost:8080/
Then inspect the JVM threads. For example, using jcmd:
jcmd <pid> Thread.print
Look for a small number of threads named something like "FiberPool‑worker-". The count should stay well below the number of concurrent connections (e.g., < 20 threads while handling 5 000+ simultaneous requests).
Check the console/log for lines similar to:
INFO: Fiber started
Absence of warnings like "Method not suspendable" indicates the bytecode weaving succeeded.
6. Practical checks and limitations
- Limitation: Only Jetty’s continuation API and Quasar‑provided synchronization primitives are fiber‑aware. Using a standard
java.util.concurrent.ReentrantLockinside a fiber without marking it@Suspendablewill block the carrier thread. - How to check: Replace a Quasar‑aware lock with a standard lock and re‑run the load test; you should see carrier‑thread count rise close to the concurrency level.
- Risk: Incorrect plugin configuration (e.g., missing
<invokeStatic>true</invokeStatic>) leads toIllegalStateExceptionat startup. Verify the plugin logs duringmvn packagefor the "Instrumenting" messages. - Rollback: If you decide not to use fibers, simply remove the Quasar dependency and plugin, and revert the handler to a non‑suspendable implementation (remove
@Suspendableand replaceThread.sleepwith an async callback or reactive approach). No persistent state is changed by the code itself, so a rollback is just a source‑code change.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.