Practical non blocking microservices in java 8
-
Upload
michal-balinski -
Category
Technology
-
view
63 -
download
3
Transcript of Practical non blocking microservices in java 8
![Page 1: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/1.jpg)
Practicalnon-blocking microservices
in Java 8
Michał BalińskiSystem Architect
Oleksandr GoldobinSystem Architect
![Page 2: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/2.jpg)
Cloud of microservices for secure IoT
gateway backingservice
coreservice
gateway
gatewaycore
servicebackingservice
![Page 3: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/3.jpg)
The demand and characteristics of our domain
Crowded IoT environment
Slow, long lived connections
Big amount of concurrent connections
Scalability and resilience
Rather I/O intensive than CPU intensive
![Page 4: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/4.jpg)
OTA Gateway – Use Case
TSM
TrustedService
Manager
![Page 5: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/5.jpg)
OTA Gateway – Use Case
TSM
TrustedService
Manager
Security Module
![Page 6: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/6.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
![Page 7: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/7.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
![Page 8: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/8.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
![Page 9: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/9.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
TCP
3. store scripts
![Page 10: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/10.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
TCP
4. submition response
3. store scripts
![Page 11: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/11.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
TCP
4. submition response
HTTP5. poll scripts
3. store scripts
![Page 12: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/12.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
TCP
3. store scripts
4. submition response
HTTP5. poll scripts
6. search scripts
![Page 13: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/13.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP
1. submit scripts
HTTP2. encrypt
scripts
TCP
3. store scripts
4. submition response
HTTP5. poll scripts
6. search scripts
7. response with scripts
![Page 14: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/14.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB
HTTP HTTP
HTTP
TCP
![Page 15: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/15.jpg)
OTA Gateway – Use Case
OTA(Over-the-Air)
Gateway
TSM
TrustedService
Manager
Security Module
DB LogStorage
HTTP HTTP
HTTP
TCP File I/OMonitoringSystem
HTTP
![Page 16: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/16.jpg)
Let’s test blocking approach!
![Page 17: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/17.jpg)
OTA Gateway Blocking - technologies
May 3, 2023 17
TSM
DB LogStorage
HTTP
TCP STDOUT
OTAGateway
JAX-RS
Logback appender
Security Module
JAX-RS
Jedis
JAX-RS Client
![Page 18: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/18.jpg)
OTA Gateway – Blocking - test
OTA(Over-the-Air)
Gateway
send 2 scripts per
request
Security Module
DB LogStorage
HTTP HTTP
HTTP
TCP File I/O
emulated latency200 ms
expected latency
< 450 ms
verified with throughput
over 7k req/s
![Page 19: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/19.jpg)
Blocking – 1k threads
OTA(Over-the-Air)
Gateway
send 2 scripts per
request
Security Module
DB LogStorage
HTTP HTTP
HTTP
TCP File I/O
emulated latency200 ms
max 1000connections
max 1000 threads
expected latency
< 450 ms
![Page 20: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/20.jpg)
Blocking – 1k threads500 req/s 1000 req/s 1500 req/s 2000 req/s
![Page 21: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/21.jpg)
The drawbacks of classic synchronous I/O
One thread per connection
Threads waiting instead of running
Context switches
Resource wasting (~1 MB per thread - 64bit)
![Page 22: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/22.jpg)
Let’s switch from blocking to non-blocking
![Page 23: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/23.jpg)
OTA Gateway Non-blocking - technologies
May 3, 2023 23
TSM
DB LogStorage
HTTP
TCP STDOUT
OTAGateway
JAX-RS 2.0
Logback asyncappender
Async Http Client 2 (Netty)
Security Module
JAX-RS 2.0
Lettuce(Netty)
![Page 24: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/24.jpg)
Non-blocking – 16 threads
OTA(Over-the-Air)
Gateway
send 2 scripts per
request
Security Module
DB LogStorage
HTTP HTTP
HTTP
TCP File I/O
emulated latency200 ms
no limit for connections
max 16 threads
expected latency
< 450 ms
![Page 25: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/25.jpg)
Non-blocking – 16 threads1k req/s 2k req/s 2.5k req/s 3k req/s
![Page 26: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/26.jpg)
Blocking Non-blocking
1000 threads 56 threads
1.2 GB 0.5 GB
~1.2k r/s 2.5k r/s
![Page 27: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/27.jpg)
Let’s talk about challenges
![Page 28: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/28.jpg)
OTA Gateway Blocking – sequence diagram
OTAGatewayTSM Security
Module DB Logs
loop
1. submit scripts
2. encrypt script
3a. store script
4. submition response
3b. count scripts
![Page 29: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/29.jpg)
OTA Gateway Non-blocking – sequence diagram
OTAGatewayTSM Security
Module DB Logs
loop
1. submit scripts
2. encrypt script
3a. store script
4. submition response
3b. count scripts
![Page 30: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/30.jpg)
OTA Non-blocking – realityOTAGateway
TSM Security Module DB Logs
„loop
ed p
roce
ssin
g ch
ain”
1. submit scripts
4. submition response
3b. count scripts
HTT
PSe
rver
2. encrypt script
3a. store script
Logg
ing
DB
Clie
nt
Secu
rity
Cl
ient
![Page 31: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/31.jpg)
Code. Bird view
31May 3, 2023
package org.demo.ota.blocking.rest;
@Path("se")public class ScriptSubmissionResource extends Application {
private static final Logger log = LoggerFactory.getLogger(ScriptSubmissionResource.class); private static final ResourceMetrics METRICS = new ResourceMetrics("ota_submission");
private SecureModuleClient secureModuleClient = SecureModuleClient.instance(); private ScriptStorageClient scriptStorageClient = ScriptStorageClient.instance();
@POST @Path("/{seId}/scripts") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.TEXT_PLAIN) public long submitScripts(@PathParam("seId") String seId, List<Script> scripts) { MDC.put("flow", "submission"); MDC.put("se", seId);
return METRICS.instrument(() -> { log.debug("Processing {} scripts submission", scripts.size(), seId);
for (int i = 0; i < scripts.size(); i++) { final Script script = scripts.get(i);
log.debug("Encrypting {} script", i); final String encryptedPayload = secureModuleClient.encrypt(seId, script.getPayload()); script.setPayload(encryptedPayload);
log.debug("Storing encrypted script {}", i); scriptStorageClient.storeScript(seId, script); }
long numberOfScripts = scriptStorageClient.numberOfScriptsForSe(seId); log.debug("Request processed", seId); return numberOfScripts; }); }
@Override public Set<Object> getSingletons() { return Collections.singleton(this); }}
package org.demo.ota.nonblocking.rest;
@Path("se")public class ScriptSubmissionResource extends Application {
private static final Logger log = LoggerFactory.getLogger(ScriptSubmissionResource.class); private static final ResourceMetrics METRICS = new ResourceMetrics("ota_submission");
private SecureModuleClient secureModuleClient = SecureModuleClient.instance(); private ScriptStorageClient scriptStorageClient = ScriptStorageClient.instance();
@POST @Path("/{seId}/scripts") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.TEXT_PLAIN) public void submitScripts( @PathParam("seId") String seId, List<Script> scripts, @Suspended final AsyncResponse asyncResponse ) { final DiagnosticContext diagnosticContext = new DiagnosticContext("submission", seId);
METRICS.instrumentStage(() -> { log.debug("{} Processing {} scripts submission", diagnosticContext, scripts.size());
return encryptAndStoreAllScripts(diagnosticContext, seId, scripts) .thenCompose( ignore -> scriptStorageClient.numberOfScriptsForSe(seId) ); }) .whenComplete((numberOfScripts, e) -> { if (e != null) { asyncResponse.resume(e); } else { log.debug("{} Request processed", diagnosticContext); asyncResponse.resume(numberOfScripts); } }); }
private CompletionStage<Void> encryptAndStoreAllScripts( final DiagnosticContext diagnosticContext, final String seId, final List<Script> scripts ) { CompletionStage<Void> stage = null; // <- non final field, potential concurrent access bug!
for (int i = 0; i < scripts.size(); i++) { final int scriptIndex = i; final Script script = scripts.get(scriptIndex);
if (stage == null) { stage = encryptAndStoreSingleScript(diagnosticContext, seId, scriptIndex, script); } else { stage = stage.thenCompose(ignore -> encryptAndStoreSingleScript(diagnosticContext, seId, scriptIndex, script)); } }
return stage; }
private CompletionStage<Void> encryptAndStoreSingleScript( final DiagnosticContext diagnosticContext, final String seId, final int scriptIndex, final Script script ) { log.debug("{} Encrypting script {}", diagnosticContext, scriptIndex);
return secureModuleClient .encrypt(seId, script.getPayload()) .thenCompose( encryptedPayload -> { log.debug("{} Storing encrypted script {}", diagnosticContext, scriptIndex); return scriptStorageClient.storeScript(seId, new Script(encryptedPayload)); } ); }
@Override public Set<Object> getSingletons() { return new HashSet<>(Collections.singletonList(this)); }}
![Page 32: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/32.jpg)
Code. Blocking. Submission 1
May 3, 2023 32
@POST@Path("/{seId}/scripts")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.TEXT_PLAIN)public long submitScripts(@PathParam("seId") String seId, List<Script> scripts) {
MDC.put("flow", "submission"); // Setting diagnostic context MDC.put("se", seId);
return METRICS.instrument(() -> { // Instrumenting with metrics //...
![Page 33: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/33.jpg)
Code. Blocking. Submission 2
May 3, 2023 33
log.debug("Processing {} scripts submission", scripts.size(), seId);
for (int i = 0; i < scripts.size(); i++) { Cycle through the scripts
final Script script = scripts.get(i);
log.debug("Encrypting {} script", i); final String encryptedPayload = secureModuleClient .encrypt(seId, script.getPayload()); Encrypting the script
script.setPayload(encryptedPayload);
log.debug("Storing encrypted script {}", i); scriptStorageClient.storeScript(seId, script); Saving the script into
DB}
long numberOfScripts = scriptStorageClient.numberOfScriptsForSe(seId); Getting current number
of scripts in DB
log.debug("Request processed", seId);return numberOfScripts;
![Page 34: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/34.jpg)
Code. Non-blocking. Submission 1
May 3, 2023 34
@POST@Path("/{seId}/scripts")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.TEXT_PLAIN)public void submitScripts( @PathParam("seId") String seId, List<Script> scripts, @Suspended final AsyncResponse asyncResponse) { final DiagnosticContext diagnosticContext = new DiagnosticContext("submission", seId); Creating diagnostic
context
METRICS.instrumentStage(() -> { Instrumenting with metrics
![Page 35: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/35.jpg)
Code. Non-blocking. Submission 1
May 3, 2023 35
@POST@Path("/{seId}/scripts")@Consumes(MediaType.APPLICATION_JSON)@Produces(MediaType.TEXT_PLAIN)public void submitScripts( @PathParam("seId") String seId, List<Script> scripts, @Suspended final AsyncResponse asyncResponse) { final DiagnosticContext diagnosticContext = new DiagnosticContext("submission", seId); Creating diagnostic
context
METRICS.instrumentStage(() -> { Instrumenting with metrics
![Page 36: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/36.jpg)
Code. Non-blocking. Submission 2
May 3, 2023 36
METRICS.instrumentStage(() -> { log.debug(
"{} Processing {} scripts submission", diagnosticContext, scripts.size());
return encryptAndStoreAllScripts(diagnosticContext, seId, scripts) .thenCompose( ignore -> scriptStorageClient.numberOfScriptsForSe(seId) );}).whenComplete((numberOfScripts, e) -> { if (e != null) { asyncResponse.resume(e); } else { log.debug("{} Request processed", diagnosticContext); asyncResponse.resume(numberOfScripts); }});
![Page 37: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/37.jpg)
Code. Non-blocking. Submission 3
May 3, 2023 37
private CompletionStage<Void> encryptAndStoreAllScripts( final DiagnosticContext diagnosticContext, final String seId, final List<Script> scripts) { CompletionStage<Void> stage = null; // <- non final field, potential // concurrent access bug!
for (int i = 0; i < scripts.size(); i++) { Cycle through the scripts
final int scriptIndex = i; final Script script = scripts.get(scriptIndex);
if (stage == null) { stage = encryptAndStoreSingleScript(
diagnosticContext, seId, scriptIndex, script); } else { stage = stage.thenCompose(ignore -> encryptAndStoreSingleScript(
diagnosticContext, seId, scriptIndex, script)); } } return stage;}
![Page 38: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/38.jpg)
Code. Non-blocking. Submission 4
May 3, 2023 38
private CompletionStage<Void> encryptAndStoreSingleScript( final DiagnosticContext diagnosticContext, final String seId, final int scriptIndex, final Script script) { log.debug("{} Encrypting script {}", diagnosticContext, scriptIndex);
return secureModuleClient .encrypt(seId, script.getPayload()) Encrypting the script .thenCompose( encryptedPayload -> { log.debug(
"{} Storing encrypted script {}", diagnosticContext, scriptIndex);
return scriptStorageClient.storeScript( Saving the script into seId,
the DB new Script(encryptedPayload));
} );}
![Page 39: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/39.jpg)
Code. Blocking. Integration
May 3, 2023 39
private final JedisPool pool;
public void storeScript(String seId, Script script) { try (Jedis jedis = pool.getResource()) { jedis.rpush(seId, script.getPayload()); }}
public long numberOfScriptsForSe(String seId) { try (Jedis jedis = pool.getResource()) { return jedis.llen(seId); }}
public Optional<Script> nextScript(String seId) { try (Jedis jedis = pool.getResource()) { return Optional.ofNullable(jedis.lpop(seId)).map(Script::new); }}
public String encrypt(String keyDiversifier, String payload) {// ...}
![Page 40: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/40.jpg)
Code. Non-Blocking. Integration
May 3, 2023 40
private final RedisAsyncCommands<String, String> commands;
public CompletionStage<Void> storeScript(String seId, Script script) { return commands .rpush(seId, script.getPayload()) .thenApply(ignore -> null);}
public CompletionStage<Long> numberOfScriptsForSe(String seId) { return commands.llen(seId);}
public CompletionStage<Optional<String>> nextScript(String seId) { return commands.lpop(seId).thenApply(Optional::ofNullable);}
public CompletionStage<String> encrypt(String keyDiversifier, String payload) {// ...}
![Page 41: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/41.jpg)
Diagnostics in non-blocking systems
No clear stack traces, need for good logs
Name your threads properly
MDC becomes useless (thread locals)
Explicitly pass debug context to trace flows
Be prepared for debuging non-obvious errors
![Page 42: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/42.jpg)
NIO technology landscapeJDK 1.4
NIOJDK 1.7NIO.2
JAX-RS 2.x
Servlet API 3.x
![Page 43: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/43.jpg)
Lessons learned, part 1
Vanila Java 8 for NIO µ-services
Netty best for custom protocols in NIO
Unit tests should be synchronous
Load/stress testing is a must
Make bulkheading and plan your resources
![Page 44: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/44.jpg)
Lessons learned, part 2
Functional programming patterns for readability
Immutability as 1-st class citizen
Scala may be good choice ;-)
![Page 45: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/45.jpg)
Conclusion
Non-blocking processing can really save your resources (== money)
But!
Weight all pros and cons and use non-blocking processing only if you really need it.
![Page 46: Practical non blocking microservices in java 8](https://reader035.fdocuments.net/reader035/viewer/2022062316/58ed2c791a28abd5108b459f/html5/thumbnails/46.jpg)
Thank you.
Michał Baliń[email protected]
Oleksandr [email protected]
goldobin
@goldobin
balonus
@MichalBalinski
Readings:• https://github.com/balonus/blocking-vs-nonblocking-demo
• C10k Problem, C10M Problem, Asynchronous I/O
• Boost application performance using asynchronous I/O (M. Tim Jones, 2006)
• Zuul 2 : The Netflix Journey to Asynchronous, Non-Blocking Systems (Netflix, 2016)
• Thousands of Threads and Blocking I/O: The Old Way to Write Java Servers Is New Again (and Way Better) (Paul Tyma, 2008)
• Why Non-Blocking? (Bozhidar Bozhanov, 2011)