Remove static instance of ActorRuntime

Contributes to: #416

This change removes a mutable static instance - this is low-hanging
fruit design improvement and helps us make these features more flexible
in the future.
This commit is contained in:
Ryan Nowak 2020-10-07 13:07:28 -07:00
parent a061b726b6
commit cd781248c7
4 changed files with 41 additions and 36 deletions

View File

@ -10,31 +10,35 @@ namespace Dapr.Actors.AspNetCore
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
internal static class EndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder AddDaprConfigRoute(this IEndpointRouteBuilder endpoints)
{
var runtime = endpoints.ServiceProvider.GetRequiredService<ActorRuntime>();
return endpoints.MapGet("dapr/config", async context =>
{
context.Response.ContentType = "application/json";
await ActorRuntime.Instance.SerializeSettingsAndRegisteredTypes(context.Response.BodyWriter);
await runtime.SerializeSettingsAndRegisteredTypes(context.Response.BodyWriter);
});
}
public static IEndpointConventionBuilder AddActorDeactivationRoute(this IEndpointRouteBuilder endpoints)
{
var runtime = endpoints.ServiceProvider.GetRequiredService<ActorRuntime>();
return endpoints.MapDelete("actors/{actorTypeName}/{actorId}", async context =>
{
var routeValues = context.Request.RouteValues;
var actorTypeName = (string)routeValues["actorTypeName"];
var actorId = (string)routeValues["actorId"];
await ActorRuntime.DeactivateAsync(actorTypeName, actorId);
await runtime.DeactivateAsync(actorTypeName, actorId);
});
}
public static IEndpointConventionBuilder AddActorMethodRoute(this IEndpointRouteBuilder endpoints)
{
var runtime = endpoints.ServiceProvider.GetRequiredService<ActorRuntime>();
return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/{methodName}", async context =>
{
var routeValues = context.Request.RouteValues;
@ -46,7 +50,7 @@ namespace Dapr.Actors.AspNetCore
if (context.Request.Headers.ContainsKey(Constants.RequestHeaderName))
{
var daprActorheader = context.Request.Headers[Constants.RequestHeaderName];
var (header, body) = await ActorRuntime.DispatchWithRemotingAsync(actorTypeName, actorId, methodName, daprActorheader, context.Request.Body);
var (header, body) = await runtime.DispatchWithRemotingAsync(actorTypeName, actorId, methodName, daprActorheader, context.Request.Body);
// Item 1 is header , Item 2 is body
if (header != string.Empty)
@ -62,7 +66,7 @@ namespace Dapr.Actors.AspNetCore
// write exception info in response.
try
{
await ActorRuntime.DispatchWithoutRemotingAsync(actorTypeName, actorId, methodName, context.Request.Body, context.Response.Body);
await runtime.DispatchWithoutRemotingAsync(actorTypeName, actorId, methodName, context.Request.Body, context.Response.Body);
}
catch (Exception e)
{
@ -78,6 +82,7 @@ namespace Dapr.Actors.AspNetCore
public static IEndpointConventionBuilder AddReminderRoute(this IEndpointRouteBuilder endpoints)
{
var runtime = endpoints.ServiceProvider.GetRequiredService<ActorRuntime>();
return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/remind/{reminderName}", async context =>
{
var routeValues = context.Request.RouteValues;
@ -86,12 +91,13 @@ namespace Dapr.Actors.AspNetCore
var reminderName = (string)routeValues["reminderName"];
// read dueTime, period and data from Request Body.
await ActorRuntime.FireReminderAsync(actorTypeName, actorId, reminderName, context.Request.Body);
await runtime.FireReminderAsync(actorTypeName, actorId, reminderName, context.Request.Body);
});
}
public static IEndpointConventionBuilder AddTimerRoute(this IEndpointRouteBuilder endpoints)
{
var runtime = endpoints.ServiceProvider.GetRequiredService<ActorRuntime>();
return endpoints.MapPut("actors/{actorTypeName}/{actorId}/method/timer/{timerName}", async context =>
{
var routeValues = context.Request.RouteValues;
@ -100,7 +106,7 @@ namespace Dapr.Actors.AspNetCore
var timerName = (string)routeValues["timerName"];
// read dueTime, period and data from Request Body.
await ActorRuntime.FireTimerAsync(actorTypeName, actorId, timerName);
await runtime.FireTimerAsync(actorTypeName, actorId, timerName);
});
}
}

View File

@ -9,6 +9,7 @@ namespace Dapr.Actors.AspNetCore
using Dapr.Actors.Runtime;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
/// <summary>
/// Class containing DaprActor related extension methods for Microsoft.AspNetCore.Hosting.IWebHostBuilder.
@ -36,7 +37,11 @@ namespace Dapr.Actors.AspNetCore
return hostBuilder;
}
configureActorRuntime.Invoke(ActorRuntime.Instance);
var runtime = new ActorRuntime();
if (configureActorRuntime != null)
{
configureActorRuntime.Invoke(runtime);
}
// Set flag to prevent double service configuration
hostBuilder.UseSetting(SettingName, true.ToString());
@ -47,6 +52,8 @@ namespace Dapr.Actors.AspNetCore
services.AddRouting();
services.AddHealthChecks();
services.AddSingleton<IStartupFilter>(new DaprActorSetupFilter());
services.AddSingleton<ActorRuntime>(runtime);
});
return hostBuilder;

