Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Host Orleans on Azure Service Fabric using the Microsoft.ServiceFabric.Services and Microsoft.Orleans.Server NuGet packages. Host silos as unpartitioned, stateless services because Orleans manages grain distribution itself. Other hosting options, such as partitioned and stateful, are more complex and yield no benefits without additional customization. Hosting Orleans as unpartitioned and stateless is recommended.
Service Fabric stateless service as a silo
Whether creating a new Service Fabric Application or adding Orleans to an existing one, both the Microsoft.ServiceFabric.Services
and Microsoft.Orleans.Server
package references are needed in the project. The stateless service project requires an implementation of ICommunicationListener and a subclass of StatelessService.
The Silo lifecycle follows the typical communication listener lifecycle:
- Initialized with ICommunicationListener.OpenAsync.
- Gracefully terminated with ICommunicationListener.CloseAsync.
- Or abruptly terminated with ICommunicationListener.Abort.
Since Orleans Silos can live within the confines of the IHost, the implementation of ICommunicationListener
is a wrapper around the IHost
. The IHost
initializes in the OpenAsync
method and gracefully terminates in the CloseAsync
method:
ICommunicationListener |
IHost interactions |
---|---|
OpenAsync | The IHost instance is created and a call to StartAsync is made. |
CloseAsync | A call to StopAsync on the host instance is awaited. |
Abort | A call to StopAsync is forcefully evaluated with GetAwaiter().GetResult() . |
Cluster support
Official clustering support is available from various packages, including:
- Microsoft.Orleans.Clustering.AzureStorage
- Microsoft.Orleans.Clustering.AdoNet
- Microsoft.Orleans.Clustering.DynamoDB
Several third-party packages are also available for other services such as Cosmos DB, Kubernetes, Redis, and Aerospike. For more information, see Cluster management in Orleans.
Example project
In the stateless service project, implement the ICommunicationListener
interface as shown in the following example:
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
namespace ServiceFabric.HostingExample;
internal sealed class HostedServiceCommunicationListener : ICommunicationListener
{
private IHost? _host;
private readonly Func<Task<IHost>> _createHost;
public HostedServiceCommunicationListener(Func<Task<IHost>> createHost) =>
_createHost = createHost ?? throw new ArgumentNullException(nameof(createHost));
/// <inheritdoc />
public async Task<string?> OpenAsync(CancellationToken cancellationToken)
{
try
{
_host = await _createHost.Invoke();
await _host.StartAsync(cancellationToken);
}
catch
{
Abort();
throw;
}
// This service does not expose any endpoints to Service Fabric for discovery by others.
return null;
}
/// <inheritdoc />
public async Task CloseAsync(CancellationToken cancellationToken)
{
if (_host is { } host)
{
await host.StopAsync(cancellationToken);
}
_host = null;
}
/// <inheritdoc />
public void Abort()
{
IHost? host = _host;
if (host is null)
{
return;
}
using CancellationTokenSource cancellation = new();
cancellation.Cancel(false);
try
{
host.StopAsync(cancellation.Token).GetAwaiter().GetResult();
}
catch
{
// Ignore.
}
finally
{
_host = null;
}
}
}
The HostedServiceCommunicationListener
class accepts a Func<Task<IHost>> createHost
constructor parameter. This function is later used to create the IHost
instance in the OpenAsync
method.
The next part of the stateless service project is implementing the StatelessService
class. The following example shows the subclass of StatelessService
:
using System.Fabric;
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
namespace ServiceFabric.HostingExample;
public sealed class OrleansHostedStatelessService : StatelessService
{
private readonly Func<StatelessServiceContext, Task<IHost>> _createHost;
public OrleansHostedStatelessService(
Func<StatelessServiceContext, Task<IHost>> createHost, StatelessServiceContext serviceContext)
: base(serviceContext) =>
_createHost = createHost ?? throw new ArgumentNullException(nameof(createHost));
/// <inheritdoc/>
protected sealed override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
// Create a listener which creates and runs an IHost
yield return new ServiceInstanceListener(
context => new HostedServiceCommunicationListener(() => _createHost(context)),
nameof(HostedServiceCommunicationListener));
}
}
In the preceding example, the OrleansHostedStatelessService
class is responsible for yielding an ICommunicationListener
instance. The Service Fabric runtime calls the CreateServiceInstanceListeners
method when the service initializes.
Pulling these two classes together, the following example shows the complete Program.cs file for the stateless service project:
using System.Fabric;
using Microsoft.Extensions.Hosting;
using Microsoft.ServiceFabric.Services.Runtime;
using ServiceFabric.HostingExample;
try
{
// The ServiceManifest.XML file defines one or more service type names.
// Registering a service maps a service type name to a .NET type.
// When Service Fabric creates an instance of this service type,
// an instance of the class is created in this host process.
await ServiceRuntime.RegisterServiceAsync(
"Orleans.ServiceFabric.Stateless",
context => new OrleansHostedStatelessService(
CreateHostAsync, context));
ServiceEventSource.Current.ServiceTypeRegistered(
Environment.ProcessId,
typeof(OrleansHostedStatelessService).Name);
// Prevents this host process from terminating so services keep running.
await Task.Delay(Timeout.Infinite);
}
catch (Exception ex)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(
ex.ToString());
throw;
}
static async Task<IHost> CreateHostAsync(StatelessServiceContext context)
{
await Task.CompletedTask;
return Host.CreateDefaultBuilder()
.UseOrleans((_, builder) =>
{
// TODO, Use real storage, something like table storage
// or SQL Server for clustering.
builder.UseLocalhostClustering();
// Service Fabric manages port allocations, so update the
// configuration using those ports. Gather configuration from
// Service Fabric.
var activation = context.CodePackageActivationContext;
var endpoints = activation.GetEndpoints();
// These endpoint names correspond to TCP endpoints
// specified in ServiceManifest.xml
var siloEndpoint = endpoints["OrleansSiloEndpoint"];
var gatewayEndpoint = endpoints["OrleansProxyEndpoint"];
var hostname = context.NodeContext.IPAddressOrFQDN;
builder.ConfigureEndpoints(hostname,
siloEndpoint.Port, gatewayEndpoint.Port);
})
.Build();
}
In the preceding code:
- The ServiceRuntime.RegisterServiceAsync method registers the
OrleansHostedStatelessService
class with the Service Fabric runtime. - The
CreateHostAsync
delegate creates theIHost
instance.