web-dev-qa-db-ja.com

Azureサービスファブリックアクターの依存性注入

Azure Service Fabric Actorのコンストラクターに依存関係を注入する方法はありますか?

20
yapepyaka

しばらく前にdotPeekを使用してこの領域を少し掘り下げました(呼び出しごとにAutofacライフタイムスコープからアクターを解決しようとしています)、トリックはStatelessActorServiceFactoryの独自の実装と独自の拡張メソッドを作成することだと思いますアクターを登録します。ファクトリクラスは内部としてマークされていますが、そのインターフェイス(IStatelessServiceFactory)とそれが作成するサービスタイプ(StatelessActorServiceInstance)は両方ともパブリックです。残念ながら、StatelessActorServiceInstanceは拡張可能に設計されているようには見えません(これが単なる見落としであることを願っています)。

残念ながら、WcfActorCommunicationProviderも内部としてマークされているように見えるため、独自のパイプラインを最初から作成する必要があります。

  1. 独自のIStatelessServiceFactoryを実装する
  2. 独自のIStatelessServiceInstance、IActorServiceを実装します
  3. 独自のIActorCommunicationProviderを実装する
  4. 独自のIActorHostを実装する

もう努力する価値はないようですよね? :-/

それは私が今のところあきらめたところです。パブリックAPIが比較的未成熟であることを考えると、今のところ自分でロールする価値はないと思います。この種の機能が表示されたとしても、おそらく壊れてしまうからです。自分で実装するものは何でも。

8
tintoy

更新しました

そのすべてがgithubとmygetになりました: https://github.com/s-innovations/S-Innovations.ServiceFabric.Unity

面倒なことなくaspnetコアの依存性注入と統合します。readme.mdの例を確認してください。


私はUnityの長年のユーザーであり、アクターを操作するときに、Niceの依存性注入エクスペリエンスを実現するために必要なコア拡張メソッドを作成することにしました。

私のprogram.csは次のようになります。

internal static class Program
{
    /// <summary>
    /// This is the entry point of the service Host process.
    /// </summary>
    private static void Main()
    {
        try
        {
            using (var container = new UnityContainer())
            {
                container.RegisterType<IMessageProcessorClientFactory, DummyFactory>(new HierarchicalLifetimeManager());
                container.RegisterType<IMessageClusterConfigurationStore, test>(new HierarchicalLifetimeManager());

                container.WithFabricContainer();
                container.WithActor<MessageClusterActor>();
                container.WithActor<QueueListenerActor>();
                container.WithStatelessFactory<ManagementApiServiceFactory>("ManagementApiServiceType");
                container.WithActor<VmssManagerActor>();

                ServiceFabricEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(ManagementApiService).Name);

                Thread.Sleep(Timeout.Infinite);  // Prevents this Host process from terminating to keep the service Host process running.
            }
        }
        catch (Exception e)
        {
            ServiceFabricEventSource.Current.ActorHostInitializationFailed(e.ToString());
            throw;
        }
    }
}

アクターとサービスの私は、コンストラクターに依存関係を置くことができます。

public class VmssManagerActor : StatefulActor<VmssManagerActor.ActorState>, IVmssManagerActor, IRemindable
{
    public const string CheckProvision = "CheckProvision";

    /// <summary>
    /// Cluster Configuration Store
    /// </summary>       
    protected IMessageClusterConfigurationStore ClusterConfigStore { get; private set; }

