Azure 函数在 "stress test" 中抛出 SimpleInjector.ActivationException

Azure Function throws SimpleInjector.ActivationException in "stress test"

我们在一个基于消费计划的函数应用程序中有几个函数。当我使用 JMeter(同时 200 个线程)对这些函数进行压力测试时,这些函数在大约 50% 的时间内抛出 SimpleInjector.ActivationException。并非所有请求都失败。

我不明白为什么这只是部分请求。

调用堆栈:

2021-07-06T08:43:41.159 [错误] Er is een probleem opgetreden

SimpleInjector.ActivationException : GetLocalKeyDataByDateQueryHandler 类型的构造函数包含名称为 'queryValidator' 且类型为 IValidator 的参数,但 IValidator 未注册。要解析 IValidator,必须在 container.at SimpleInjector.Container.ThrowParameterTypeMustBeRegistered(InjectionTargetInfo target)at SimpleInjector.Advanced.DefaultDependencyInjectionBehavior.GetInstanceProducer(InjectionConsumerInfo dependency,Boolean throwOnFailure)at SimpleInjector.ContainerOptions.GetInstanceProducerFor(InjectionConsumerInfo consumer)at 注册SimpleInjector.Registration.BuildConstructorParameters(ConstructorInfo constructor)at SimpleInjector.Registration.BuildNewExpression()at SimpleInjector.Registration.BuildTransientExpression()at SimpleInjector.Registration.BuildTransientDelegate()at SimpleInjector.Lifestyles.ScopedRegistration.BuildExpression()at SimpleInjector.InstanceProducer.BuildExpressionInternal()at SimpleInjector.Internals.LazyEx`1.InitializeAndReturn() 在 SimpleInjector.InstanceProducer.BuildInstanceCreator() 在 SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance() 在 SimpleInjector.InstanceProducer.GetInstance() 在 SimpleInjector.Container.GetInstanceTServiceat 异步 ProRail。PUIC2.Web.Functions.GetPuicDataByChangeDate.Run(GetPuicDataByDate inputParams、ILogger 记录器、ExecutionContext 上下文) 在 D:\a\s\Src\Web\ProRail.PUIC2.Web.Functions\Functions\GetPuicDataByChangeDate.cs : 35

函数:

    public class GetPuicDataByLocalKey
    {
        [FunctionName("GetPuicDataByLocalKey")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]
            Models.GetPuicDataByLocalKey queryInput, ILogger logger, ExecutionContext context)
        {
            var container = DependencyInjection.GetContainerInstance(context);

            await using (AsyncScopedLifestyle.BeginScope(container))
            {
                var loggingUtils = container.GetInstance<ILoggingUtils>();
                try
                {
                    logger.LogTrace(loggingUtils.MethodBegin);

                    container.GetInstance<Configuration.ILoggerFactory>().SetLogger(logger);
                    var queryHandler = container.GetInstance<IQueryHandler<GetPuicDataQuery, IEnumerable<PuicData>>>();
                    var results = await queryHandler.Handle(new GetPuicDataQuery
                    {
                        Source = queryInput.Source,
                        DateValid = queryInput.DateValid,
                        LocalKeyValues = queryInput.LocalKeyValues.Select(lkv => new Core.Entities.Data.KeyValue
                        {
                            Key = lkv.Key,
                            Value = lkv.Value
                        })
                    });

                    if (results == null)
                    {
                        return new JsonResult(Enumerable.Empty<Models.PuicData>());
                    }

                    // Get local keys grouped by Puic
                    var puicDataList = results.GroupBy(l => l.Puic).Select(g => PuicDataConverter.Convert(g));

                    return new JsonResult(puicDataList);
                }
                catch (PuicException e)
                {
                    logger.LogError(e, "Data is waarschijnlijk niet goed");
                    return new BadRequestErrorMessageResult(e.Message);
                }
                catch (Exception e)
                {
                    logger.LogError(e, "Er is een probleem opgetreden");
                    return new InternalServerErrorResult();
                }
                finally
                {
                    logger.LogTrace(loggingUtils.MethodEnd);
                }
            }
        }
    }

容器配置


    public static class DependencyInjection
    {
        private static Container containerInstance = null;

        public static Container GetContainerInstance(ExecutionContext context)
        {
            if (containerInstance == null)
            {
                containerInstance = new Container();
                containerInstance.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

                InitializeConfiguration(containerInstance, context);
                RegisterSimpleTypes(containerInstance);
                RegisterQueryHandlers(containerInstance);
                RegisterCommandHandlers(containerInstance);
                RegisterValidators(containerInstance);
                RegisterDatabaseTypes(containerInstance);
            }

            return containerInstance;
        }

[...]

        private static void RegisterQueryHandlers(Container container)
        {
            var businessLogicAssembly = typeof(GetUsersQuery).Assembly;
            container.Register(typeof(IQueryHandler<,>), businessLogicAssembly, Lifestyle.Scoped);
        }

[...]

    }

您的代码似乎存在多线程问题; DependencyInjection.GetContainerInstance 方法。

GetContainerInstance 可能会在并行调用时创建多个 Container 实例,最终 Container 实例的配置将不确定。

要解决此问题,您必须同步容器的创建。有很多方法可以做到这一点,但您可以试试这个:

public static Container GetContainerInstance(ExecutionContext context)
{
    // Double-checked lock. Ensures only one Container instance
    // is created.
    if (containerInstance is null)
    {
        lock (typeof(DependencyInjection))
        {
            if (containerInstance is null)
            {
                containerInstance = BuildContainer();
            }
        }
    }

    return containerInstance;
}

private static Container BuildContainer()
{
    var container = new Container();
    container.Options.DefaultScopedLifestyle =
        new AsyncScopedLifestyle();

    InitializeConfiguration(container, context);
    RegisterSimpleTypes(container);
    RegisterQueryHandlers(container);
    RegisterCommandHandlers(container);
    RegisterValidators(container);
    RegisterDatabaseTypes(container);

    return container;
}