2017-03-29 5 views
0

최근에 EMC AppXtender REST Services 8.1로 업그레이드되었습니다. 이것을 서버에 설치하면 Virtual Directory (AppXtenderRest)가 생성됩니다.대규모 요청에서 REST API 서버가 중단됨 (C# HttpClient 사용)

우리는이 서버에서 사용할 수있는 REST 서비스를 호출하여 웹 응용 프로그램을 개발했습니다.

우리가 개발 중일 때 REST 서버가 중단되지 않았습니다. 하지만 한 번 우리가 생산으로 옮기자 매달리기 시작했습니다. 이제 2-3 시간마다이 서버에서 IIS를 다시 설정하고 있습니다.

일부 연구를 마친 후 우리는 코드에서 다음 단계를 밟았습니다.

  1. 가 작동되어 1 ~ 4

Nothing으로 작업자 프로세스 헤아 렸어요 증가 REST 서버에서 30 분

  • async/await
  • 설정 HttpClient를 제한 시간에 우리의 모든 코드를 변환.

    특정 요청으로 인해 서버가 멈추는 지 확인하려고했으나 그렇게 보지 않았습니다. 모든 요청은 Stream (Tiff/Pdf)을 반환하는 것을 제외하고는 JSON을 반환합니다. 여기

    우리 REST 서비스로서 호출의 샘플이다 :

    using (var client = CreateHttpClient()) 
    { 
        using (var response = await client.DeleteAsync(string.Format(RestUrls.deletedoc, DataSource, AppId, docId), GetCancelToken())) 
        { 
         if (response.IsSuccessStatusCode) 
         { 
          result = await response.Content.ReadAsStringAsync(); 
         } 
         else 
         { 
          result = await response.Content.ReadAsStringAsync(); 
          throw new Exception(result); 
         } 
        } 
    } 
    
    또한

    요청이 일정 시간 후 걸려 도시 서버 작업자 프로세스 요청 큐 스크린을 부착 (2-3 시간 후)

    enter image description here

    정지 한 직후에 찍은 서버에서 디버그 분석 보고서를 첨부합니다.

    https://drive.google.com/open?id=0Bx6jnZk4gj2Ycmw2M1RKM3RiTzg

    우리가 지금 생산에있는 바와 같이, 자주 IIS 재설정을 감당할 수 없습니다.

  • 답변

    1

    TLDR - HTTP 클라이언트 연결 누출 수정 좋지만, 첫 번째 문제는 스레드를 차단하고 있습니다. 또한 민감한 데이터를 노출했을뿐입니다. 또한 iisreset 대신 항상 전체 응용 프로그램 풀 재활용으로 시작하여 전체 서버를 중단시키지 마십시오.

    위에서 언급했듯이 HTTPClient를 사용하여 TCP 연결을 유출했는데 다음 문제가 발생하기를 기다리는 확장 제한 항목이긴하지만 중요한 문제는 아닙니다.

    플러스 모든 TCP 포트를 다 써 버린 경우, 예외가 나타나면 중단이 아닌 더 명확하게 나타납니다.

    debugdiag 분석을 보면 문제가 동기화 SQL 호출로 다른 스레드의 40 %를 차단하고있는 것으로 보입니다. 결국 모든 작업자 스레드가 다른 블로킹 스레드에서 대기 중일 경우, 요청 큐가 가득 차서 503 서비스를 사용할 수 없게 될 때까지 요청이 대기열에 들어갑니다.

    The following threads in w3wp.exe__AppXtender Rest Services__PID__12056__Date__03_28_2017__Time_09_58_36AM__83__Manual Dump.dmp are waiting to enter a .NET Lock 
    
    
    (33 34 35 50 52 53 54 56 57 58 59 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107) 
    
    50.91% of threads blocked (56 threads) 
    

    그리고 그들은 소문에 대기중인 스레드를 SqlCommand.ExecuteReader

    실행 55 인은

    당신이 변경 (또는 해당 구성 요소의 소유자가 변경 수)

    Thread 55 - System ID 17820 
    
    
    
    Entry point clr!Thread::intermediateThreadProc 
    Create time 3/28/2017 9:51:46 AM 
    Time spent in user mode 0 Days 00:00:00.421 
    Time spent in kernel mode 0 Days 00:00:00.187 
    
    
    This thread is waiting on data to be returned from the database server 
    
    The current executing command is : SELECT cfgid, cfgvalue FROM ae_cfg WHERE cfgid = 34 and the command timeout is set to 0 seconds. 
    
    The connection string for this connection : *** and the connection timeout : 15 seconds. 
    
    
    
    
    
    .NET Call Stack 
    
    
    
    
    System_Data_ni!DomainNeutralILStubClass.IL_STUB_PInvoke(SNI_ConnWrapper*, SNI_Packet**, Int32)+84 
    [[InlinedCallFrame] (.SNIReadSyncOverAsync)] .SNIReadSyncOverAsync(SNI_ConnWrapper*, SNI_Packet**, Int32) 
    System_Data_ni!SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle, IntPtr ByRef, Int32)+6a 
    System_Data_ni!System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()+83 
    System_Data_ni!System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()+7e 
    System_Data_ni!System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()+65 
    System_Data_ni!System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte ByRef)+2e 
    System_Data_ni!System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior, System.Data.SqlClient.SqlCommand, System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.BulkCopySimpleResultSet, System.Data.SqlClient.TdsParserStateObject, Boolean ByRef)+292 
    System_Data_ni!System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()+5c 
    System_Data_ni!System.Data.SqlClient.SqlDataReader.get_MetaData()+66 
    System_Data_ni!System.Data.SqlClient.SqlCommand.FinishExecuteReader(System.Data.SqlClient.SqlDataReader, System.Data.SqlClient.RunBehavior, System.String)+11d 
    System_Data_ni!System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, Boolean, Int32, System.Threading.Tasks.Task ByRef, Boolean, System.Data.SqlClient.SqlDataReader, Boolean)+ba0 
    System_Data_ni!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String, System.Threading.Tasks.TaskCompletionSource`1, Int32, System.Threading.Tasks.Task ByRef, Boolean)+22a 
    System_Data_ni!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior, System.Data.SqlClient.RunBehavior, Boolean, System.String)+62 
    System_Data_ni!System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior, System.String)+ca 
    XtenderSolutions.UtilityLibrary.General.DbCommon.GetStringTypeFromDB(XtenderSolutions.Administration.Database.DbCommonEx)+1aa 
    XtenderSolutions.UtilityLibrary.General.DbCommon.Open()+11c 
    XtenderSolutions.CMData.CMConnection.Open()+a7 
    XtenderSolutions.CMData.CMCfgMgr.Load(XtenderSolutions.CMData.CMConnection, Int16)+55 
    XtenderSolutions.CMData.CMConnection.InitEAIHooks()+4f 
    XtenderSolutions.CMData.CMConnection.Init(System.String)+595 
    XtenderSolutions.CMData.CMConnection..ctor(XtenderSolutions.CMData.CMSession, System.String)+17b 
    XtenderSolutions.CMData.CMSession.get_Connection()+7e 
    XtenderSolutions.CMData.CMSession.Login(XtenderSolutions.Configuration.DataSourceConfig, System.String, System.String, System.Security.Principal.WindowsIdentity, System.String, Boolean)+46e 
    
    해야하는 async version - ExecuteReaderAsync있다

    또한 은 공유하기 전에 debugdiag 공유를 제거하거나 적어도 공유에서 중요한 데이터를 제거하고 변경 사항을 적용하려면을 강력하게 추천합니다. NT 암호.

    힌트 : Basic Auth headers -> 64 기수 -> 일반 텍스트 사용자 : PWD

    마지막으로 IISReset을이 :

    당신이 채워 HTTP.SYS 요청 큐의 단계에서 아직하지 않은 경우, 당신은 또한 응용 프로그램을 시도 할 수 있습니다 풀 리사이클링은 새로운 w3wp.exe 작업자 프로세스를 제공하거나 현재 중지 요청을 기다리지 않으려는 경우에도 풀을 중지/시작합니다. 풀 재활용은 전체 IIS 서버를 중단시키는 데 덜 관입입니다. 그러나 http.sys 큐에서 많은 요청을 받자 마자 iisreset이 필요할 수 있습니다. 나는 항상 그 호스트에 다른 사이트/vdirs가있는 경우 iisreset을 피하려고 노력합니다 ... IIS perf counters을 모니터링하고이를 기반으로 결정할 수 있습니다.

    +0

    감사합니다. 정적 HttpClient를 사용하는 수정 된 코드와 db 요청을 차례로 보내기 위해 SQL Server에 데이터를 쓰는 몇 가지 메소드에 semaphoreslim을 추가했습니다. 앱 풀 중단은 어느 정도 감소했지만 여전히 발생합니다. 당신은 시사 포림이 어떤 영향을 미칠 것이라고 생각합니까? 또한 요청이 생성되어 다른 서버 (단일)로 전송되어 처리되도록하는 웹 팜 (3 서버)이 있습니다. 이것이 교수형과 관련이 있습니까? –

    1

    포트 고갈에 대해 살펴보십시오. 당신이보고있는 것을 설명 할 수 있습니다. 내가 믿는 때문에 HttpClient를 완료하고 연결이 배치되어

    You're using HttpClient wrong - Simon Timms

    후에도 (미확인) 포트를 기다리고에 - 트래픽이 너무 무거운 아니었지만 대기 시간이 증가했다 어디 비슷한 보았다 TIME_WAIT '에 열린 상태로, 네트워크에서 어딘가에서 지연되었을 수 있으므로 추가 패킷이 들어 왔는지 기다리는 중입니다.' Windows는 기본적으로이 상태에서 240 초 동안 연결을 유지합니다. 이것에 대한 해답은 관심있는 도메인 당 HttpClient를 보유하는 것입니다. 응용 프로그램과 관련하여 (적절한 오류 처리 등을 통해) 오래 살아야합니다.

    최근에 확인 된 문제는 HttpClient가 다시 연결될 때까지 DNS 변경 사항이 적용되지 않는다는 것입니다 (see here [byterot - aliostad]). (나는이 기사가이 질문을 발견 한 곳이라는 것을 당신이 알았다는 것을 알고 있습니다!) 여기에는 몇 가지 해결책이 있습니다.

    var client = new HttpClient(); client.DefaultRequestHeaders.ConnectionClose = true;

    또는

    var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab")); sp.ConnectionLeaseTimeout = 60*1000;

    +0

    이 링크가 질문에 대답 할 수 있지만, 답변의 일부를 참조 용으로 제공하십시오. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않게 될 수 있습니다. - [검토 중] (리뷰/저품절 포스트/16018386) –

    +0

    감사합니다. Nahuel, 업데이트했습니다. – sij

    +0

    제안 된 기사에 따라 코드 1을 변경했습니다. HttpClient를 정적으로 설정합니다. 2. 명시 적으로 특정 동작 (예 : 서비스 실패)을 찾지 않는 한 HttpClient를 사용하여 처리하거나 래핑하지 마십시오. 정지 요청이 교수형에 처해 있습니다. –