#c# #asp.net #xml #rest #active-directory
#c# #asp.net #xml #rest #active-directory
Вопрос:
я создал API Active Directory, который экспортирует данные в формате Rest.
Хотя сам по себе код работает очень медленно, я попробовал несколько способов извлечения данных из нашего активного каталога, но скорость процесса практически не изменилась.
В текущем виде на 1000 пользователей требуется почти 15 секунд, чтобы получить их и сериализовать их свойства, а затем вернуть данные.
Итак, мой вопрос в том, может ли кто-нибудь помочь мне выяснить, как оптимизировать этот процесс и сократить время загрузки. Единственное решение, которое я могу придумать прямо сейчас, — это хранить данные непосредственно в оперативной памяти серверов и использовать потоки для их обновления в интервалах.
Вот как это выглядит сейчас:
Класс, который выбирает пользователей:
public List<ADUserDetail> GetUserFromGroup(String groupName)
{
List<ADUserDetail> userlist = new List<ADUserDetail>();
try
{
var context = new PrincipalContext(
ContextType.Domain,
"domain", @"username", "password");
using (var group = GroupPrincipal.FindByIdentity(context, groupName))
{
var users = group.GetMembers(true);
int i = 1;
foreach (UserPrincipal user in users)
{
ADUserDetail userobj = ADUserDetail.GetProp(user);
Debug.WriteLine(i " " userobj.sAMAccountName);
i ;
userlist.Add(userobj);
}
}
return userlist;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return userlist;
}
}
Итак, как вы можете сказать, этот метод захватывает всех пользователей из группы и создает объект ADUserDetail для каждого из них, в конечном итоге возвращая список этих объектов.
Я пробовал разные методы здесь, используя Directorysearcher, но время отклика было почти идентичным UserPrincipal.
ADUserDetail выглядит следующим образом:
public ADUserDetail()
{
_Groups = new List<string>();
}
public static ADUserDetail GetProp(UserPrincipal directoryUser)
{
return new ADUserDetail(directoryUser);
}
private static String GetProperty(UserPrincipal userDetail, String propertyName)
{
if (userDetail.GetProperty(propertyName) != null)
{
return userDetail.GetProperty(propertyName);
}
else
{
return string.Empty;
}
}
private ADUserDetail(UserPrincipal directoryUser)
{
String domainAddress;
String domainName;
_Groups = new List<string>();
var groups = directoryUser.GetGroups();
IEnumerable<string> groupNames = groups.Select(x => x.SamAccountName);
foreach (string name in groupNames)
{
_Groups.Add(name);
}
_firstName = GetProperty(directoryUser, ADProperties.FIRSTNAME);
_middleName = GetProperty(directoryUser, ADProperties.MIDDLENAME);
_lastName = GetProperty(directoryUser, ADProperties.LASTNAME);
_sAMAccountName = GetProperty(directoryUser, ADProperties.SAMACCOUNTNAME);
String userPrincipalName = GetProperty(directoryUser, ADProperties.USERPRINCIPALNAME);
if (!string.IsNullOrEmpty(userPrincipalName))
{
domainAddress = userPrincipalName.Split('@')[1];
}
else
{
domainAddress = String.Empty;
}
if (!string.IsNullOrEmpty(domainAddress))
{
domainName = domainAddress.Split('.').First();
}
else
{
domainName = String.Empty;
}
_streetAddress = GetProperty(directoryUser, ADProperties.STREETADDRESS);
_city = GetProperty(directoryUser, ADProperties.CITY);
_state = GetProperty(directoryUser, ADProperties.STATE);
_postalCode = GetProperty(directoryUser, ADProperties.POSTALCODE);
_country = GetProperty(directoryUser, ADProperties.COUNTRY);
_company = GetProperty(directoryUser, ADProperties.COMPANY);
_department = GetProperty(directoryUser, ADProperties.DEPARTMENT);
_homePhone = GetProperty(directoryUser, ADProperties.HOMEPHONE);
_extension = GetProperty(directoryUser, ADProperties.EXTENSION);
_mobile = GetProperty(directoryUser, ADProperties.MOBILE);
_fax = GetProperty(directoryUser, ADProperties.FAX);
_emailAddress = GetProperty(directoryUser, ADProperties.EMAILADDRESS);
_title = GetProperty(directoryUser, ADProperties.TITLE);
_manager = GetProperty(directoryUser, ADProperties.MANAGER);
_adminDescription = GetProperty(directoryUser, ADProperties.ADMINDESCRIPTION);
_cn = GetProperty(directoryUser, ADProperties.CONTAINERNAME);
_company = GetProperty(directoryUser, ADProperties.COMPANY);
_department = GetProperty(directoryUser, ADProperties.DEPARTMENT);
_displayName = GetProperty(directoryUser, ADProperties.DISPLAYNAME);
_distinguishedName = GetProperty(directoryUser, ADProperties.DISTINGUISHEDNAME);
_homeDirectory = GetProperty(directoryUser, ADProperties.HOMEDIRECTORY);
_homeDrive = GetProperty(directoryUser, ADProperties.HOMEDRIVE);
_homeMDB = GetProperty(directoryUser, ADProperties.HOMEMDB);
_homeMTA = GetProperty(directoryUser, ADProperties.HOMEMTA);
_info = GetProperty(directoryUser, ADProperties.INFO);
_mail = GetProperty(directoryUser, ADProperties.EMAILADDRESS);
_mailNickname = GetProperty(directoryUser, ADProperties.MAILNICKNAME);
_manager = GetProperty(directoryUser, ADProperties.MANAGER);
_mDBUseDefaults = GetProperty(directoryUser, ADProperties.MDBUSEDEFAULTS);
_mobile = GetProperty(directoryUser, ADProperties.MOBILE);
_name = GetProperty(directoryUser, ADProperties.NAME);
_neEmployeeNumber = GetProperty(directoryUser, ADProperties.NEEMPLOYEENUMBER);
_neEdirDn = GetProperty(directoryUser, ADProperties.NEEDIRDN);
_objectCategory = GetProperty(directoryUser, ADProperties.OBJECTCATEGORY);
_objectClass = "Placeholder";;
_primaryGroupID = GetProperty(directoryUser, ADProperties.PRIMARYGROUPID);
_proxyAddresses = "Placeholder"; ;
_sAMAccountType = GetProperty(directoryUser, ADProperties.SAMACCOUNTTYPE);
_showInAddressBook = "Placeholder"; ;
_streetAddress = GetProperty(directoryUser, ADProperties.STREETADDRESS);
_telephoneNumber = GetProperty(directoryUser, ADProperties.TELEPHONENUMBER);
_title = GetProperty(directoryUser, ADProperties.TITLE);
_userPrincipalName = GetProperty(directoryUser, ADProperties.USERPRINCIPALNAME);
_employeeID = GetProperty(directoryUser, ADProperties.EMPLOYEEID);
_c = GetProperty(directoryUser, ADProperties.COUNTRYNOTATION);
_postalCode = GetProperty(directoryUser, ADProperties.POSTALCODE);
_physicalDeliveryOfficeName = GetProperty(directoryUser, ADProperties.PHYSICALDELIVERYOFFICENAME);
_instanceType = GetProperty(directoryUser, ADProperties.INSTANCETYPE);
_whenCreated = GetProperty(directoryUser, ADProperties.WHENCREATED);
_whenChanged = GetProperty(directoryUser, ADProperties.WHENCHANGED);
_memberOf = "PlaceHolder";//GetProperty(directoryUser, ADProperties.MEMBEROF);
_directReports = GetProperty(directoryUser, ADProperties.DIRECTREPORTS);
_userAccountControl = GetProperty(directoryUser, ADProperties.USERACCOUNTCONTROL);
_codePage = GetProperty(directoryUser, ADProperties.CODEPAGE);
_countryCode = GetProperty(directoryUser, ADProperties.COUNTRYCODE);
_adminCount = GetProperty(directoryUser, ADProperties.ADMINCOUNT);
_logonCount = GetProperty(directoryUser, ADProperties.LOGONCOUNT);
_legacyExchangeDN = GetProperty(directoryUser, ADProperties.LEGACYEXCHANGEDN);
_servicePrincipalName = GetProperty(directoryUser, ADProperties.SERVICEPRINCIPALNAME);
_dSCorePropagationData = "Placeholder";
_pager = GetProperty(directoryUser, ADProperties.PAGER);
_homePhone = GetProperty(directoryUser, ADProperties.HOMEPHONE);
_msExchUserAccountControl = GetProperty(directoryUser, ADProperties.MSEXCHUSERACCOUNTCONTROL);
_msExchPoliciesIncluded = GetProperty(directoryUser, ADProperties.MSEXCHPOLICIESINCLUDED);
_msExchRecipientDisplayType = GetProperty(directoryUser, ADProperties.MSEXCHRECIPIENTDISPLAYTYPE);
if (!String.IsNullOrEmpty(_manager))
{
String[] managerArray = _manager.Split(',');
_managerName = managerArray[0].Replace("CN=", "");
}
}
All property declarations in AdUserdetail looks like this:
[DataMember]
private String _firstName;
[DataMember]
private String _middleName;
[DataMember]
private String _lastName;
Followed by:
public String FirstName
{
get { return _firstName; }
}
public String MiddleName
{
get { return _middleName; }
}
public String LastName
{
get { return _lastName; }
}
Все свойства объявляются таким образом и сериализуются с помощью элемента данных.
Третий класс, на который здесь намекают, — это класс ADProperties . Однако это всего лишь класс констант, облегчающий отслеживание имен свойств, это выглядит так:
public const String ADMINDESCRIPTION = "adminDescription";
public const String FIRSTNAME = "givenName";
public const String MIDDLENAME = "initials";
и так далее.
Затем все это вызывается контроллером API следующим образом:
public HttpResponseMessage Get()
{
ActiveDirectorySearcher ADSearcher = new ActiveDirectorySearcher();
var Users = ADSearcher.GetUserFromGroup("NameofGroup");
if (Users != null)
{
return Request.CreateResponse(HttpStatusCode.OK, Users);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Users found");
}
Итак, вот как это выглядит, наш AD довольно большой, с более чем 30 тыс. пользователей, и в некоторых запланированных работах требуется, чтобы этот API отвечал до 5 тыс. пользователей, в его текущем состоянии на это уходит почти 70 секунд, и я хочу сократить это время до менее 10 секунд. Но пока мои попытки были бесплодными. На данный момент подавляющее большинство времени обработки происходит в процессе сериализации.
Ответ №1:
Узким местом был лямбда-запрос в:
IEnumerable<string> groupNames = groups.Select(x => x.SamAccountName);
Замена этого сокращает время загрузки в 10 раз для большинства вызовов. Самые тяжелые загрузки, которые ранее занимали 132 секунды, теперь занимают 17.
Я знаю, что это не полный ответ, поскольку я не могу сказать, почему этот лямбда-запрос был настолько неэффективным. Но это, безусловно, было виновато во времени загрузки.