使用 Service Fabric 托管
Orleans 可使用 NuGet 包 Microsoft.ServiceFabric.Services 和 Microsoft.Orleans.Server 托管在 Azure Service Fabric 上。 由于 Orleans 管理粒度本身的分布,接收器应托管为未分区的无状态服务。 其他托管选项(如分区和有状态)更为复杂,如果没有开发人员的额外自定义,则不会产生任何好处。 建议托管无分区和无状态的 Orleans。
Service Fabric 无状态服务作为接收器
无论是创建新的 Service Fabric 应用程序还是将 Orleans 添加到现有应用程序,都需要项目中的 Microsoft.ServiceFabric.Services
和 Microsoft.Orleans.Server
包引用。 无状态服务项目需要在 ICommunicationListener 和 StatelessService 的子类上实现。
接收器生命周期遵循典型的通信侦听器生命周期:
- 它使用 ICommunicationListener.OpenAsync 进行初始化。
- 它使用 ICommunicationListener.CloseAsync 正常终止。
- 或者,它使用 ICommunicationListener.Abort 突然终止。
由于 Orleans 接收器能够生存在 IHost 的范围内,因此 ICommunicationListener
的实现是围绕 IHost
的包装器。 在 OpenAsync
方法中初始化 IHost
,并在 CloseAsync
方法中正常终止:
ICommunicationListener |
IHost 交互 |
---|---|
OpenAsync | 创建 IHost 实例并调用 StartAsync。 |
CloseAsync | 正在等待在主机实例上对 StopAsync 进行调用。 |
Abort | 使用 GetAwaiter().GetResult() 强制计算对 StopAsync 的调用。 |
群集支持
各种包提供官方群集支持,包括:
- Microsoft.Orleans.Clustering.AzureStorage
- Microsoft.Orleans.Clustering.AdoNet
- Microsoft.Orleans.Clustering.DynamoDB
还有几个第三方包可用于其他服务,例如 CosmosDB、Kubernetes、Redis 和 Aerospike。 有关详细信息,请参阅 Orleans 中的群集管理。
示例项目
在无状态服务项目中,实现 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
实例。