Developing Reactive Microservices with Spring Data and Distributed SQL
In 2016 in the keynote presentation of Spring One Platform, Juergen Hoeller announced Spring WebFlux, one of the most highly anticipated projects being worked on by the Spring Team due to the performance gains that reactive streams promised for web controllers. Subsequently, with Spring Framework 5.0, Spring Reactive MVC went GA along with the release of WebFlux API, making the reactive stream based web controller mainstream.
Fast-forward to 2020, Spring WebFlux MVC has gained wide adoption in cloud native applications, where developers strive for high throughput and low latency microservices. Clearly there has been a shift towards the reactive programming model, now that many of the database providers support reactive drivers where traditional blocking database calls are replaced by async and non-blocking back pressure aware data access.
In this blog post, we will walk you through the basics of getting started with a fully reactive tech stack, from web controllers to database calls. We will build a Spring microservice using Spring WebFlux, Spring Data Reactive Repositories, and YugabyteDB, which supports reactive drivers for CRUD operations.
What’s YugabyteDB? It is an open source, high-performance distributed SQL database built on a scalable and fault-tolerant design inspired by Google Spanner.
The technology stack to build a Spring microservices application
By following this tutorial, we will build a Spring microservices application exposing the reactive REST API for performing CRUD operations against YugabyteDB. This sample application uses following tech stack:
- Spring WebFlux – Support for reactive MVC controllers and REST controllers.
- Spring Data reactive for Apache Cassandra – Reactive repositories support for YugabyteDB.
- Yugabyte Cloud Query Language (YCQL) – A SQL-based, flexible-schema API that supports distributed transactions, strongly consistent secondary indexes, and a native JSON column type. YCQL is compatible with the query dialect of Apache Cassandra and works natively with Apache Cassandra reactive drivers.
The Git repo for the sample application is here.
Starting YugabyteDB
Start the YugabyteDB cluster using the following command from YugabyteDB installation directory:
$ ./bin/yb-ctl destroy && ./bin/yb-ctl --rf 3 create --tserver_flags="cql_nodelist_refresh_interval_secs=10" --master_flags="tserver_unresponsive_timeout_ms=10000"
This will start a 3-node local cluster with replication factor (RF) 3. The flag cql_nodelist_refresh_interval_secs
configures how often the drivers will get notified of cluster topology changes and the following flag tserver_unresponsive_timeout_ms
is for the master to mark a node as dead after it stops responding (heartbeats) for 10 seconds.
Note: Here are the complete instructions for installing YugabyteDB on your local Mac laptop.
Project initialization and dependency configuration
Create a new Spring Boot project using Spring Initializr and add the following dependencies required for the sample application:
- Spring Reactive Web
- Spring Data Reactive for Apache Cassandra
On reviewing the pom.xml
of the project, you will see the following reactive dependencies:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-cassandra-reactive</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
Reactive repository configuration
Spring Boot auto reconfiguration initializes the Cassandra datasource on startup, all we have to do is to specify the YugabyteDB connection information in application.properties
as shown below:
spring.data.cassandra.keyspace-name=sample spring.data.cassandra.contact-points=127.0.0.1 spring.data.cassandra.port=9042
Once we have the datasource configuration applied, let’s go ahead and review the reactive repositories required for the sample application.
The sample application contains the product
domain object, for which we will be creating reactive repositories for performing CRUD operations. The domain object should have the @Table
and @Id
annotations of Spring Data Cassandra:
@Table("products") public class Product { @Id private String productId; ... }
Now let’s implement a reactive repository for the product
domain object:
public interface ProductReactiveRepository extends ReactiveCassandraRepository<Product, String> { }
The ReactiveCassandraRepository
interface enables reactive APIs for all the common operations Spring Data supports like save( )
, findById( )
, findAll()
, and deleteByID()
. As you might expect, these API calls will return reactive types, either Mono<>
or flux<>
based on the result of the API call.
Reactive REST controller and data access
As we saw from the previous section, all boilerplate code for datasource creation and implementing the data access code is provided by the Spring Framework. Let us now use the product repository in our reactive REST controller.
@RestController public class ProductController { @Autowired private ProductReactiveRepository productReactiveRepository; @GetMapping("/products") public Flux getProducts() { return productReactiveRepository.findAll(); } @PostMapping("/products/save") public Mono createProductUsingSaveAPI(@RequestBody Product product) { return productReactiveRepository.save(product); } }
That’s it! Controller methods which are using the repository APIs are also returning reactive types, which makes the overall interaction asynchronous, from serving the http request to database call, giving applications performance benefits of having a fully reactive programming model.
Summary
As cloud native applications shift towards a non-blocking programming model, application developers can quickly start prototyping using Spring abstractions for reactive programming. And coupling that with distributed cloud native databases like YugabyteDB, which support reactive drivers for data access, will result in highly scalable and performant applications.
What’s next?
- Learn more about Spring + YugabyteDB
- Join the Spring community Slack channel
- Learn more about the internals of Distributed SQL