Instead of blocking until a result is ready, you provide a callback function that is called when it completes. This frees up the thread to do something else. UI developers are really good at writing and understanding async code to prevent rendering from blocking on long API operations.
A typical async example could look something like
API Request Worker Thread Database Worker Thread
1. request XYZ comes in 1. working on handling the result from a API ABC
2. put callback into database |
worker queue |
3. thread works on next API request 2. result from database for XYZ is ready but worker
thread still working on callback for ABC
3. XYZ finished, moving on to ABC
4. use callback from ABC to finish off the API request
using result from database
Dimension | Asynchronous | Synchronous |
---|---|---|
Throughput | High | Low |
Efficiency | High | Low |
Simplicity | Low | High |
- gRPC stream observer (this experiment)
- Vert.x
- Netty
- Spring Webflux
- R2DBC
- JDK 21 Project Loom
Async callbacks are done thru the StreamObserver interface. The two most important methods are onNext and onComplete. onNext is when the next message from the other side is ready. onComplete is when the other side is done sending stuff.
Mixing async and synchronous code is challenging. If async is handled properly, a response can be returned when the server is still processing the request! You might have an HTTP Server like spring web that responses to requests asynchronously, but invoking async operations against gRPC services or databases.
This experiment demonstrates this problem.
Terminal 1:
java -cp .\target\java-fx-async-grpc-1-shaded.jar ServerMain
> Server running on port: 64610
>
Terminal 2:
java -cp .\target\java-fx-async-grpc-1-shaded.jar ClientMain --method AllSynchronous --port 64610
java -cp .\target\java-fx-async-grpc-1-shaded.jar ClientMain --method OnlyResponseAsync --port 64610
java -cp .\target\java-fx-async-grpc-1-shaded.jar ClientMain --method OnlyResponseAsyncBroken --port 64610
java -cp .\target\java-fx-async-grpc-1-shaded.jar ClientMain --method BidirectionalAsync --port 64610
How can you send gigantic response that consumes too much memory using a stream response?
How would you use bi-direction gRPC to implement a chat server?
Solution:
Chat Server:
java -cp .\target\java-fx-async-grpc-1-shaded.jar ChatServerMain
Client 1:
java -cp .\target\java-fx-async-grpc-1-shaded.jar ChatClientMain --port 53215
Client 2:
java -cp .\target\java-fx-async-grpc-1-shaded.jar ChatClientMain --port 53215