Reactive Systems are designed
to address challenges posed by modern software systems - the challenges related
to large number of users and high throughput. Reactive systems are expected to
be highly responsive, resilient, elastic and message driven.
In this article we will:
- Build a set of fully non-blocking REST API using SpringBoot 2.0, WebFlux and Reactive Redis.
- Performance test the above Reactive APIs against the traditional non-reactive APIs.
The code used in this example can be downloaded from GitHub
Step 1: Create a skeleton Reactive WebFlux SpringBoot project
Add the following dependencies:
- spring-boot-starter-web
- spring-boot-starter-data-redis
- spring-webflux
- spring-boot-starter-data-redis-reactive
Refer to the dependencies in pom.xml
Step 2: Create Domain Objects
The demo project uses the domain objects Customer and Account. A customer can have
multiple accounts.
Step 3: Create non-blocking reactive REST APIs using WebFlux
Create a REST controller CustomerControllerRx for the purpose of serving the following
reactive no-blocking APIs.
- Add/update a Customer
- findById a Customer
The code snippet use Mono which is an implementation of
Reactive streams Publisher interface and ReactiveRedisTemplate and ReactiveValueOperations to interact
with Redis in a non-blocking way.
@RestController
@RequestMapping("/customers/rx")
public class CustomerControllerRx {
@Autowired
private ReactiveRedisTemplate redisTemplate;
private ReactiveValueOperations reactiveValueOps;
@PostMapping
public Mono add( @RequestBody Customer customer) {
reactiveValueOps
= redisTemplate.opsForValue();
Mono
result = reactiveValueOps.set(customer.getExternalId(), customer);
return result;
}
@GetMapping("/{id}")
public Mono findById( @PathVariable("id") String id) {
reactiveValueOps =
redisTemplate.opsForValue();
Mono
fetchedAccount = reactiveValueOps.get(id);
return fetchedAccount;
}
}
ReactiveRedisTemplate is configured in RedisConfigRx
Step 4: Create synchronous (blocking) REST API.
Create a REST controller CustomerController for the purpose of serving
non-reactive blocking APIs. We are using CustomerRepository which extends a CurdRepository to
interact with the Redis database.
@RestController
@RequestMapping("/customers")
public class CustomerController {
@Autowired
CustomerRepository repository;
@PostMapping
public Customer add(@RequestBody Customer customer) {
return repository.save(customer);
}
@GetMapping("/{externalId}")
public Customer findById(@PathVariable("externalId") String externalId) {
Customer optCustomer = repository.findByExternalId(externalId);
if (Optional.ofNullable(optCustomer).isPresent())
return optCustomer;
else{
return null;
}
}
}
// Code for
CustomerRepository
public interface CustomerRepository extends CrudRepository {
Customer findByExternalId(String externalId);
List findByAccountsId(Long id);
}
Step 5: Connecting to Redis using Docker.
· Redis doesn’t officially support Windows. However,
the easiest way to get Redis up and running for UNIX or Windows is by using Docker.
· Use the following
steps to pull a redis image from docker hub and to start on port 6379 in
detached mode.
$ docker pull redis
$
docker run -d -p 6379:6379 --name redis1 redis
$
docker ps -a //
make sure redis is up and running.
· Enter the Docker
shell for Redis
$docker exec -it redis1 sh
$redis-cli // enter into CLI mode
$keys * // list of keys
You may use the $docker-machine ip command
to find the IP address of your docker machine, where the Redis database is
running.
Step 6: Set up JMeter for testing
o
Basic Graphs
o
Additional Graphs
The above plugins are zip files and can be extracted to the
lib folder of the JMeter installation folder. Once the plugins are installed,
JMeter can be started from the bin folder.
The next step is to create Test Plans for the APIs that are
required to be benchmarked. I have the following Test Plans for the
APIs.
GetCustomers.jmx
|
To performance
test non-reactive CustomerController : findById () method.
Get
Mapping
|
SaveCustomers.jmx
|
To performance
test non-reactive CustomerController : add () method.
PostMapping
|
GetCustomersRx.jmx
|
To performance
test reactive CustomerControllerRx : findById () method.
Get
Mapping
|
SaveCustomersRx.jmx
|
To performance
test reactive CustomerControllerRx : Add () method.
PostMapping
|
The above Test Plans can be opened in JMeter and configured
for different number of concurrent users – E.g.
5, 50, 100, 400, 500 and so on. Once its configured, JMeter test cases can
be fired against the application in non-UI mode as below.
jmeter -n -t <TestPLan.jmx>
-l <TestPlan.jtl> -e -o
Where:
-n à run in non-GUI mode
-t à
provide the name of the test file
-l à
name of the output report file
-e à
jMeter to follow post processing specified in jmx file.
-o à
dashboard folder.
Step 6: Benchmark reactive REST APIs vs blocking REST APIs
Start SpringRedisReactiveApplication
a)
Make sure the application starts without errors by
connecting to the Redis DB on docker.
b)
Set no of users (threads) and loops (iterations) for
the Test Plans.
Open the TestPlan using JMeter UI
and change the number of users (threads) and set the number of loops. Save the
test plan. Exit JMeter UI.
c)
Execute Test Plans
Go to JMeter\bin folder and
execute:
jmeter -n -t
\SaveCustomers.jmx
-l \SaveCustomers.jtl
-e -o \SaveCustomersOutput-5Users
The above command will run SaveCustomers.jmx
TestCases, creates a reporting folder named SaveCustomersOutput
d)
Repeat step 6 (b) and 6 (c) for the other test
plans also, every time changing the output folder name.
· SaveCustomersRx.jmx
· GetCustomers.jmx
· SaveCustomersRx
e)
Repeat step 6 (b), (c) and (d) for 10, 50, 100,
200 and 400 users.
Comments