我有一个正在运行的服务正在迭代X个actor,使用ActorProxy询问它们的状态。
对我来说很重要的是,这个服务不会被等待提醒回调中的其他一些长时间运行的方法阻止。
是否有某种方法可以调用下面的简单示例GetState(),该示例允许该方法以正确的方式完成,而不会阻止某些提醒运行。
class Actor : IMyActor{
public Task<MyState> GetState() => StateManager.GetAsync("key")
}
alterntive。
调用服务的正确方法是什么,如果它在5秒内没有回复,只需要包含。
var proxy = ActorProxy.Create<IMyActor();
var state = await proxy.GetState(); // this will wait until the actor is ready to send back the state.
答案 0 :(得分:3)
即使对于当前正在执行阻塞方法的Actors,也可以读取actor状态。演员使用IActorStateManager
存储他们的状态,而IActorStateProvider
又使用IActorStateProvider
。 ActorService
每ActorService
实例化一次StatefulServiceBase
。每个分区都实例化负责托管和运行actor的IService
。 actor服务的核心是StatefulService
(或者更确切地说是IActorStateProvider
,它是常规有状态服务使用的基类)。考虑到这一点,我们可以使用迎合我们的Actors的ActorService
,就像我们使用常规服务一样,即使用基于KvsActorStateProvider
的服务界面。
{{3}}(如果您使用的是持久状态,则由{{3}}实现)有两种方法可供我们使用:
Task<T> LoadStateAsync<T>(ActorId actorId, string stateName, CancellationToken cancellationToken = null);
Task<PagedResult<ActorId>> GetActorsAsync(int numItemsToReturn, ContinuationToken continuationToken, CancellationToken cancellationToken);
对这些方法的调用不受演员的影响。锁,这是有道理的,因为它们旨在支持分区上的所有actor。
示例:强>
创建自定义ActorService
并使用该自定义来托管您的演员:
public interface IManyfoldActorService : IService
{
Task<IDictionary<long, int>> GetCountsAsync(CancellationToken cancellationToken);
}
public class ManyfoldActorService : ActorService, IManyfoldActorService
{
...
}
在Program.Main
注册新的ActorService:
ActorRuntime.RegisterActorAsync<ManyfoldActor>(
(context, actorType) => new ManyfoldActorService(context, actorType)).GetAwaiter().GetResult();
假设我们有一个简单的Actor,其中包含以下方法:
Task IManyfoldActor.SetCountAsync(int count, CancellationToken cancellationToken)
{
Task.Delay(TimeSpan.FromSeconds(30), cancellationToken).GetAwaiter().GetResult();
var task = this.StateManager.SetStateAsync("count", count, cancellationToken);
ActorEventSource.Current.ActorMessage(this, $"Finished set {count} on {this.Id.GetLongId()}");
return task;
}
等待30秒(模拟长时间运行,阻塞,方法调用),然后将状态值"count"
设置为int
。
在单独的服务中,我们现在可以为Actors调用SetCountAsync
来生成一些状态数据:
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var actorProxyFactory = new ActorProxyFactory();
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
iterations += 1;
var actorId = iterations % 10;
var count = Environment.TickCount % 100;
var manyfoldActor = actorProxyFactory.CreateActorProxy<IManyfoldActor>(new ActorId(actorId));
manyfoldActor.SetCountAsync(count, cancellationToken).ConfigureAwait(false);
ServiceEventSource.Current.ServiceMessage(this.Context, $"Set count {count} on {actorId} @ {iterations}");
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);
}
}
这种方法只是无休止地循环改变actor的值。 (注意总共10个演员之间的相关性,延迟3秒和演员30秒的延迟。简单地设计这种方式以防止等待锁定的Actor调用的无限累积)。每个调用也会执行为“即发即忘”,因此我们可以在返回之前继续更新下一个actor的状态。它是一段愚蠢的代码,它只是用这种方式来证明这个理论。
现在在actor服务中,我们可以像这样实现方法GetCountsAsync
:
public async Task<IDictionary<long, int>> GetCountsAsync(CancellationToken cancellationToken)
{
ContinuationToken continuationToken = null;
var actors = new Dictionary<long, int>();
do
{
var page = await this.StateProvider.GetActorsAsync(100, continuationToken, cancellationToken);
foreach (var actor in page.Items)
{
var count = await this.StateProvider.LoadStateAsync<int>(actor, "count", cancellationToken);
actors.Add(actor.GetLongId(), count);
}
continuationToken = page.ContinuationToken;
}
while (continuationToken != null);
return actors;
}
这使用底层ActorStateProvider
查询所有已知的Actors(针对该分区),然后直接读取每种方式的状态&#39;绕过&#39;演员并没有被演员的方法执行所阻挡。
最终作品,一些可以调用我们的ActorService并在所有分区中调用GetCountsAsync
的方法:
public IDictionary<long, int> Get()
{
var applicationName = FabricRuntime.GetActivationContext().ApplicationName;
var actorServiceName = $"{typeof(IManyfoldActorService).Name.Substring(1)}";
var actorServiceUri = new Uri($"{applicationName}/{actorServiceName}");
var fabricClient = new FabricClient();
var partitions = new List<long>();
var servicePartitionList = fabricClient.QueryManager.GetPartitionListAsync(actorServiceUri).GetAwaiter().GetResult();
foreach (var servicePartition in servicePartitionList)
{
var partitionInformation = servicePartition.PartitionInformation as Int64RangePartitionInformation;
partitions.Add(partitionInformation.LowKey);
}
var serviceProxyFactory = new ServiceProxyFactory();
var actors = new Dictionary<long, int>();
foreach (var partition in partitions)
{
var actorService = serviceProxyFactory.CreateServiceProxy<IManyfoldActorService>(actorServiceUri, new ServicePartitionKey(partition));
var counts = actorService.GetCountsAsync(CancellationToken.None).GetAwaiter().GetResult();
foreach (var count in counts)
{
actors.Add(count.Key, count.Value);
}
}
return actors;
}
现在运行此代码将为我们提供10个演员,每33秒进行一次状态更新,每个演员每次都忙30秒。一旦每个actor方法返回,Actor服务就会看到更新后的状态。
此示例中省略了一些内容,例如,当您在actor服务中加载状态时,我们应该防止超时。
答案 1 :(得分:0)
没有办法做到这一点。演员是单线程的。如果他们正在等待在任何actor方法内完成的长时间运行工作,那么任何其他方法(包括来自外部的方法)都必须等待。
答案 2 :(得分:0)
感谢您的所有帮助。能够以您的示例为例,并进行了一些调整。我唯一的问题是将数据传递回原始应用程序服务时遇到未知类型。正在
“不希望使用ArrayOfKeyValueOflonglong。将任何静态未知的类型添加到已知类型的列表中-例如,通过使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型的列表中”
因此,我将GetCountsAsync的Return类型更改为List