在Azure Service Fabric上使用Orleans,并使用Microsoft.ServiceFabric.Services和Microsoft.Orleans.Server NuGet 包进行托管。 托管孤岛作为未分区的无状态服务,因为 Orleans 管理粮食分发本身。 其他托管选项(如已分区和有状态)更为复杂,如果不进行额外的自定义,就不会有任何好处。 建议将 Orleans 以无分区和无状态的形式进行托管。
Service Fabric 无状态服务作为独立单元
无论是创建新的 Service Fabric 应用程序还是将 Orleans 添加到现有的应用程序, 项目中都需要引用 Microsoft.ServiceFabric.Services
和 Microsoft.Orleans.Server
包。 无状态服务项目需要实现 ICommunicationListener 和子类 StatelessService。
Silo生命周期遵循典型的通信侦听器生命周期:
- 使用ICommunicationListener.OpenAsync初始化。
- 优雅地终止了 ICommunicationListener.CloseAsync。
- 或者突然终止了 ICommunicationListener.Abort。
由于Orleans孤岛可以存在于IHost的限制范围内,因此实现ICommunicationListener
是IHost
的封装器。
IHost
在OpenAsync
方法中初始化,并在CloseAsync
方法中正常终止。
ICommunicationListener |
IHost 相互 作用 |
---|---|
OpenAsync | 创建 IHost 实例并调用 StartAsync。 |
CloseAsync | 正在等待主机实例上的 StopAsync 调用。 |
Abort |
StopAsync调用被强制使用GetAwaiter().GetResult() 进行评估。 |
群集支持
各种包提供官方群集支持,包括:
- Microsoft.Orleans.Clustering.AzureStorage
- Microsoft.Orleans.Clustering.AdoNet
- Microsoft.Orleans.Clustering.DynamoDB
其他服务(如 Cosmos DB、Kubernetes、Redis 和 Aerospike)也提供了多个第三方包。 有关详细信息,请参阅
示例项目
在无状态服务项目中,实现 ICommunicationListener
接口,如以下示例所示:
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;
}
}
}
该 HostedServiceCommunicationListener
类接受 Func<Task<IHost>> createHost
构造函数参数。 此函数稍后用于在OpenAsync
方法中创建IHost
实例。
无状态服务项目的下一部分是实现 StatelessService
类。 以下示例显示了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));
}
}
在前面的示例中,类 OrleansHostedStatelessService
负责生成 ICommunicationListener
实例。 Service Fabric 运行时在服务初始化时调用 CreateServiceInstanceListeners
该方法。
以下示例将这两个类拉在一起,显示了无状态服务项目的完整 Program.cs 文件:
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();
}
在前面的代码中:
- 该方法 ServiceRuntime.RegisterServiceAsync 将
OrleansHostedStatelessService
类注册到 Service Fabric 运行时。 - 委托
CreateHostAsync
创建IHost
实例。