我需要在AD中查找大约50,000个ID,因此我使用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--;
}
}
答案 0 :(得分:0)
对我来说最明显的问题是:
mySearcher.FindAll().Dispose();
是的,您应该使用Dispose
中的SearchResultCollection
,但是必须处理为循环创建的代码。再次调用FindAll()
只会重复搜索。然后,您只是丢弃结果,而仍然保留先前的SearchResultCollection
。
您应该使用类似以下的内容:
using (var results = mySearcher.FindAll()) {
foreach (SearchResult result in results) {
}
}
进行此更改将加快处理速度,因为它消除了每个帐户不必要的广告拨出。
我发现您对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)
设为ConcurrentDictionary
,而不必使用UserProfileDictionary
来代替(&(samAccountType=805306368)(|(sAMAccountName=username1)(sAMAccountName=username2)(sAMAccountName=username3)))
线程安全的。
编辑2:如果值得努力,您实际上可以在一个查询中搜索多个帐户:
GetADInfo
LDAP查询的最大长度显然为very big,但是您可以批量50个甚至100个(或更多?)来完成它们。
因此,您可以将用户名列表传递给public static void main(String[] args) {
int[]myArray = new int[5];
for(int index =0;index<myArray.length;index++)
myArray[3]=100*myArray[3]*myArray[3]+3;
System.out.println("myArray[]="+myArray[3]);
}
方法,而不仅仅是一个。这确实可以减少您与AD建立的连接数量。