Skip to content

Commit 5e1fa76

Browse files
ReubenBondgithub-actions[bot]IEvangelist
authored
Use Orleans with .NET Aspire (#840)
* Add Aspire + Orleans docs * Update orleans.md * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Update docs/fundamentals/orleans.md Co-authored-by: David Pine <david.pine@microsoft.com> * Use code snippets. Overhaul * Try to fix highlighting * Use capitalization on folder name to make the linter be quiet * Fix linter warnings * Update docs/frameworks/orleans.md * Update docs/frameworks/orleans.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: David Pine <david.pine@microsoft.com>
1 parent 26ec112 commit 5e1fa76

23 files changed

+581
-52
lines changed

docs/frameworks/orleans.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
---
2+
title: Use Orleans with .NET Aspire
3+
description: Learn how to use Orleans with .NET Aspire
4+
ms.date: 05/3/2024
5+
ms.topic: overview
6+
---
7+
8+
# Use Orleans with .NET Aspire
9+
10+
Orleans and .NET Aspire are built for each other. .NET Aspire's application model lets you describe the services, databases, and other resources/infrastructure in your app and how they relate. Orleans' provides a straightforward way to build distributed applications which are elastically scalable and fault-tolerant. .NET Aspire is used to configure and orchestrate Orleans and its dependencies, such as, by providing Orleans with database cluster membership and storage.
11+
12+
Orleans is represented as a resource in .NET Aspire. The Orleans resource includes configuration which your service needs to operate, such as cluster membership providers and storage providers.
13+
14+
## Prerequisites
15+
16+
- .NET 8.0 SDK or later
17+
- .NET Aspire workload
18+
- Orleans version 8.1.0 or later
19+
20+
For more information, see [.NET Aspire setup and tooling](../fundamentals/setup-tooling.md).
21+
22+
## Get started
23+
24+
To get started you need to add the Orleans hosting package to your app host project by installing the [Aspire.Hosting.Orleans](https://www.nuget.org/packages/Aspire.Hosting.Orleans) NuGet package.
25+
26+
### [.NET CLI](#tab/dotnet-cli)
27+
28+
```dotnetcli
29+
dotnet add package Aspire.Hosting.Orleans --prerelease
30+
```
31+
32+
### [PackageReference](#tab/package-reference)
33+
34+
```xml
35+
<PackageReference Include="Aspire.Hosting.Orleans"
36+
Version="[SelectVersion]" />
37+
```
38+
39+
---
40+
41+
For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies).
42+
43+
The Orleans resource is added to the .NET Aspire distributed application builder using the `AddOrleans(string name)` method, which returns an Orleans resource builder.
44+
The name provided to the Orleans resource is for diagnostic purposes. For most applications, a value of `"default"` will suffice.
45+
46+
:::code language="csharp" source="snippets/Orleans/OrleansAppHost/Program.cs" range="12":::
47+
48+
The Orleans resource builder offers methods to configure your Orleans resource. In the following example, the Orleans resource is configured with clustering and grain storage using the `WithClustering` and `WithGrainStorage` methods respectively:
49+
50+
:::code language="csharp" source="snippets/Orleans/OrleansAppHost/Program.cs" range="3-14" highlight="4-5,11-12":::
51+
52+
This tells Orleans that any service referencing it will also need to reference the `clusteringTable` resource.
53+
54+
To participate in an Orleans cluster, reference the Orleans resource from your service project, either using `WithReference(orleans)` to participate as an Orleans server, or `WithReference(orleans.AsClient())` to participate as a client. When you reference the Orleans resource from your service, those resources will also be referenced:
55+
56+
:::code language="csharp" source="snippets/Orleans/OrleansAppHost/Program.cs" range="16-22":::
57+
58+
Putting that all together, here is an example of an Aspire AppHost project which includes:
59+
60+
- An Orleans resource with clustering and storage.
61+
- An Orleans server project, 'OrleansServer'.
62+
- An Orleans client project, 'OrleansClient'.
63+
64+
:::code language="csharp" source="snippets/Orleans/OrleansAppHost/Program.cs" :::
65+
66+
To consume the .NET Aspire Orleans resource from an Orleans server project, there are a few steps:
67+
68+
1. Add the relevant .NET Aspire components. In this example, you're using _Aspire.Azure.Data.Tables_ and _Aspire.Azure.Storage.Blobs_.
69+
2. Add the Orleans provider packages for those .NET Aspire components. In this example, you're using _Microsoft.Orleans.Persistence.AzureStorage_ and _Microsoft.Orleans.Clustering.AzureStorage_.
70+
3. Add Orleans to the host application builder.
71+
72+
In the _Program.cs_ file of your Orleans server project, you must configure the .NET Aspire components you are using and add Orleans to the host builder. Note that the names provided must match the names used in the .NET Aspire app host project: "clustering" for the clustering provider, and "grain-state" for the grain state storage provider:
73+
74+
:::code language="csharp" source="snippets/Orleans/OrleansServer/Program.cs" :::
75+
76+
You do similarly in the _OrleansClient_ project, adding the .NET Aspire resources which your project needs to join the Orleans cluster as a client, and configuring the host builder to add an Orleans client:
77+
78+
:::code language="csharp" source="snippets/Orleans/OrleansClient/Program.cs" :::
79+
80+
## Enabling OpenTelemetry
81+
82+
By convention, .NET Aspire application include a project for defining default configuration and behavior for your service. This project is called the _service default_ project and templates create it with a name ending in _ServiceDefaults_.
83+
To configure Orleans for OpenTelemetry in .NET Aspire, you will need to apply configuration to your service defaults project following the [Orleans observability](/dotnet/orleans/host/monitoring/) guide.
84+
In short, you will need to modify the `ConfigureOpenTelemetry` method to add the Orleans _meters_ and _tracing_ instruments. The following code snippet shows the _Extensions.cs_ file from a service defaults project which has been modified to include metrics and traces from Orleans.
85+
86+
:::code language="csharp" source="snippets/Orleans/OrleansServiceDefaults/Extensions.cs" range="40-68" highlight="15,19-20":::
87+
88+
## Supported providers
89+
90+
The Orleans Aspire integration supports a limited subset of Orleans providers today:
91+
92+
- Clustering:
93+
- Redis
94+
- Azure Storage Tables
95+
- Persistence:
96+
- Redis
97+
- Azure Storage Tables
98+
- Azure Storage Blobs
99+
- Reminders:
100+
- Redis
101+
- Azure Storage Tables
102+
- Grain directory:
103+
- Redis
104+
- Azure Storage Tables
105+
106+
Streaming providers are not supported as of Orleans version 8.1.0.
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
<IsAspireHost>true</IsAspireHost>
9-
<UserSecretsId>28af853d-fd6a-45a4-9e12-2e51806fe248</UserSecretsId>
9+
<UserSecretsId>88427062-d086-46c2-b35e-171d742a6fe0</UserSecretsId>
1010
</PropertyGroup>
1111

12-
<ItemGroup>
13-
<ProjectReference Include="..\SignalR.ApiService\SignalR.ApiService.csproj" />
14-
<ProjectReference Include="..\SignalR.Web\SignalR.Web.csproj" />
15-
</ItemGroup>
16-
1712
<ItemGroup>
1813
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0-preview.7.24251.11" />
14+
<PackageReference Include="Aspire.Hosting.Orleans" Version="8.0.0-preview.7.24251.11" />
1915
<PackageReference Include="Aspire.Hosting.Azure" Version="8.0.0-preview.7.24251.11" />
20-
<PackageReference Include="Aspire.Hosting.Azure.SignalR" Version="8.0.0-preview.7.24251.11" />
16+
<PackageReference Include="Aspire.Hosting.Azure.Storage" Version="8.0.0-preview.7.24251.11" />
17+
18+
<ProjectReference Include="..\OrleansServer\OrleansServer.csproj" />
19+
<ProjectReference Include="..\OrleansClient\OrleansClient.csproj" />
2120
</ItemGroup>
2221

2322
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var builder = DistributedApplication.CreateBuilder(args);
2+
3+
// Add the resources which you will use for Orleans clustering and
4+
// grain state storage.
5+
var storage = builder.AddAzureStorage("storage").RunAsEmulator();
6+
var clusteringTable = storage.AddTables("clustering");
7+
var grainStorage = storage.AddBlobs("grain-state");
8+
9+
// Add the Orleans resource to the Aspire DistributedApplication
10+
// builder, then configure it with Azure Table Storage for clustering
11+
// and Azure Blob Storage for grain storage.
12+
var orleans = builder.AddOrleans("default")
13+
.WithClustering(clusteringTable)
14+
.WithGrainStorage("Default", grainStorage);
15+
16+
// Add our server project and reference your 'orleans' resource from it.
17+
// it can join the Orleans cluster as a service.
18+
// This implicitly add references to the required resources.
19+
// In this case, that is the 'clusteringTable' resource declared earlier.
20+
builder.AddProject<Projects.OrleansServer>("silo")
21+
.WithReference(orleans)
22+
.WithReplicas(3);
23+
24+
// Reference the Orleans resource as a client from the 'frontend'
25+
// project so that it can connect to the Orleans cluster.
26+
builder.AddProject<Projects.OrleansClient>("frontend")
27+
.WithReference(orleans.AsClient())
28+
.WithExternalHttpEndpoints()
29+
.WithReplicas(3);
30+
31+
// Build and run the application.
32+
using var app = builder.Build();
33+
await app.RunAsync();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"profiles": {
3+
"https": {
4+
"commandName": "Project",
5+
"launchBrowser": true,
6+
"dotnetRunMessages": true,
7+
"applicationUrl": "https://localhost:15887;http://localhost:15888",
8+
"environmentVariables": {
9+
"ASPNETCORE_ENVIRONMENT": "Development",
10+
"DOTNET_ENVIRONMENT": "Development",
11+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:16031",
12+
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:17037",
13+
"DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true"
14+
}
15+
},
16+
"http": {
17+
"commandName": "Project",
18+
"launchBrowser": true,
19+
"dotnetRunMessages": true,
20+
"applicationUrl": "http://localhost:15888",
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development",
23+
"DOTNET_ENVIRONMENT": "Development",
24+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031",
25+
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:17038",
26+
"DOTNET_ASPIRE_SHOW_DASHBOARD_RESOURCES": "true",
27+
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
28+
}
29+
},
30+
"generate-manifest": {
31+
"commandName": "Project",
32+
"launchBrowser": true,
33+
"dotnetRunMessages": true,
34+
"commandLineArgs": "--publisher manifest --output-path aspire-manifest.json",
35+
"applicationUrl": "http://localhost:15888",
36+
"environmentVariables": {
37+
"ASPNETCORE_ENVIRONMENT": "Development",
38+
"DOTNET_ENVIRONMENT": "Development",
39+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:16031"
40+
}
41+
}
42+
},
43+
"$schema": "http://json.schemastore.org/launchsettings.json"
44+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"resources": {
3+
"storage": {
4+
"type": "azure.bicep.v0",
5+
"path": "storage.module.bicep",
6+
"params": {
7+
"principalId": "",
8+
"principalType": ""
9+
}
10+
},
11+
"clustering": {
12+
"type": "value.v0",
13+
"connectionString": "{storage.outputs.tableEndpoint}"
14+
},
15+
"grainstate": {
16+
"type": "value.v0",
17+
"connectionString": "{storage.outputs.blobEndpoint}"
18+
},
19+
"silo": {
20+
"type": "project.v0",
21+
"path": "../OrleansServer/OrleansServer.csproj",
22+
"env": {
23+
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
24+
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
25+
"ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true",
26+
"Orleans__Clustering__ProviderType": "AzureTableStorage",
27+
"Orleans__Clustering__ServiceKey": "clustering",
28+
"ConnectionStrings__clustering": "{clustering.connectionString}",
29+
"Orleans__GrainStorage__Default__ProviderType": "AzureBlobStorage",
30+
"Orleans__GrainStorage__Default__ServiceKey": "grainstate",
31+
"ConnectionStrings__grainstate": "{grainstate.connectionString}",
32+
"Orleans__ClusterId": "c7673f46dfc745c8b4492b957aa42408",
33+
"Orleans__Endpoints__SiloPort": "{silo.bindings.orleans-silo.targetPort}",
34+
"Orleans__Endpoints__GatewayPort": "{silo.bindings.orleans-gateway.targetPort}",
35+
"Orleans__EnableDistributedTracing": "true"
36+
},
37+
"bindings": {
38+
"http": {
39+
"scheme": "http",
40+
"protocol": "tcp",
41+
"transport": "http",
42+
"external": true
43+
},
44+
"https": {
45+
"scheme": "https",
46+
"protocol": "tcp",
47+
"transport": "http",
48+
"external": true
49+
},
50+
"orleans-silo": {
51+
"scheme": "tcp",
52+
"protocol": "tcp",
53+
"transport": "tcp",
54+
"targetPort": 8000
55+
},
56+
"orleans-gateway": {
57+
"scheme": "tcp",
58+
"protocol": "tcp",
59+
"transport": "tcp",
60+
"targetPort": 8001
61+
}
62+
}
63+
}
64+
}
65+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
targetScope = 'resourceGroup'
2+
3+
@description('')
4+
param location string = resourceGroup().location
5+
6+
@description('')
7+
param principalId string
8+
9+
@description('')
10+
param principalType string
11+
12+
13+
resource storageAccount_1XR3Um8QY 'Microsoft.Storage/storageAccounts@2022-09-01' = {
14+
name: toLower(take('storage${uniqueString(resourceGroup().id)}', 24))
15+
location: location
16+
tags: {
17+
'aspire-resource-name': 'storage'
18+
}
19+
sku: {
20+
name: 'Standard_GRS'
21+
}
22+
kind: 'StorageV2'
23+
properties: {
24+
accessTier: 'Hot'
25+
networkAcls: {
26+
defaultAction: 'Allow'
27+
}
28+
}
29+
}
30+
31+
resource blobService_vTLU20GRg 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = {
32+
parent: storageAccount_1XR3Um8QY
33+
name: 'default'
34+
properties: {
35+
}
36+
}
37+
38+
resource roleAssignment_Gz09cEnxb 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
39+
scope: storageAccount_1XR3Um8QY
40+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'))
41+
properties: {
42+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
43+
principalId: principalId
44+
principalType: principalType
45+
}
46+
}
47+
48+
resource roleAssignment_HRj6MDafS 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
49+
scope: storageAccount_1XR3Um8QY
50+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'))
51+
properties: {
52+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
53+
principalId: principalId
54+
principalType: principalType
55+
}
56+
}
57+
58+
resource roleAssignment_r0wA6OpKE 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
59+
scope: storageAccount_1XR3Um8QY
60+
name: guid(storageAccount_1XR3Um8QY.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88'))
61+
properties: {
62+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
63+
principalId: principalId
64+
principalType: principalType
65+
}
66+
}
67+
68+
output blobEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.blob
69+
output queueEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.queue
70+
output tableEndpoint string = storageAccount_1XR3Um8QY.properties.primaryEndpoints.table
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Aspire.Azure.Data.Tables" Version="8.0.0-preview.7.24251.11" />
15+
<PackageReference Include="Microsoft.Orleans.Client" Version="8.1.0" />
16+
<PackageReference Include="Microsoft.Orleans.Clustering.AzureStorage" Version="8.1.0" />
17+
<ProjectReference Include="..\OrleansContracts\OrleansContracts.csproj" />
18+
<ProjectReference Include="..\OrleansServiceDefaults\OrleansServiceDefaults.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)