2012-04-13 34 views
16

Tôi có thư viện mà tôi sử dụng để sử dụng WCF để gọi dịch vụ http để nhận cài đặt. Thông thường, cuộc gọi đầu tiên mất ~ 100 mili giây và các cuộc gọi tiếp theo chỉ mất vài phần nghìn giây. Nhưng tôi đã thấy rằng khi tôi tạo một AppDomain mới, cuộc gọi WCF đầu tiên từ AppDomain đó mất hơn 2,5 giây.Kết nối WCF đầu tiên được tạo trong AppDomain mới rất chậm

Có ai có giải thích hoặc khắc phục vì sao việc tạo kênh WCF đầu tiên trong một AppDomain mới sẽ mất nhiều thời gian?

Đây là kết quả benchmark (Khi chạy mà không cần gỡ lỗi kèm theo trong phiên bản trong 64bit), hãy chú ý cách trong tập thứ hai của số các kết nối đầu tiên tiếp quản 25x còn

Running in initial AppDomain 
First Connection: 92.5018 ms 
Second Connection: 2.6393 ms 

Running in new AppDomain 
First Connection: 2457.8653 ms 
Second Connection: 4.2627 ms 

Đây không phải là một ví dụ hoàn chỉnh nhưng cho thấy hầu hết làm thế nào tôi sản xuất những con số:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Running in initial AppDomain"); 
     new DomainRunner().Run(); 

     Console.WriteLine(); 
     Console.WriteLine("Running in new thread and AppDomain"); 
     DomainRunner.RunInNewAppDomain("test"); 

     Console.ReadLine(); 
    } 
} 

class DomainRunner : MarshalByRefObject 
{ 
    public static void RunInNewAppDomain(string runnerName) 
    { 
     var newAppDomain = AppDomain.CreateDomain(runnerName); 
     var runnerProxy = (DomainRunner)newAppDomain.CreateInstanceAndUnwrap(typeof(DomainRunner).Assembly.FullName, typeof(DomainRunner).FullName); 

     runnerProxy.Run(); 
    } 

    public void Run() 
    { 
     AppServSettings.InitSettingLevel(SettingLevel.Production); 
     var test = string.Empty; 

     var sw = Stopwatch.StartNew(); 
     test += AppServSettings.ServiceBaseUrlBatch; 
     Console.WriteLine("First Connection: {0}", sw.Elapsed.TotalMilliseconds); 

     sw = Stopwatch.StartNew(); 
     test += AppServSettings.ServiceBaseUrlBatch; 
     Console.WriteLine("Second Connection: {0}", sw.Elapsed.TotalMilliseconds); 
    } 
} 

các cuộc gọi đến AppServSettings.ServiceBaseUrlBatch đang tạo ra một kênh để một dịch vụ và gọi một phương thức duy nhất. Tôi đã sử dụng wireshark để xem cuộc gọi và chỉ mất một phần nghìn giây để nhận phản hồi từ dịch vụ. Nó tạo ra các kênh với đoạn mã sau:

public static ISettingsChannel GetClient() 
{ 
    EndpointAddress address = new EndpointAddress(SETTINGS_SERVICE_URL); 

    BasicHttpBinding binding = new BasicHttpBinding 
    { 
     MaxReceivedMessageSize = 1024, 
     OpenTimeout = TimeSpan.FromSeconds(2), 
     SendTimeout = TimeSpan.FromSeconds(5), 
     ReceiveTimeout = TimeSpan.FromSeconds(5), 
     ReaderQuotas = { MaxStringContentLength = 1024}, 
     UseDefaultWebProxy = false, 
    }; 

    cf = new ChannelFactory<ISettingsChannel>(binding, address); 

    return cf.CreateChannel(); 
} 

Từ profiling ứng dụng nó cho thấy rằng trong trường hợp đầu tiên xây dựng nhà máy sản xuất kênh và tạo kênh và gọi phương pháp này mất chưa đến 100 mili giây

Trong AppDomain mới xây dựng nhà máy kênh mất 763 mili giây, 521 mili giây để tạo kênh, 1.098 mili giây để gọi phương thức trên giao diện.

TestSettingsRepoInAppDomain.DomainRunner.Run() 2,660.00 TestSettingsRepoInAppDomain.AppServSettings.get_ServiceBaseUrlBatch() 2,543.47 Tps.Core.Settings.Retriever.GetSetting (string, !! 0, 0 !!, !! 0) 2,542.66 TPS. Core.Settings.Retriever.TryGetSetting (chuỗi, !! 0 &) 2.522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient() 1,371.21 Tps.Core.Settings.ServiceModel.IClientChannelExtensions.CallWithRetry (class System.ServiceModel.IClientChannel) 1.098.83

EDIT

Sau khi sử dụng perfmon với đối tượng .NET CLR Loading, tôi có thể thấy rằng khi nó tải AppDomain thứ hai, nó sẽ nạp nhiều lớp vào bộ nhớ hơn là ban đầu. Đường thẳng đầu tiên là một tạm dừng mà tôi đặt vào sau appdomain đầu tiên, ở đó nó có 218 lớp được nạp. AppDomain thứ hai làm cho tổng số 1.944 lớp được nạp.

