最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - How to access a durable entity from a DelegatingHandler? - Stack Overflow

programmeradmin10浏览0评论

I have a durable functions orchestration, where I need to rate-limit the requests to an external API. There are a bunch of tasks in the orchestrator that invoke a 'DownloadData' function with different parameters, but I couldn't figure out how to limit the requests to the external service globally, for all instances of 'DownloadData'. I figured I'd try using a Durable Entity to keep track of the remaining calls within a certain timeframe.

Here is the DelegatingHandler I use for authentication and was hoping to also use for rate-limiting via the durable entity, however the app crashes as it cannot set the DurableTaskClient via DI and I don't know how to fix this.

public class AuthenticationHandler : DelegatingHandler
{
    private readonly IKeyVaultService _keyVaultService;
    private readonly ILogger<AuthenticationHandler> _logger;
    private readonly DurableTaskClient _durableClient;

    public AuthenticationHandler(IKeyVaultService keyVaultService, ILogger<AuthenticationHandler> logger, DurableTaskClient durableClient)
    {
        _keyVaultService = keyVaultService ?? throw new ArgumentNullException(nameof(keyVaultService));
        _logger = logger;
        _durableClient = durableClient;
    }
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {       
        string token = await _keyVaultService.GetAccessTokenAsync();
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var entityId = new EntityInstanceId(nameof(RateLimiterEntity), "RateLimiter");
        EntityMetadata<RateLimiterEntity>? entity = await _durableClient.Entities.GetEntityAsync<RateLimiterEntity>(entityId, cancellation: cancellationToken);

        _logger.LogInformation("Remaining requests: {rem}", entity.State.RemainingRequests.ToString());

        if (entity != null && entity.State.RemainingRequests <= 25)
        {
            var delay = entity.State.ResetTime - DateTime.UtcNow;
            if (delay > TimeSpan.Zero)
            {
                _logger.LogWarning("Rate limit reached. Delaying for {Delay} seconds", delay.TotalSeconds);
                await Task.Delay(delay, cancellationToken);
            }
            await _durableClient.Entities.SignalEntityAsync(entityId, nameof(RateLimiterEntity.Reset), cancellationToken);
        }
        await _durableClient.Entities.SignalEntityAsync(entityId, nameof(RateLimiterEntity.Decrement), cancellationToken);
    
        var response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}

Here is the durable entity (isolated, .NET 8):

[DurableTask(nameof(RateLimiterEntity))]
public class RateLimiterEntity
{
    public int RemainingRequests { get; set; } = 100;
    public DateTime ResetTime { get; set; } = DateTime.UtcNow.AddSeconds(60);

    public void Reset()
    {
        RemainingRequests = 100;
        ResetTime = DateTime.UtcNow.AddSeconds(60);
    }

    public void Decrement()
    {
        RemainingRequests--;
    }

    [Function(nameof(RateLimiterEntity))]
    public static Task Run([EntityTrigger] TaskEntityDispatcher dispatcher)
        => dispatcher.DispatchAsync<RateLimiterEntity>();
}

So how to properly register the DurableTaskClient and access the entity from within the DelegatingHandler?

发布评论

评论列表(0)

  1. 暂无评论