首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >.net - api调用阻塞了其他api调用。

.net - api调用阻塞了其他api调用。
EN

Stack Overflow用户
提问于 2021-03-01 21:55:17
回答 1查看 239关注 0票数 0

我有一个问题,端点阻塞调用从我的应用程序的其他端点。当我们调用这个端点时,这基本上阻止了所有其他api调用的执行,它们需要等待直到完成。

代码语言:javascript
复制
       public async Task<ActionResult> GrantAccesstoUsers()
      {
         // other operations
         var grantResult = await 
         this._workSpaceProvider.GrantUserAccessAsync(this.CurrentUser.Id).ConfigureAwait(false);

         return this.Ok(result);
     }

GrantUserAccessAsync方法调用将在并行上运行的一组任务。

代码语言:javascript
复制
        public async Task<List<WorkspaceDetail>> GrantUserAccessAsync(string currentUser)
        {
            var responselist = new List<WorkspaceDetail>();

            try
            {
                // calling these prematurely to be reused once threads are created
// none expensive calls
                var properlyNamedWorkSpaces = await this._helper.GetProperlyNamedWorkspacesAsync(true).ConfigureAwait(false);
                var dbGroups = await this._reportCatalogProvider.GetWorkspaceFromCatalog().ConfigureAwait(false);
                var catalogInfo = await this._clientServiceHelper.GetDatabaseConfigurationAsync("our-service").ConfigureAwait(false);

                if (properlyNamedWorkSpaces != null && properlyNamedWorkSpaces.Count > 0)
                {
// these methods returns tasks for parallel processing
                    var grantUserContributorAccessTaskList = await this.GrantUserContributorAccessTaskList(properlyNamedWorkSpaces, currentUser, dbGroups, catalogInfo).ConfigureAwait(false);
                    var grantUserAdminAccessTaskList = await this.GrantUserAdminAccessTaskList(properlyNamedWorkSpaces, currentUser, dbGroups, catalogInfo).ConfigureAwait(false);
                    var removeInvalidUserAndSPNTaskList = await this.RemoveAccessRightsToWorkspaceTaskList(properlyNamedWorkSpaces, dbGroups, currentUser, catalogInfo).ConfigureAwait(false);

                    var tasklist = new List<Task<WorkspaceDetail>>();
                    tasklist.AddRange(grantUserContributorAccessTaskList);
                    tasklist.AddRange(grantUserAdminAccessTaskList);
                    tasklist.AddRange(removeInvalidUserAndSPNTaskList);

                    // Start running Parallel Task
                    Parallel.ForEach(tasklist, task =>
                    {
                        Task.Delay(this._config.CurrentValue.PacingDelay);
                        task.Start();
                    });

                    // Get All Client Worspace Processing Results
                    var clientWorkspaceProcessingResult = await Task.WhenAll(tasklist).ConfigureAwait(false);

                    // Populate result
                    responselist.AddRange(clientWorkspaceProcessingResult.ToList());
                }
            }
            catch (Exception)
            {
                throw;
            }

            return responselist;
        }

这些方法在结构上基本相同,它们如下所示:

代码语言:javascript
复制
private async Task<List<Task<WorkspaceDetail>>> GrantUserContributorAccessTaskList(List<Group> workspaces, string currentUser, List<WorkspaceManagement> dbGroups, DatabaseConfig catalogInfo)
        {
            var tasklist = new List<Task<WorkspaceDetail>>();

            foreach (var workspace in workspaces)
            {
                tasklist.Add(new Task<WorkspaceDetail>(() =>
                this.GrantContributorAccessToUsers(workspace, currentUser, dbGroups, catalogInfo).Result));
                // i added a delay here because we encountered an issue before in production and this seems to solve the problem. this is set to 4ms.
                Task.Delay(this._config.CurrentValue.DelayInMiliseconds);
            }

            return tasklist;
        }

这里调用的其他方法如下所示:

代码语言:javascript
复制
private async Task<WorkspaceDetail> GrantContributorAccessToUsers(Group workspace, string currentUser, List<Data.ReportCatalogDB.WorkspaceManagement> dbGroups, DatabaseConfig catalogInfo)
        {
            // This prevents other thread or task to start and prevents exceeding the number of threads allowed
            await this._batchProcessor.WaitAsync().ConfigureAwait(false);

            var result = new WorkspaceDetail();

            try
            {
                
                var contributorAccessresult = await this.helper.GrantContributorAccessToUsersAsync(workspace, this._powerBIConfig.CurrentValue.SPNUsers).ConfigureAwait(false);

                if (contributorAccessresult != null 
                    && contributorAccessresult.Count > 0)
                {
                    // do something
                }
                else
                {
                    // do something
                }

// this is done to reuse the call that is being executed in the helper above. it's an expensive call from an external endpoint so we opted to reuse what was used in the initial call, instead of calling it again for this process
                var syncWorkspaceAccessToDb = await this.SyncWorkspaceAccessAsync(currentUser, workspace.Id, contributorAccessresult, dbGroups, catalogInfo).ConfigureAwait(false);

                foreach (var dbResponse in syncWorkspaceAccessToDb) {

                    result.ResponseMessage += dbResponse.ResponseMessage;
                }
            }
            catch (Exception ex)
            {
                this._loghelper.LogEvent(this._logger, logEvent, OperationType.GrantContributorAccessToWorkspaceManager, LogEventStatus.FAIL);

            }
            finally
            {
                this._batchProcessor.Release();
            }

            return result;
        }

最后一个名为将记录写入数据库表的方法是:

代码语言:javascript
复制
private async Task<List<WorkspaceDetail>> SyncWorkspaceAccessAsync(string currentUser,
            Guid workspaceId,
            List<GroupUser> groupUsers,
            List<WorkspaceManagement> dbGroups,
            DatabaseConfig catalogInfo) {

            var result = new List<WorkspaceDetail>();

            var tasklist = new List<Task<WorkspaceDetail>>();

            // get active workspace details from the db
            var workspace = dbGroups.Where(x => x.PowerBIGroupId == workspaceId).FirstOrDefault();

            try
            {
                // to auto dispose the provider, we are creating this for each instance because 
                // having only one instance creates an error when the other task starts running
                using (var contextProvider = this._contextFactory.GetReportCatalogProvider(
                        catalogInfo.Server,
                        catalogInfo.Database,
                        catalogInfo.Username,
                        catalogInfo.Password,
                        this._dbPolicy))
                {
                    if (workspace != null)
                    {
                        // get current group users in the db from the workspace object
                        var currentDbGroupUsers = workspace.WorkspaceAccess.Where(w => w.Id == workspace.Id
                                                                                && w.IsDeleted == false).ToList();
                        #region identify to process

                        #region users to add
                        // identify users to add
                        var usersToAdd = groupUsers.Where(g => !currentDbGroupUsers.Any(w => w.Id == workspace.Id ))
                                                                    .Select(g => new WorkspaceAccess
                                                                    {

                                                                        // class properties

                                                                    }).ToList();
                        #endregion

                        var addTasks = await this.AddWorkspaceAccessToDbTask(catalogProvider, usersToAdd, workspace.PowerBIGroupId, workspace.WorkspaceName).ConfigureAwait(false);
                        
                        tasklist.AddRange(addTasks);

                        // this is a potential fix that i did, hoping adding another parallel thread can solve the problem
                        Parallel.ForEach(tasklist, new ParallelOptions { MaxDegreeOfParallelism = this._config.CurrentValue.MaxDegreeOfParallelism }, task =>
                        {
                            Task.Delay(this._config.CurrentValue.PacingDelay);
                            task.Start();
                        });

                        var processResult = await Task.WhenAll(tasklist).ConfigureAwait(false);

                        // Populate result
                        result.AddRange(processResult.ToList());

                    } 
                }

            }
            catch (Exception ex)
            {
                // handle error
            }

            return result;
        }

我已经尝试了一些可能的解决方案,比如这里的方法是以前用Task.FromResult编写的,而不是用异步编写的,所以我改变了它。引用来自此线程:

Using Task.FromResult v/s await in C#

此外,我认为这是一个类似的问题,我们在运行多个并行任务时需要创建多个db上下文连接,增加了对任务的小延迟,但这并没有解决问题。

代码语言:javascript
复制
Task.Delay(this._config.CurrentValue.DelayInMiliseconds);

任何帮助都将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-01 22:51:29

我假设您的this._batchProcessorSemaphoreSlim的一个实例。如果您的其他端点以某种方式调用

代码语言:javascript
复制
await this._batchProcessor.WaitAsyc()

这意味着他们不能走得更远,直到信号量将被释放。

还有一件事我想提一提:请避免使用带有异步/等待的Parallel.ForEach。TPL不是为使用异步/等待而设计的,下面是您应该避免同时使用它们的好答案:Nesting await in Parallel.ForEach

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66430255

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档