Tôi cho rằng quá trình tải tất cả các lớp này luôn chiếm hết thời gian, do đó, bây giờ câu hỏi là, lớp nào đang tải và tại sao?

enter image description here

CẬP NHẬT

Câu trả lời hóa ra là vì thực tế rằng chỉ có một AppDomain có thể tận dụng lợi thế của hệ thống dlls native image. Vì vậy, sự chậm chạp trong appdomain thứ hai là nó phải rejit tất cả các hệ thống. * Dlls được sử dụng bởi wcf. Tên miền ứng dụng đầu tiên có thể sử dụng các phiên bản gốc được phát triển trước của các dll đó, vì vậy nó không có cùng chi phí khởi động.

Sau khi điều tra các LoaderOptimizationAttribute rằng Petar đề nghị, mà thực sự dường như để sửa chữa vấn đề này, sử dụng một trong hai MultiDomain or MultiDomainHost kết quả trong AppDomain thứ hai để lấy cùng một lượng thời gian như lần đầu tiên để truy cập nội dung trên WCF

Ở đây bạn có thể thấy tùy chọn mặc định, lưu ý như thế nào trong không AppDomain thứ hai của hội nói Native, có nghĩa là tất cả họ đều phải được rejitted, đó là những gì đã được tham gia tất cả các thời gian

enter image description here

đây là sau khi thêm LoaderOptimiza tion (LoaderOptimization.MultiDomain) đến Chính. Bạn có thể thấy rằng tất cả mọi thứ được nạp vào AppDomain chia sẻ

enter image description here

Đây là sau khi người dùng LoaderOptimization (LoaderOptimization.MultiDomainHost) to main. Bạn có thể thấy rằng tất cả dlls hệ thống được chia sẻ, nhưng dlls của riêng tôi và bất kỳ không nằm trong GAC được nạp riêng rẽ vào mỗi AppDomain

enter image description here

Vì vậy cho dịch vụ mà nhắc câu hỏi này sử dụng MultiDomainHost là câu trả lời, bởi vì nó có thời gian khởi động nhanh và tôi có thể dỡ bỏ AppDomains để loại bỏ các cụm động được xây dựng mà dịch vụ sử dụng

Trả lời

9

Bạn có thể trang trí Chính bằng thuộc tính LoaderOptimization để báo cho trình tải CLR biết cách tải lớp.

[LoaderOptimization(LoaderOptimization.MultiDomain)] 
MultiDomain - Indicates that the application will probably have many domains that use the same code, and the loader must share maximal internal resources across application domains. 
+1

Cảm ơn, thêm điều đó thực sự làm giảm thời gian trong AppDomain mới là 35 ms cho lần đầu tiên và 2ms cho thứ hai, nhưng điều này sẽ ngăn chặn việc dỡ bỏ bất kỳ assembly nào được nạp vào AppDomain thứ hai? Một lý do mà ứng dụng sử dụng AppDomains là vì nó là một dịch vụ chạy dài cần tải mã tùy chỉnh để thực hiện một hành động, vì vậy tôi vẫn cần có khả năng dỡ bỏ một số assembly để kích thước của dịch vụ không phát triển . – BrandonAGr

+1

Cài đặt này không được ngăn việc dỡ các hội đồng từ AppDomain thứ hai. –

+0

Ứng dụng nào đang tạo ảnh chụp màn hình? Sysinternals công cụ? –

1

Bạn có proxy HTTP được xác định trong IE không? (có thể là một kịch bản cấu hình tự động). Điều này có thể là một nguyên nhân.

Nếu không, tôi đoán đó là thời gian cần để tải tất cả các dll. Cố gắng deparate tạo proxy từ các cuộc gọi actull đến dịch vụ, để xem những gì đang dành thời gian.

+0

Trước đây tôi đã kiểm tra cài đặt proxy và tôi không nghĩ vậy. Tôi cũng nghi ngờ của nó tải dlls bởi vì nếu nó được thì tại sao sẽ không phải là nỗ lực wcf đầu tiên nhìn thấy sự chậm trễ tương tự như từ AppDomain mới? Một điều tôi đã thông báo là trong khi nó đã tạo ra AppDomain mới, việc sử dụng CPU đã được tối đa cho lõi đó trong 2,5 giây – BrandonAGr

1

Tôi tìm thấy số following article nói về cách AppDomain đầu tiên có thể sử dụng tệp hình ảnh gốc, vì vậy appdomain con sẽ luôn bị buộc phải JIT rất nhiều thứ mà AppDomain ban đầu không phải làm. Điều này có thể dẫn đến tác động biểu diễn mà tôi đang thấy, nhưng liệu có thể nào đó không thể nhận được hình phạt về hiệu suất này?

Nếu có hình ảnh gốc cho hội đồng, chỉ AppDomain đầu tiên mới có thể sử dụng hình ảnh gốc. Tất cả các AppDomain khác sẽ phải JIT biên dịch mã có thể dẫn đến chi phí CPU đáng kể.

Các vấn đề liên quan