-
Notifications
You must be signed in to change notification settings - Fork 335
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into conversations
Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
- Loading branch information
Showing
39 changed files
with
3,659 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
type: docs | ||
title: "Dapr Jobs .NET SDK" | ||
linkTitle: "Jobs" | ||
weight: 50000 | ||
description: Get up and running with Dapr Jobs and the Dapr .NET SDK | ||
--- | ||
|
288 changes: 288 additions & 0 deletions
288
daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
--- | ||
type: docs | ||
title: "How to: Author and manage Dapr Jobs in the .NET SDK" | ||
linkTitle: "How to: Author & manage jobs" | ||
weight: 10000 | ||
description: Learn how to author and manage Dapr Jobs using the .NET SDK | ||
--- | ||
|
||
Let's create an endpoint that will be invoked by Dapr Jobs when it triggers, then schedule the job in the same app. We'll use the [simple example provided here](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs), for the following demonstration and walk through it as an explainer of how you can schedule one-time or recurring jobs using either an interval or Cron expression yourself. In this guide, | ||
you will: | ||
|
||
- Deploy a .NET Web API application ([JobsSample](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs/JobsSample)) | ||
- Utilize the .NET Jobs SDK to schedule a job invocation and set up the endpoint to be triggered | ||
|
||
In the .NET example project: | ||
- The main [`Program.cs`](https://github.com/dapr/dotnet-sdk/tree/master/examples/Jobs/JobsSample/Program.cs) file comprises the entirety of this demonstration. | ||
|
||
## Prerequisites | ||
- [.NET 6+](https://dotnet.microsoft.com/download) installed | ||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) | ||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost) | ||
- [Dapr Jobs .NET SDK](https://github.com/dapr/dotnet-sdk) | ||
|
||
## Set up the environment | ||
Clone the [.NET SDK repo](https://github.com/dapr/dotnet-sdk). | ||
|
||
```sh | ||
git clone https://github.com/dapr/dotnet-sdk.git | ||
``` | ||
|
||
From the .NET SDK root directory, navigate to the Dapr Jobs example. | ||
|
||
```sh | ||
cd examples/Jobs | ||
``` | ||
|
||
## Run the application locally | ||
|
||
To run the Dapr application, you need to start the .NET program and a Dapr sidecar. Navigate to the `JobsSample` directory. | ||
|
||
```sh | ||
cd JobsSample | ||
``` | ||
|
||
We'll run a command that starts both the Dapr sidecar and the .NET program at the same time. | ||
|
||
```sh | ||
dapr run --app-id jobsapp --dapr-grpc-port 4001 --dapr-http-port 3500 -- dotnet run | ||
``` | ||
> Dapr listens for HTTP requests at `http://localhost:3500` and internal Jobs gRPC requests at `http://localhost:4001`. | ||
## Register the Dapr Jobs client with dependency injection | ||
The Dapr Jobs SDK provides an extension method to simplify the registration of the Dapr Jobs client. Before completing the dependency injection registration in `Program.cs`, add the following line: | ||
|
||
```cs | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
//Add anywhere between these two | ||
builder.Services.AddDaprJobsClient(); //That's it | ||
var app = builder.Build(); | ||
``` | ||
|
||
> Note that in today's implementation of the Jobs API, the app that schedules the job will also be the app that receives the trigger notification. In other words, you cannot schedule a trigger to run in another application. As a result, while you don't explicitly need the Dapr Jobs client to be registered in your application to schedule a trigger invocation endpoint, your endpoint will never be invoked without the same app also scheduling the job somehow (whether via this Dapr Jobs .NET SDK or an HTTP call to the sidecar). | ||
It's possible that you may want to provide some configuration options to the Dapr Jobs client that | ||
should be present with each call to the sidecar such as a Dapr API token or you want to use a non-standard | ||
HTTP or gRPC endpoint. This is possible through an overload of the register method that allows configuration of a `DaprJobsClientBuilder` instance: | ||
|
||
```cs | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Services.AddDaprJobsClient(daprJobsClientBuilder => | ||
{ | ||
daprJobsClientBuilder.UseDaprApiToken("abc123"); | ||
daprJobsClientBuilder.UseHttpEndpoint("http://localhost:8512"); //Non-standard sidecar HTTP endpoint | ||
}); | ||
|
||
var app = builder.Build(); | ||
``` | ||
|
||
Still, it's possible that whatever values you wish to inject need to be retrieved from some other source, itself registered as a dependency. There's one more overload you can use to inject an `IServiceProvider` into the configuration action method. In the following example, we register a fictional singleton that can retrieve secrets from somewhere and pass it into the configuration method for `AddDaprJobClient` so | ||
we can retrieve our Dapr API token from somewhere else for registration here: | ||
|
||
```cs | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Services.AddSingleton<SecretRetriever>(); | ||
builder.Services.AddDaprJobsClient((serviceProvider, daprJobsClientBuilder) => | ||
{ | ||
var secretRetriever = serviceProvider.GetRequiredService<SecretRetriever>(); | ||
var daprApiToken = secretRetriever.GetSecret("DaprApiToken").Value; | ||
daprJobsClientBuilder.UseDaprApiToken(daprApiToken); | ||
|
||
daprJobsClientBuilder.UseHttpEndpoint("http://localhost:8512"); | ||
}); | ||
|
||
var app = builder.Build(); | ||
``` | ||
|
||
## Use the Dapr Jobs client without relying on dependency injection | ||
While the use of dependency injection simplifies the use of complex types in .NET and makes it easier to | ||
deal with complicated configurations, you're not required to register the `DaprJobsClient` in this way. Rather, you can also elect to create an instance of it from a `DaprJobsClientBuilder` instance as demonstrated below: | ||
|
||
```cs | ||
|
||
public class MySampleClass | ||
{ | ||
public void DoSomething() | ||
{ | ||
var daprJobsClientBuilder = new DaprJobsClientBuilder(); | ||
var daprJobsClient = daprJobsClientBuilder.Build(); | ||
|
||
//Do something with the `daprJobsClient` | ||
} | ||
} | ||
|
||
``` | ||
|
||
## Set up a endpoint to be invoked when the job is triggered | ||
|
||
It's easy to set up a jobs endpoint if you're at all familiar with [minimal APIs in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview) as the syntax is the same between the two. | ||
|
||
Once dependency injection registration has been completed, configure the application the same way you would to handle mapping an HTTP request via the minimal API functionality in ASP.NET Core. Implemented as an extension method, | ||
pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and you can optionally pass a `JobDetails` to get information about the job that has been triggered (e.g. access its scheduling setup or payload). | ||
|
||
There are two delegates you can use here. One provides an `IServiceProvider` in case you need to inject other services into the handler: | ||
|
||
```cs | ||
//We have this from the example above | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Services.AddDaprJobsClient(); | ||
|
||
var app = builder.Build(); | ||
|
||
//Add our endpoint registration | ||
app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string? jobName, JobDetails? jobDetails) => { | ||
var logger = serviceProvider.GetService<ILogger>(); | ||
logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob"); | ||
|
||
//Do something... | ||
}); | ||
|
||
app.Run(); | ||
``` | ||
|
||
The other overload of the delegate doesn't require an `IServiceProvider` if not necessary: | ||
|
||
```cs | ||
//We have this from the example above | ||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
builder.Services.AddDaprJobsClient(); | ||
|
||
var app = builder.Build(); | ||
|
||
//Add our endpoint registration | ||
app.MapDaprScheduledJob("myJob", (string? jobName, JobDetails? jobDetails) => { | ||
//Do something... | ||
}); | ||
|
||
app.Run(); | ||
``` | ||
|
||
## Register the job | ||
|
||
Finally, we have to register the job we want scheduled. Note that from here, all SDK methods have cancellation token support and use a default token if not otherwise set. | ||
|
||
There are three different ways to set up a job that vary based on how you want to configure the schedule: | ||
|
||
### One-time job | ||
A one-time job is exactly that; it will run at a single point in time and will not repeat. This approach requires that you select a job name and specify a time it should be triggered. | ||
|
||
| Argument Name | Type | Description | Required | | ||
|---|---|---|---| | ||
| jobName | string | The name of the job being scheduled. | Yes | | ||
| scheduledTime | DateTime | The point in time when the job should be run. | Yes | | ||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No | | ||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No | | ||
|
||
One-time jobs can be scheduled from the Dapr Jobs client as in the following example: | ||
|
||
```cs | ||
public class MyOperation(DaprJobsClient daprJobsClient) | ||
{ | ||
public async Task ScheduleOneTimeJobAsync(CancellationToken cancellationToken) | ||
{ | ||
var today = DateTime.UtcNow; | ||
var threeDaysFromNow = today.AddDays(3); | ||
|
||
await daprJobsClient.ScheduleOneTimeJobAsync("myJobName", threeDaysFromNow, cancellationToken: cancellationToken); | ||
} | ||
} | ||
``` | ||
|
||
### Interval-based job | ||
An interval-based job is one that runs on a recurring loop configured as a fixed amount of time, not unlike how [reminders](https://docs.dapr.io/developing-applications/building-blocks/actors/actors-timers-reminders/#actor-reminders) work in the Actors building block today. These jobs can be scheduled with a number of optional arguments as well: | ||
|
||
| Argument Name | Type | Description | Required | | ||
|---|---|---|---| | ||
| jobName | string | The name of the job being scheduled. | Yes | | ||
| interval | TimeSpan | The interval at which the job should be triggered. | Yes | | ||
| startingFrom | DateTime | The point in time from which the job schedule should start. | No | | ||
| repeats | int | The maximum number of times the job should be triggered. | No | | ||
| ttl | When the job should expires and no longer trigger. | No | | ||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No | | ||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No | | ||
|
||
Interval-based jobs can be scheduled from the Dapr Jobs client as in the following example: | ||
|
||
```cs | ||
public class MyOperation(DaprJobsClient daprJobsClient) | ||
{ | ||
|
||
public async Task ScheduleIntervalJobAsync(CancellationToken cancellationToken) | ||
{ | ||
var hourlyInterval = TimeSpan.FromHours(1); | ||
|
||
//Trigger the job hourly, but a maximum of 5 times | ||
await daprJobsClient.ScheduleIntervalJobAsync("myJobName", hourlyInterval, repeats: 5), cancellationToken: cancellationToken; | ||
} | ||
} | ||
``` | ||
|
||
### Cron-based job | ||
A Cron-based job is scheduled using a Cron expression. This gives more calendar-based control over when the job is triggered as it can used calendar-based values in the expression. Like the other options, these jobs can be scheduled with a number of optional arguments as well: | ||
|
||
| Argument Name | Type | Description | Required | | ||
|---|---|---|---| | ||
| jobName | string | The name of the job being scheduled. | Yes | | ||
| cronExpression | string | The systemd Cron-like expression indicating when the job should be triggered. | Yes | | ||
| startingFrom | DateTime | The point in time from which the job schedule should start. | No | | ||
| repeats | int | The maximum number of times the job should be triggered. | No | | ||
| ttl | When the job should expires and no longer trigger. | No | | ||
| payload | ReadOnlyMemory<byte> | Job data provided to the invocation endpoint when triggered. | No | | ||
| cancellationToken | CancellationToken | Used to cancel out of the operation early, e.g. because of an operation timeout. | No | | ||
|
||
A Cron-based job can be scheduled from the Dapr Jobs client as follows: | ||
|
||
```cs | ||
public class MyOperation(DaprJobsClient daprJobsClient) | ||
{ | ||
public async Task ScheduleCronJobAsync(CancellationToken cancellationToken) | ||
{ | ||
//At the top of every other hour on the fifth day of the month | ||
const string cronSchedule = "0 */2 5 * *"; | ||
|
||
//Don't start this until next month | ||
var now = DateTime.UtcNow; | ||
var oneMonthFromNow = now.AddMonths(1); | ||
var firstOfNextMonth = new DateTime(oneMonthFromNow.Year, oneMonthFromNow.Month, 1, 0, 0, 0); | ||
|
||
//Trigger the job hourly, but a maximum of 5 times | ||
await daprJobsClient.ScheduleCronJobAsync("myJobName", cronSchedule, dueTime: firstOfNextMonth, cancellationToken: cancellationToken); | ||
} | ||
} | ||
``` | ||
|
||
## Get details of already-scheduled job | ||
If you know the name of an already-scheduled job, you can retrieve its metadata without waiting for it to | ||
be triggered. The returned `JobDetails` exposes a few helpful properties for consuming the information from the Dapr Jobs API: | ||
|
||
- If the `Schedule` property contains a Cron expression, the `IsCronExpression` property will be true and the expression will also be available in the `CronExpression` property. | ||
- If the `Schedule` property contains a duration value, the `IsIntervalExpression` property will instead be true and the value will be converted to a `TimeSpan` value accessible from the `Interval` property. | ||
|
||
This can be done by using the following: | ||
|
||
```cs | ||
public class MyOperation(DaprJobsClient daprJobsClient) | ||
{ | ||
public async Task<JobDetails> GetJobDetailsAsync(string jobName, CancellationToken cancellationToken) | ||
{ | ||
var jobDetails = await daprJobsClient.GetJobAsync(jobName, canecllationToken); | ||
return jobDetails; | ||
} | ||
} | ||
``` | ||
|
||
## Delete a scheduled job | ||
To delete a scheduled job, you'll need to know its name. From there, it's as simple as calling the `DeleteJobAsync` method on the Dapr Jobs client: | ||
|
||
```cs | ||
public class MyOperation(DaprJobsClient daprJobsClient) | ||
{ | ||
public async Task DeleteJobAsync(string jobName, CancellationToken cancellationToken) | ||
{ | ||
await daprJobsClient.DeleteJobAsync(jobName, cancellationToken); | ||
} | ||
} | ||
``` |
Oops, something went wrong.