    public VmssManagerActor(IMessageClusterConfigurationStore clusterProvider)
    {
        ClusterConfigStore = clusterProvider;
    }

これが便利だと感じて、それをnugetパッケージに入れたい場合は、この回答に賛成してください。

実装に関する1つの注意点として、各アクターは独自のスコープを取得します。これは、IDisposableを実装する 'HierarchicalLifetimeManager'に登録されているすべての依存関係が、アクターOnDeactivationAsyncに自動的に破棄されることを意味します。これは、OnDeactivationAsyncへの呼び出しをインターセプトする動的タイプでアクタークラスを動的にプロキシすることによって行われました。これが機能するには、アクターがパブリック定義されている必要があります。

IActorDeactivationInterception.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Abstraction
{
    /// <summary>
    /// The <see cref="IActorDeactivationInterception"/> interface for defining an OnDeactivateInterception
    /// </summary>
    public interface IActorDeactivationInterception
    {
        void Intercept();
    }
}

ActorProxyTypeFactory.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Actors
{
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using SInnovations.Azure.ServiceFabric.Unity.Abstraction;

    public class ActorProxyTypeFactory
    {
        /// <summary>
        /// Creates a new instance of the <see cref="ActorProxyTypeFactory"/> class.
        /// </summary>
        /// <param name="target"></param>
        public ActorProxyTypeFactory(Type target)
        {
            this.target = target;
        }

        /// <summary>
        /// Creates the proxy registered with specific interceptor.
        /// </summary>
        /// <returns></returns>
        public static T Create<T>(IActorDeactivationInterception deactivation, params object[] args)
        {
            return (T)new ActorProxyTypeFactory(typeof(T)).Create(new object[] { deactivation }.Concat(args).ToArray());
        }
        public static Type CreateType<T>()
        {
            return new ActorProxyTypeFactory(typeof(T)).CreateType();
        }
        /// <summary>
        /// Creates the proxy registered with specific interceptor.
        /// </summary>
        /// <returns></returns>
        public object Create(object[] args)
        {
            BuidAssembly();
            BuildType();
            InterceptAllMethods();

            Type proxy = this.typeBuilder.CreateType();

            return Activator.CreateInstance(proxy, args);
        }

        public Type CreateType()
        {
            BuidAssembly();
            BuildType();
            InterceptAllMethods();

            Type proxy = this.typeBuilder.CreateType();
            return proxy;
            //  return Activator.CreateInstance(proxy, args);
        }

        /// <summary>
        /// Builds a dynamic Assembly with <see cref="AssemblyBuilderAccess.RunAndSave"/> mode.
        /// </summary>
        /// <returns></returns>
        public void BuidAssembly()
        {
            AssemblyName assemblyName = new AssemblyName("BasicProxy");
            AssemblyBuilder createdAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            // define module
            this.moduleBuilder = createdAssembly.DefineDynamicModule(assemblyName.Name);
        }

        public void BuildType()
        {
            if (!target.IsPublic)
            {
                throw new ArgumentException("Actors have to be public defined to proxy them");
            }


            this.typeBuilder =
                this.moduleBuilder.DefineType(target.FullName + "Proxy", TypeAttributes.Class | TypeAttributes.Public, target);
            this.fldInterceptor = this.typeBuilder.DefineField("interceptor", typeof(IActorDeactivationInterception), FieldAttributes.Private);

            foreach (var constructor in target.GetConstructors())
            {
                //  Type[] parameters = new Type[1];

                ParameterInfo[] parameterInfos = constructor.GetParameters();
                Type[] parameters = new Type[parameterInfos.Length + 1];

                parameters[0] = typeof(IActorDeactivationInterception);


                for (int index = 1; index <= parameterInfos.Length; index++)
                {
                    parameters[index] = parameterInfos[index - 1].ParameterType;
                }

                ConstructorBuilder constructorBuilder =
                    typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, parameters);

                for (int argumentIndex = 0; argumentIndex < parameters.Length; argumentIndex++)
                    constructorBuilder.DefineParameter(
                        argumentIndex + 1,
                        ParameterAttributes.None,
                        $"arg{argumentIndex}");

                ILGenerator generator = constructorBuilder.GetILGenerator();

                generator.Emit(OpCodes.Ldarg_0);

                for (int index = 1; index < parameters.Length; index++)
                {
                    generator.Emit(OpCodes.Ldarg, index + 1);
                }

                generator.Emit(OpCodes.Call, constructor);

                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldarg_1);
                generator.Emit(OpCodes.Stfld, fldInterceptor);
                generator.Emit(OpCodes.Ret);
            }
        }

        /// <summary>
        /// Builds a type in the dynamic Assembly, if already the type is not created.
        /// </summary>
        /// <returns></returns>
        public void InterceptAllMethods()
        {

            const MethodAttributes targetMethodAttributes =
                MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig;

            var methodInfo = target.GetMethod("OnDeactivateAsync", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            {
                if (methodInfo.IsVirtual)
                {
                    Type[] paramTypes = GetParameterTypes(methodInfo.GetParameters());

                    MethodBuilder methodBuilder =
                        typeBuilder.DefineMethod(methodInfo.Name, targetMethodAttributes, methodInfo.ReturnType, paramTypes);

                    ILGenerator ilGenerator = methodBuilder.GetILGenerator();


                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, fldInterceptor);
                    ilGenerator.Emit(OpCodes.Call, typeof(IActorDeactivationInterception).GetMethod("Intercept"));

                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Call, methodInfo);
                    ilGenerator.Emit(OpCodes.Ret);

                    return;


                }
            }
        }



        private Type[] GetParameterTypes(ParameterInfo[] parameterInfos)
        {
            Type[] parameters = new Type[parameterInfos.Length];

            int index = 0;

            foreach (var parameterInfo in parameterInfos)
            {
                parameters[index++] = parameterInfo.ParameterType;
            }
            return parameters;
        }

        private TypeBuilder typeBuilder;
        private ModuleBuilder moduleBuilder;
        private readonly Type target;
        private FieldInfo fldInterceptor;

    }

}

OnActorDeactivateInterceptor.cs

namespace SInnovations.Azure.ServiceFabric.Unity.Actors
{
    using Microsoft.Practices.Unity;
    using SInnovations.Azure.ServiceFabric.Unity.Abstraction;

    public class OnActorDeactivateInterceptor : IActorDeactivationInterception
    {
        private readonly IUnityContainer container;
        public OnActorDeactivateInterceptor(IUnityContainer container)
        {
            this.container = container;
        }

