multithreading - 多线程 DirectoryEntry 搜索完成缓慢
问题描述
我有大约 50,000 个 ID 需要在 AD 中查找,因此我使用 TPL 设置了 10 个线程来同时处理列表。浏览这些 ID 并获取所需信息需要 5 个多小时,如下所示。知道为什么吗?这是正常的还是我的代码放慢了速度?
我测试过,一个 ID 可能需要 3-4 秒到有时 70 或 80 秒来完成任何给定 ID 的迭代。
谢谢
maxThreads = 10;
int hdCount = UserProfileDictionary.Count;
int completedCount = 0;
foreach (var intKey in UserProfileDictionary.Keys.ToList())
{
String ID = intKey;
MyTasks.Add(System.Threading.Tasks.Task.Factory.StartNew(() => GetADInfo(ID, ref UserProfileDictionary, supportedOUCNs), TaskCreationOptions.LongRunning));
completedCount++;
Console.Write("\rCompleted " + completedCount + " out of " + hdCount);
lock (numActiveThreadLock)
numActiveThreads++;
bool continuethreads = false;
while (continuethreads == false)
{
if (numActiveThreads < maxThreads)
continuethreads = true;
else
System.Threading.Thread.Sleep(1000);
}
}
Task.WaitAll(MyTasks.ToArray(), -1);
MyTasks.Clear();
protected static void GetADInfo(String ID, ref Dictionary<string, UserProfile> UserProfileDictionary, List<string> supportedOUCNs)
{
using (DirectoryEntry entry = new DirectoryEntry("LDAP://DC=A,DC=B,DC=C"))
{
using (DirectorySearcher mySearcher = new DirectorySearcher(entry))
{
mySearcher.SearchScope = SearchScope.Subtree;
mySearcher.CacheResults = false;
mySearcher.PropertiesToLoad.AddRange(new string[] { "cn", "displayName", "canonicalName", "userAccountControl", "distinguishedName"});
mySearcher.Filter = ("(&(samAccountType=805306368)(sAMAccountName=" + ID + "))");
foreach (SearchResult result in mySearcher.FindAll())
{
String displayname = "";
String acctstatus = "N/A";
String acctLocation = "N/A";
String strUserDN = result.Properties["distinguishedName"][0].ToString();
try
{
displayname = result.Properties["displayName"][0].ToString();
}
catch
{
displayname = "N/A";
}
acctLocation = result.Properties["canonicalName"][0].ToString().Replace(@"/" + result.Properties["cn"][0].ToString(), "");
int userAccountControl = Convert.ToInt32(result.Properties["userAccountControl"][0]);
bool disabled = ((userAccountControl & 2) > 0);
if (disabled == true)
acctstatus = "Disabled";
else
acctstatus = "Enabled";
String suptUser = "NOT SUPPORTED";
foreach (String CN in supportedOUCNs)
{
if (acctLocation.ToLower().Contains(CN.ToLower()) == true)
{
suptUser = "SUPPORTED";
break;
}
}
Dictionary<string, string> usermemberOfDictionary = new Dictionary<string, string>();
List<ResourceInfo> resInfoList = new List<ResourceInfo>();
entry.Path = "LDAP://" + strUserDN;
entry.RefreshCache(new string[] { "msds-memberOfTransitive" });
foreach (String strDN in entry.Properties["msds-memberOfTransitive"])
{
usermemberOfDictionary.Add(strDN, "GROUP");
}
String userOU = strUserDN;
String[] OUArray = userOU.Split(',');
foreach (String OU in OUArray)
{
userOU = userOU.Replace(OU + ",", "");
if (userOU != "DC=net")
{
usermemberOfDictionary.Add(userOU, "OU");
}
}
foreach (KeyValuePair<string, string> DNEntry in usermemberOfDictionary)
{
String strObject = "";
entry.Path = "LDAP://" + DNEntry.Key;
entry.RefreshCache(new string[] { "cn", "DriveMapping", "Priority" });
if (DNEntry.Value == "GROUP")
strObject = entry.Properties["cn"][0].ToString();
else
strObject = DNEntry.Key;
try
{
if (entry.Properties["DriveMapping"].Count > 0)
{
String resPriority = "";
try
{
resPriority = entry.Properties["Priority"][0].ToString();
}
catch
{
resPriority = "N/A";
}
PropertyValueCollection driveResources = entry.Properties["DriveMapping"];
for (int DRindex = 0; DRindex < driveResources.Count; DRindex++)
{
if (driveResources[DRindex].ToString().ToLower().Contains("<username>") == true)
{
String[] driveResourceArray = driveResources[DRindex].ToString().Split(',');
String resLetter = driveResourceArray[0] + @":\";
String resServer = driveResourceArray[1];
String resShare = driveResourceArray[2];
resInfoList.Add(new ResourceInfo
{
resObject = strObject,
resLetter = resLetter,
resServer = resServer,
resShare = resShare,
resPriority = resPriority,
resTargetLink = @"\\" + resServer + @"\" + resShare.ToLower().Replace("<username>", ID.ToLower()),
resServerSupportStatus = "NOT SUPPORTED",
resServerStatus = "OFFLINE",
resShareStatus = "NOT ACTIVE"
});
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
lock(UserProfileDictionaryLock)
{
UserProfile userProfile = UserProfileDictionary[ID.ToLower()];
userProfile.displayname = displayname;
userProfile.acctstatus = acctstatus;
userProfile.acctLocation = acctLocation;
userProfile.resources = resInfoList;
userProfile.userSupportStatus = suptUser;
UserProfileDictionary[ID.ToLower()] = userProfile;
}
}
mySearcher.FindAll().Dispose();
}
}
lock (numActiveThreadLock)
{
numActiveThreads--;
}
}
解决方案
对我来说最明显的问题是:
mySearcher.FindAll().Dispose();
是的,你应该Dispose
的SearchResultCollection
,但你必须处理你为循环创建的那个。第二次调用FindAll()
只是重复搜索。然后你只是丢弃结果,仍然留下你以前的SearchResultCollection
未处理。
你应该使用更像这样的东西:
using (var results = mySearcher.FindAll()) {
foreach (SearchResult result in results) {
}
}
做出这种改变应该会加快速度,因为它消除了每个帐户对 AD 的不必要调用。
我发现你的重用entry
有点奇怪,但我想它有效:)
你有没有理由不包括msds-memberOfTransitive
在内PropertiesToLoad
?这将节省对 AD 的另一个调用。
mySearcher.PropertiesToLoad.AddRange(new string[] { "cn", "displayName", "canonicalName", "userAccountControl", "distinguishedName", "msds-memberOfTransitive"});
...
//These lines no longer needed
//entry.Path = "LDAP://" + strUserDN;
//entry.RefreshCache(new string[] { "msds-memberOfTransitive" });
foreach (String strDN in result.Properties["msds-memberOfTransitive"]) {
...
}
我看到这是 Windows Server 2012 中的一个新属性。我无权访问在 2012 上运行的域,所以我无法测试以确保它有效,所以也许它不会。但我知道它会返回其他构造属性(如canonicalName
),所以它应该可以工作。
编辑:
另外 - 我不知道这是否有助于提高速度,但它会帮助简化你的代码 - 而不是使用lock(UserProfileDictionaryLock)
你可以只做UserProfileDictionary
a ConcurrentDictionary
,它被设计为线程安全的。
编辑 2:如果值得付出努力,您实际上可以在一个查询中搜索多个帐户:
(&(samAccountType=805306368)(|(sAMAccountName=username1)(sAMAccountName=username2)(sAMAccountName=username3)))
LDAP 查询的最大长度显然非常大,但是您可以分批执行 50 甚至 100(或更多?)。
因此,您可以将用户名列表传递给您的GetADInfo
方法,而不仅仅是一个。这确实可以减少您与 AD 建立的数字连接。
推荐阅读
- python - 熊猫 CSV 导入失败?
- html - 在 Angular 10 中滑动以删除
- php - 如何在 PHP 中配置基于会话的超时选项?
- google-cloud-platform - 将 10gb 文件上传到 GCS
- c# - 当用户在 FTP 发布期间访问 ASP.NET MVC 网站时会发生什么?
- reactjs - 当后端位于实际服务器上时,如何使用 React 向 Express 后端发送请求?
- python - discord.py 获取用户“关于我”部分
- arduino - 向 arduino 形式 ROS 提供输入
- java - 实现 Thread.UncaughtExceptionHandler 的类会捕获所有可能的未捕获异常吗?
- r - R根据另一个不公平连接的表的日期创建一个汇总表