首页 > 解决方案 > c# microsoft graph api getAsync() 中的 windows 服务调用用户进入挂起状态

问题描述

我正在使用 MS graph api 使用线程读取 Windows 服务中的不同邮箱。服务运行好几天,但之后进入挂起状态。

通过观察日志发现卡点是在为用户GetAsync()调用方法时,之后它也没有更新日志文件但服务显示为运行状态。

重新启动服务后,它会正常运行几天。

public static async Task MainAsync()
        {
            Task t1 = ParallelThreadOne.MainAsync();
            Task t2 = ParallelThreadSecond.MainAsync();
            Task t3 = ParallelThreadThird.MainAsync();            
            
            await Task.WhenAll(t1, t2, t3);

        }

 public static async Task MainAsync()
        {

            try
            {                
                EmailMaster objEmailMaster = new EmailMaster();

                List<MailBoxConfiguration> lstMailBoxConfiguration = objEmailMaster.GetMailBoxInformation(1,logger);
                if (lstMailBoxConfiguration != null)
                {
                    if (lstMailBoxConfiguration.Count != 0)
                    {                        
                        GraphServiceClient client = GetAuthenticatedClient();
                       
                        if (client != null)
                        {
                            for (int j = 0; j < lstMailBoxConfiguration.Count; j++)
                            {                                
                                var users = await graphClient
                                .Users
                                .Request()
                                .Filter("startswith(Mail,'" + lstMailBoxConfiguration[j].EmailId + "')")
                                .GetAsync();                               
                                
                                if (users.Count > 0)
                                {                                   
                                    var msgs = await graphClient
                                           .Users[users[0].Id]
                                           .MailFolders["Inbox"].Messages
                                           .Request().Top(500)
                                           .GetAsync();
                                     
                                        if (msgs.Count > 0)
                                        {
                                            foreach (var item in msgs)
                                            {                                                
                                                //business logic goes here                                                
                                            }
                                        }
                                        else
                                        {
                                            logger.Info("msg.Count is zero");
                                        }
                                }
                                else
                                {
                                    logger.Info("users.Count is zero");
                                }
                            }
                        }
                        else
                        {
                            logger.Info("client is null");
                        }
                    }
                    else
                    {
                        logger.Info("lstMailBoxConfiguration.Count is zero from the database");
                    }
                }
                else
                {
                    logger.Info("lstMailBoxConfiguration is null from the database");
                }
                logger.Info("MainAsync(1) : End of MainAsync(1)");
            }
            catch (Exception ex)
            {
                logger.Error("MainAsync(1) : Exception : " + ex.Message);
            }

        }
        public static GraphServiceClient GetAuthenticatedClient()
        {
            
            string clientId = ConfigurationManager.AppSettings["AzureClientId"];
            string password = ConfigurationManager.AppSettings["password"];           
            string tenantId = ConfigurationManager.AppSettings["tenantId"];
            string getTokenUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";           
            const string grantType = "client_credentials";
            const string myScopes = "https://graph.microsoft.com/.default"; 
            string postBody = $"client_id={clientId}&scope={myScopes}&client_secret={password}&grant_type={grantType}";
            try
            {                             
                if (graphClient == null)
                {
                    graphClient = new GraphServiceClient(

                      "https://graph.microsoft.com/v1.0",
                        new DelegateAuthenticationProvider(
                            async (requestMessage) =>
                            {                         
                            HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, getTokenUrl);
                                httpRequestMessage.Content = new StringContent(postBody, Encoding.UTF8, "application/x-www-form-urlencoded");                         
                                HttpResponseMessage httpResponseMessage = await client.SendAsync(httpRequestMessage);
                            string responseBody = await httpResponseMessage.Content.ReadAsStringAsync();
                                userToken = JObject.Parse(responseBody).GetValue("access_token").ToString();
                                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", userToken);
                            }));
                }
                return graphClient;
            }
            catch (Exception ex)
            {
                logger.Error("Could not create a graph client: " + ex.Message);
            }
            finally
            {
                logger.Info("GetAuthenticatedClient() :inside finally!");   
            }

            return graphClient;
        }

标签: c#async-awaitmicrosoft-graph-apiwindows-services

解决方案


通过查看 的源代码GraphServiceClient,它被HTTPClient用作其底层通信提供者。有一个问题,因为在HTTPClient处理后,Windows 会在一定时间内保持 TCP/IP 连接打开HTTPClient。如果你调用new然后disposeHTTPClient课堂上足够快足够长的时间,它可能会导致套接字饥饿。(注意,当实例超出范围时,会在后台using(var client = new HTTPClient())调用)dispose

看看这个关于这个问题的博客文章。 https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

GraphServiceClient只要您与同一个 GraphQL 端点通信并解决服务挂起的问题,您就应该能够使用 的单个实例。如果您添加日志记录,您可能会注意到服务在一系列活动后挂起,导致GraphServiceClient创建大量新实例,然后在短时间内处理,并且服务器上的开放网络连接爆炸导致错误导致其中一个崩溃你的线程。


推荐阅读