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?
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
đâ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ẻ
Đâ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
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
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
Cài đặt này không được ngăn việc dỡ các hội đồng từ AppDomain thứ hai. –
Ứng dụng nào đang tạo ảnh chụp màn hình? Sysinternals công cụ? –