        public void Intercept()
        {
            this.container.Dispose();
        }
    }
}

UnityFabricExtensions.cs

namespace SInnovations.Azure.ServiceFabric.Unity
{
    using System;
    using System.Fabric;
    using Microsoft.Practices.Unity;
    using Microsoft.ServiceFabric.Actors;
    using SInnovations.Azure.ServiceFabric.Unity.Abstraction;
    using SInnovations.Azure.ServiceFabric.Unity.Actors;

    public static class UnityFabricExtensions
    {
        public static IUnityContainer WithFabricContainer(this IUnityContainer container)
        {
            return container.WithFabricContainer(c => FabricRuntime.Create());
        }
        public static IUnityContainer WithFabricContainer(this IUnityContainer container, Func<IUnityContainer,FabricRuntime> factory)
        {
            container.RegisterType<FabricRuntime>(new ContainerControlledLifetimeManager(), new InjectionFactory(factory));
            return container;
        }

        public static IUnityContainer WithActor<TActor>(this IUnityContainer container) where TActor : ActorBase
        {
            if (!container.IsRegistered<IActorDeactivationInterception>())
            {
                container.RegisterType<IActorDeactivationInterception, OnActorDeactivateInterceptor>(new HierarchicalLifetimeManager());
            }

            container.RegisterType(typeof(TActor), ActorProxyTypeFactory.CreateType<TActor>(),new HierarchicalLifetimeManager());
            container.Resolve<FabricRuntime>().RegisterActorFactory(() => {
                try {
                    var actor = container.CreateChildContainer().Resolve<TActor>();
                    return actor;
                }
                catch (Exception ex)
                {
                    throw;
                }
                });

            return container;
        }


        public static IUnityContainer WithStatelessFactory<TFactory>(this IUnityContainer container, string serviceTypeName) where TFactory : IStatelessServiceFactory
        {
            if (!container.IsRegistered<TFactory>())
            {
                container.RegisterType<TFactory>(new ContainerControlledLifetimeManager());
            }
            container.Resolve<FabricRuntime>().RegisterStatelessServiceFactory(serviceTypeName, container.Resolve<TFactory>());
            return container;
        }
        public static IUnityContainer WithStatefulFactory<TFactory>(this IUnityContainer container, string serviceTypeName) where TFactory : IStatefulServiceFactory
        {
            if (!container.IsRegistered<TFactory>())
            {
                container.RegisterType<TFactory>(new ContainerControlledLifetimeManager());
            }
            container.Resolve<FabricRuntime>().RegisterStatefulServiceFactory(serviceTypeName, container.Resolve<TFactory>());
            return container;
        }
        public static IUnityContainer WithService<TService>(this IUnityContainer container, string serviceTypeName) 
        {
            container.Resolve<FabricRuntime>().RegisterServiceType(serviceTypeName, typeof(TService));
            return container;
        }
    }
}
22

私はこれが古いことを知っていますが、ドキュメントのために、DIはあなたが期待するのと同じようにReliableActorフレームワークでサポートされるようになりました。

public class ActorOne : Actor<MyActorState>, IMyActor{

private readonly IDependency _dependency;

public ActorOne(IDependency dependency)
{
    _dependency = dependency;
}}

次に、次のように、依存関係を持つアクターをサービスファブリックに登録します。

using (FabricRuntime fRuntime = FabricRuntime.Create()){

fRuntime.RegisterActor(() => new ActorOne(new MyDependency());
Thread.Sleep(Timeout.Infinite);}
11
daustinash

Autofacを使用している場合は、そのための特定の統合パッケージがあります。

https://alexmg.com/introducing-the-autofac-integration-for-service-fabric/https://www.nuget.org/packages/Autofac.ServiceFabric/

つまり、登録はActorRuntime.RegisterActorAsync/ServiceRuntime.RegisterServiceAsyncを使用して実行されます。ただし、より問題のある部分、つまりオブジェクトの解放は、動的プロキシを使用してOnDeactivateAsync/OnCloseAsync/OnAbortオーバーライドで自動的に処理されます。適切なライフタイムスコープも維持されます。

これを書いている時点ではまだアルファ版です(先月リリースされたばかりです)。

1
Ohad Schneider

アクターでルート要素フィールドを使用し、アクターのコンストラクターに依存関係が挿入されたコンテナーから解決しないのはなぜですか?これが悪い決定である場合は、その理由を説明してください。

public class StatelessActor2 : Actor, IStatelessActor2
{
    private ConfiguredContainer _container;
    private IRootElement _rootElement;

    public StatelessActor2()

    {
        _container = new ConfiguredContainer(); //... container is configured in it's constructor
        _rootElement = _container.Resolve<IRootElement>();
    }

    public async Task<string> DoWorkAsync()
    {
        // Working with a RootElement with all dependencies are injected..
        return await Task.FromResult(_rootElement.WorkingWithInjectedStuff());
    }
}
1
AsValeO