View File

@ -6,6 +6,7 @@
namespace Dapr.Actors.Runtime
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
@ -15,13 +16,8 @@ namespace Dapr.Actors.Runtime
/// <summary>
/// Contains methods to register actor types. Registering the types allows the runtime to create instances of the actor.
/// </summary>
public class ActorRuntime
public sealed class ActorRuntime
{
/// <summary>
/// Gets ActorRuntime.
/// </summary>
public static readonly ActorRuntime Instance = new ActorRuntime();
private const string TraceType = "ActorRuntime";
// Map of ActorType --> ActorManager.
@ -29,10 +25,6 @@ namespace Dapr.Actors.Runtime
private ActorSettings actorSettings;
/// <remarks>
/// WARNING: This type is expected to be accessed via the <see cref="Instance" /> singleton instance.
/// This constructor is exposed only for unit testing purposes.
/// </remarks>
internal ActorRuntime()
{
this.actorSettings = new ActorSettings();
@ -78,7 +70,7 @@ namespace Dapr.Actors.Runtime
actorSettingsDelegate.Invoke(this.actorSettings);
}
internal Task SerializeSettingsAndRegisteredTypes(System.Buffers.IBufferWriter<byte> output)
internal Task SerializeSettingsAndRegisteredTypes(IBufferWriter<byte> output)
{
using Utf8JsonWriter writer = new Utf8JsonWriter(output);
writer.WriteStartObject();
@ -124,9 +116,9 @@ namespace Dapr.Actors.Runtime
/// <param name="actorTypeName">Actor type name to deactivate the actor for.</param>
/// <param name="actorId">Actor id for the actor to be deactivated.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
internal static async Task DeactivateAsync(string actorTypeName, string actorId)
internal async Task DeactivateAsync(string actorTypeName, string actorId)
{
await Instance.GetActorManager(actorTypeName).DeactivateActor(new ActorId(actorId));
await GetActorManager(actorTypeName).DeactivateActor(new ActorId(actorId));
}
/// <summary>
@ -134,14 +126,14 @@ namespace Dapr.Actors.Runtime
/// </summary>
/// <param name="actorTypeName">Actor type name to invokde the method for.</param>
/// <param name="actorId">Actor id for the actor for which method will be invoked.</param>
/// <param name="actorMethodName">MEthos name on actor type which will be invoked.</param>
/// <param name="actorMethodName">Method name on actor type which will be invoked.</param>
/// <param name="daprActorheader">Actor Header.</param>
/// <param name="data">Payload for the actor method.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
internal static Task<Tuple<string, byte[]>> DispatchWithRemotingAsync(string actorTypeName, string actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken = default)
internal Task<Tuple<string, byte[]>> DispatchWithRemotingAsync(string actorTypeName, string actorId, string actorMethodName, string daprActorheader, Stream data, CancellationToken cancellationToken = default)
{
return Instance.GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken);
return GetActorManager(actorTypeName).DispatchWithRemotingAsync(new ActorId(actorId), actorMethodName, daprActorheader, data, cancellationToken);
}
/// <summary>
@ -149,14 +141,14 @@ namespace Dapr.Actors.Runtime
/// </summary>
/// <param name="actorTypeName">Actor type name to invokde the method for.</param>
/// <param name="actorId">Actor id for the actor for which method will be invoked.</param>
/// <param name="actorMethodName">MEthos name on actor type which will be invoked.</param>
/// <param name="actorMethodName">Method name on actor type which will be invoked.</param>
/// <param name="requestBodyStream">Payload for the actor method.</param>
/// <param name="responseBodyStream">Response for the actor method.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
internal static Task DispatchWithoutRemotingAsync(string actorTypeName, string actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken = default)
internal Task DispatchWithoutRemotingAsync(string actorTypeName, string actorId, string actorMethodName, Stream requestBodyStream, Stream responseBodyStream, CancellationToken cancellationToken = default)
{
return Instance.GetActorManager(actorTypeName).DispatchWithoutRemotingAsync(new ActorId(actorId), actorMethodName, requestBodyStream, responseBodyStream, cancellationToken);
return GetActorManager(actorTypeName).DispatchWithoutRemotingAsync(new ActorId(actorId), actorMethodName, requestBodyStream, responseBodyStream, cancellationToken);
}
/// <summary>
@ -168,9 +160,9 @@ namespace Dapr.Actors.Runtime
/// <param name="requestBodyStream">Payload for the actor method.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
internal static Task FireReminderAsync(string actorTypeName, string actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default)
internal Task FireReminderAsync(string actorTypeName, string actorId, string reminderName, Stream requestBodyStream, CancellationToken cancellationToken = default)
{
return Instance.GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken);
return GetActorManager(actorTypeName).FireReminderAsync(new ActorId(actorId), reminderName, requestBodyStream, cancellationToken);
}
/// <summary>
@ -181,9 +173,9 @@ namespace Dapr.Actors.Runtime
/// <param name="timerName">The name of timer provided during registration.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
internal static Task FireTimerAsync(string actorTypeName, string actorId, string timerName, CancellationToken cancellationToken = default)
internal Task FireTimerAsync(string actorTypeName, string actorId, string timerName, CancellationToken cancellationToken = default)
{
return Instance.GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), timerName, cancellationToken);
return GetActorManager(actorTypeName).FireTimerAsync(new ActorId(actorId), timerName, cancellationToken);
}
private ActorManager GetActorManager(string actorTypeName)

