Generic Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this article, we'll start with a Spring Cloud Gateway application and a Spring Boot application. Then, we'll update it to use Dapr (Distributed Application Runtime) instead. Finally, we'll update the Dapr configuration to show the flexibility that Dapr provides when integrating with cloud-native components.

2. Intro to Dapr

With Dapr, we can manage the deployment of a cloud-native application without any impact on the application itself. Dapr uses the sidecar pattern to off-load deployment concerns from the application, which allows us to deploy it into other environments (such as on-premise, different proprietary Cloud platforms, Kubernetes, and others) without any changes to the application itself. For more details, check out this overview on the Dapr website.

3. Create Sample Applications

We'll start by creating a sample Spring Cloud Gateway and Spring Boot application. In the great tradition of “Hello world” examples, the gateway will proxy requests to a back-end Spring Boot application for the standard “Hello world” greeting.

3.1. Greeting Service

First, let's create a Spring Boot application for the greeting service. This is a standard Spring Boot application with spring-boot-starter-web as the only dependency, the standard main class, and the server port configured as 3001.

Let's add a controller to respond to the hello endpoint:

@RestController
public class GreetingController {
    @GetMapping(value = "/hello")
    public String getHello() {
        return "Hello world!";
    }
}

After building our greeting service app, we'll start it up:

java -jar greeting/target/greeting-1.0-SNAPSHOT.jar

We can test it out using curl to return the “Hello world!” message:

curl http://localhost:3001/hello

3.2. Spring Cloud Gateway

Now, we'll create a Spring Cloud Gateway on port 3000 as a standard Spring Boot application with spring-cloud-starter-gateway as the only dependency and the standard main class. We'll also configure the routing to access the greeting service:

spring:
  cloud:
    gateway:
      routes:
        - id: greeting-service
          uri: http://localhost:3001/
          predicates:
            - Path=/**
          filters:
          - RewritePath=/?(?<segment>.*), /$\{segment}

Once we build the gateway app, we can start the gateway:

java -Dspring.profiles.active=no-dapr -jar gateway/target/gateway-1.0-SNAPSHOT.jar

We can test it out using curl to return the “Hello world!” message from the greeting service:

curl http://localhost:3000/hello

4. Add Dapr

Now that we have a basic example in place, let’s add Dapr to the mix.

We do this by configuring the gateway to communicate with the Dapr sidecar instead of directly with the greeting service. Dapr will then be responsible for finding the greeting service and forwarding the request to it; the communication path will now be from the gateway, through the Dapr sidecars, and to the greeting service.

4.1. Deploy Dapr Sidecars

First, we need to deploy two instances of the Dapr sidecar – one for the gateway and one for the greeting service. We do this using the Dapr CLI.

We'll use a standard Dapr configuration file:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec: {}

Let's start the Dapr sidecar for the gateway on port 4000 using the dapr command:

dapr run --app-id gateway --dapr-http-port 4000 --app-port 3000 --config dapr-config/basic-config.yaml

Next, let's start the Dapr sidecar for the greeting service on port 4001 using the dapr command:

dapr run --app-id greeting --dapr-http-port 4001 --app-port 3001 --config dapr-config/basic-config.yaml

Now that the sidecars are running, we can see how they take care of intercepting and forwarding requests to the greeting service. When we test it out using curl, it should return the “Hello world!” greeting:

curl http://localhost:4001/v1.0/invoke/greeting/method/hello

Let's try the same test using the gateway sidecar to confirm that it also returns the “Hello world!” greeting:

curl http://localhost:4000/v1.0/invoke/greeting/method/hello

What's going on here behind the scenes? The Dapr sidecar for the gateway uses service discovery (in this case, mDNS for a local environment) to find the Dapr sidecar for the greeting service. Then, it uses Service Invocation to call the specified endpoint on the greeting service.

4.2. Update Gateway Configuration

The next step is to configure the gateway routing to use its Dapr sidecar instead:

spring:
  cloud:
    gateway:
      routes:
        - id: greeting-service
          uri: http://localhost:4000/
          predicates:
            - Path=/**
          filters:
          - RewritePath=//?(?<segment>.*), /v1.0/invoke/greeting/method/$\{segment}

Then, we'll restart the gateway with the updated routing:

java -Dspring.profiles.active=with-dapr -jar gateway/target/gateway-1.0-SNAPSHOT.jar

We can test it out using the curl command to once again get the “Hello world” greeting from the greeting service:

curl http://localhost:3000/hello

When we look at what's happening on the network using Wireshark, we can see that the traffic between the gateway and the service goes through the Dapr sidecars.

Congratulations! We have now successfully brought Dapr into the picture. Let's review what this has gained us: The gateway no longer needs to be configured to find the greeting service (that is, the port number for the greeting service no longer needs to be specified in the routing configuration), and the gateway no longer needs to know the details of how the request is forwarded to the greeting service.

5. Update Dapr Configuration

Now that we have Dapr in place, we can configure Dapr to use other cloud-native components instead.

5.1. Use Consul for Service Discovery

Let's use Consul for Service Discovery instead of mDNS.

First, we need to install and start Consul on the default port of 8500, and then update the Dapr sidecar configuration to use Consul:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  nameResolution:
    component: "consul"
    configuration:
      selfRegister: true

Then we'll restart both Dapr sidecars with the new configuration:

dapr run --app-id greeting --dapr-http-port 4001 --app-port 3001 --config dapr-config/consul-config.yaml
dapr run --app-id gateway --dapr-http-port 4000 --app-port 3000 --config dapr-config/consul-config.yaml

Once the sidecars are restarted, we can access the Services page in the consul UI and see the gateway and greeting apps listed. Notice that we did not need to restart the application itself.

See how easy that was? A simple configuration change for the Dapr sidecars now gives us support for Consul and, most importantly, with no impact on the underlying application. This differs from using Spring Cloud Consul, which requires updating the application itself.

5.2. Use Zipkin for Tracing

Dapr also supports integration with Zipkin for tracing calls across applications.

First, install and start up Zipkin on the default port of 9411, and then update the configuration for the Dapr sidecar to add Zipkin:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  nameResolution:
    component: "consul"
    configuration:
      selfRegister: true
  tracing:
    samplingRate: "1"
    zipkin:
      endpointAddress: "http://localhost:9411/api/v2/spans"

We'll need to restart both Dapr sidecars to pick up the new configuration:

dapr run --app-id greeting --dapr-http-port 4001 --app-port 3001 --config dapr-config/consul-zipkin-config.yaml
dapr run --app-id gateway --dapr-http-port 4000 --app-port 3000 --config dapr-config/consul-zipkin-config.yaml

Once Dapr is restarted, you can issue a curl command and check out the Zipkin UI to see the call trace.

Once again, there's no need to restart the gateway and greeting service. It requires only an easy update to the Dapr configuration. Compare this to using Spring Cloud Zipkin instead.

5.3. Other Components

There are many components that Dapr supports to address other concerns such as security, monitoring, and reporting. Check out the Dapr documentation for a full list.

6. Conclusion

We have added Dapr to a simple example of a Spring Cloud Gateway communicating with a back-end Spring Boot service. We've shown how to configure and start the Dapr sidecar and how it then takes care of cloud-native concerns such as service discovery, communication, and tracing.

Although this comes at the cost of deploying and managing a sidecar application, Dapr provides flexibility for deployment into different cloud-native environments and cloud-native concerns without requiring changes to the applications once the integration with Dapr is in place.

This approach also means that developers don't need to be encumbered with cloud-native concerns as they are writing the code, which frees them up to focus on business functionality. Once the application is configured to use the Dapr sidecar, different deployment concerns can be addressed without any impact on the application – no need for re-coding, re-building, or re-deploying applications. Dapr provides a clean separation between application and deployment concerns.

As always, the complete code for this article can be found over on GitHub.

Generic bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Cloud footer banner
Comments are closed on this article!