Scheduled tasks with the Cron binding in Dapr
I wrote recently about the benefits of adopting Dapr in a microservices application. In that post I focused on the "service invocation" building block. In this post, I want to highlight a particularly useful capability that is exposed by the "bindings" building block.
Dapr bindings
The concept of "bindings" in Dapr will be familiar to anyone who has worked with Azure Functions. They expose a simplified way of interacting with a wide variety of third party services.
Bindings can be "input" or "output". An input binding (also called a "trigger") allows Dapr to subscribe to events in external systems and call an endpoint on your service for you to let you know what has happened. Good examples in Azure would be subscribing to events on Event Grid or messages on Service Bus. But there are many supported bindings, including things like Twitter so you can get notified whenever something is tweeted matching your search criteria.
An output binding allows you to send data to an external service. In Azure this might be posting a message to a queue, writing a document to Cosmos DB. Or you could use it to send an SMS with Twilio.
Bindings benefits and weaknesses
One strength of bindings is that they can greatly simplify your application code as they remove a lot of the cumbersome boilerplate typically required to connect to the service.
Another advantage is that they provide a certain level of abstraction. Whilst some bindings can't be swapped out with other alternatives due to the service-specific nature of the data they deal with, the ability to swap out components has potential to be very useful in dev/test environments, where you may not want or need to actually communicate with a real service.
The key weakness of bindings is that they usually expose a fairly limited subset of capabilities of the underlying platform. So if you are a power user, then you may prefer to just use the SDK of the service directly. And of course Dapr doesn't prevent you from doing that - bindings are completely optional.
The cron binding
The binding I want to focus on particularly is a bit of a special case. It's the "cron" binding. Rather than supporting connection to an external system, this makes it easy to set up scheduled tasks.
To set this up, you need to define a component YAML file. You can just copy an example, and customise the schedule
to meet your needs. This supports a regular cron syntax and some simplified shortcuts like @every 5m
for every five minutes as shown below.
The only 'advanced' thing I've done is limited this component to only apply to a single Dapr service by using the scopes
property - in this example the catalog
service.
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: scheduled
namespace: default
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 5m"
scopes:
- catalog
Now all we need to do is listen on an endpoint that matches the name of our component. In this example it's called scheduled
. Note that this will be made as a HTTP POST
request, so in the example below I'm showing how a simple Node.js Express application can receive calls on the /scheduled
endpoint and write a message to the console.
app.post('/scheduled', async function(req, res){
console.log("scheduled endpoint called", req.body)
res.status(200).send()
});
If we run this, we'll see that the /scheduled
endpoint is called every five minutes by the Dapr sidecar.
And that's all there is to it. Of course, Dapr doesn't force you to use any of its building blocks, so if you already have a solution for scheduled tasks, then feel free to keep using it. But if not, it's great that Dapr provides such a simple to use option out of the box.