View File

@ -7,7 +7,6 @@ namespace Dapr.Actors.Test
{
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
@ -54,18 +53,19 @@ namespace Dapr.Actors.Test
// This tests the change that removed the Activate message from Dapr runtime -> app.
[Fact]
public void NoActivateMessageFromRuntime()
public async Task NoActivateMessageFromRuntime()
{
var actorType = typeof(MyActor);
ActorRuntime.Instance.RegisterActor<MyActor>();
var runtime = new ActorRuntime();
runtime.RegisterActor<MyActor>();
var output = new MemoryStream();
ActorRuntime.DispatchWithoutRemotingAsync("MyActor", "abc", "MyMethod", new MemoryStream(), output).GetAwaiter().GetResult();
await runtime.DispatchWithoutRemotingAsync("MyActor", "abc", "MyMethod", new MemoryStream(), output);
string s = Encoding.UTF8.GetString(output.ToArray());
Assert.Equal("\"hi\"", s);
Assert.Contains(actorType.Name, ActorRuntime.Instance.RegisteredActorTypes, StringComparer.InvariantCulture);
Assert.Equal("\"hi\"", s);
Assert.Contains(actorType.Name, runtime.RegisteredActorTypes, StringComparer.InvariantCulture);
Console.WriteLine("done");
}