ServiceTalk is a JVM network application framework with APIs tailored to specific protocols (e.g. HTTP/1.x, HTTP/2.x, etc…) and supports multiple Programming Paradigms.
It is built on Netty and is designed to provide most of the performance/scalability benefits of Netty for common networking protocols used in service to service communication. ServiceTalk provides server support and "smart client" like features such as client-side load balancing and service discovery integration.
ServiceTalk is intended to provide a common and extensible networking abstraction on top of a lower-level networking framework (e.g. Netty). Netty is a great low-level networking framework, but from the perspective of application developers who need service to service communication it presents a few challenges:
-
Threading Model
-
fully asynchronous and requires knowledge of EventLoop threading model
-
back-pressure requires manual association of source of data and sink of data
-
executing CPU intensive or "blocking" code requires manual thread hops
-
ordering issues when code executes both on and off the EventLoop thread
-
-
Usability
-
APIs not targeted toward specific protocols
-
Asynchronous programming paradigm presents a barrier to entry in scenarios where it isn’t currently required for scalability
-
Error propagation follows multiple paths depending on the event and state of Channel
-
-
Lacking Feature Set
-
Smart Client (e.g. client-side load balancing, service discovery, retry) features missing
-
ServiceTalk addresses these challenges and provides a framework that supports multiple Programming Paradigms. ServiceTalk accomplishes this by building on a fully asynchronous non-blocking I/O core and taking care of the threading model complexities internally.
When developing a new application it may not be clear if the complexity of asynchronous control flow is justified. Initially the scale may be relatively small, but over time the scale may grow. The scaling or response size characteristics may not be uniform for all APIs offered by the application (e.g. health check vs file serving). ServiceTalk is designed to evolve with your application so that you can get started quickly and avoid/defer the complexity of asynchronous control flow in these cases. This can dramatically lower the bar to entry for ServiceTalk compared with most non-blocking I/O frameworks and avoid "application re-write" if scaling/data size characteristics change over time.
ServiceTalk APIs may use the term "blocking" in areas where the APIs may be identified as "synchronous". "blocking" in this context is meant to declare that the API "may block" the calling thread. This is done because there is no general way to determine if a method will return synchronously or block the calling thread, and "blocking" is the least common denominator.
Blocking and Aggregated
This API paradigm is similar to concepts from java.io and generally blocks the calling thread until all I/O is
completed. The result is aggregated into a single object (e.g.
Files.readAllLines
).
Blocking and Streaming
This API paradigm is similar to concepts from java.io and generally blocks the calling thread until I/O is
flushed/received. The result can be provided/processed in a streaming fashion (e.g.
InputStream or
OutputStream) however processing each chunk of
the stream may also block the calling thread.
This API paradigm performs I/O asynchronously (e.g. the calling thread is not blocked) and the user is notified when all
the I/O is complete. ServiceTalk provides ReactiveStreams compatible
Asynchronous Primitives but using this API is similar to using a Future/Promise based
API (e.g.
CompletionStage and
CompletableFuture).
This API paradigm performs I/O asynchronously (e.g. the calling thread is not blocked) and the user can provide/process
the I/O in chunks (as opposed to in a single Object). ServiceTalk provides
ReactiveStreams compatible Asynchronous Primitives to enable this AP
paradigm.
ServiceTalk follows SemVer 2.0.0. API/ABI breaking changes will
require package renaming for that module to avoid runtime classpath conflicts. Note that 0.x.y releases are not stable
and are permitted to break API/ABI.
ServiceTalk provides ReactiveStreams compatible Asynchronous Primitives.
Note that all asynchronous primitives are "lazy"/"cold" in that the action they represent does not start until someone is "listening" for the results. This is different from "eager"/"hot" CompletableFuture usages in that the work being done to complete the CompletableFuture is already happening regardless if anyone is "listening" for the results.
A Publisher represents an
asynchronous stream of data. There is a
PublisherSource.Subscriber<T>
that subscribes and
requests more data via a
PublisherSource.Subscription.
Users are generally not expected to subscribe to a PublisherSource, or even deal directly with a PublisherSource.
Instead, most users are expected to use the
Publisher API which provides
many operators to define asynchronous and streaming control flow.
For more details on the API contract please review the ReactiveStreams Specification.
A Single shares all the same semantics as Publisher, but it either terminates with a single value, or an error.
Users are generally not expected to subscribe to a SingleSource, or even deal directly with a SingleSource.
Instead, most users are expected to use the
Single API which provides many
operators to define asynchronous and streaming control flow.
A Completable shares all the same semantics as Publisher, but it either terminates successfully, or with an error.
Users are generally not expected to subscribe to a CompletableSource, or even deal directly with a
CompletableSource. Instead, most users are expected to use the
Completable API which
provides many operators to define asynchronous and streaming control flow.
ServiceTalk is designed to provide an extensible core and APIs tailored to networking protocols. ServiceTalk does not intend to provide abstractions for low-level networking primitives (e.g. Channels, EventLoop, TLS, etc…) but instead uses these primitives to provide a higher level API in numerous Programming Paradigms.
The project is divided into many modules to decouple the user-facing API from implementation details. This gives users freedom to choose only the functionality they need, and also allows us to evolve each module independently. Note that these modules may be divided out into independent repositories to decouple from the core and enable independent versioning.