【问题标题】:Optimizing Active directory data serialization for web-api为 web-api 优化 Active Directory 数据序列化
【发布时间】:2017-01-30 18:38:43
【问题描述】:

我创建了一个以 Rest 格式导出数据的活动目录 API。

虽然代码本身运行起来很慢,但我尝试了几种方法从我们的活动目录中获取数据,但处理速度几乎没有变化。

在目前的形式中,每 1000 个用户需要将近 15 秒的时间来检索它们并序列化它们的属性,然后返回数据。

所以我的问题是,是否有人可以帮助我弄清楚如何优化此过程并减少加载时间。我现在能想到的唯一解决方案是将数据直接存储在服务器 RAM 中,并使用线程以间隔更新它。

这就是现在的样子:

获取用户的类:

        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=", "");
        }
    }

AdUserdetail 中的所有属性声明如下所示:

    [DataMember]
    private String _firstName;
    [DataMember]
    private String _middleName;
    [DataMember]
    private String _lastName;

接着是:

    public String FirstName
    {
        get { return _firstName; }
    }

    public String MiddleName
    {
        get { return _middleName; }
    }

    public String LastName
    {
        get { return _lastName; }
    }

所有属性都以这种方式声明并使用 DataMember 进行序列化。

这里提示的第三个类是 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 非常大,有超过 30k 的用户,在一些预定的工作中,这个 API 需要响应多达 5k 的用户,在当前状态下它需要将近 70 秒来执行此操作,并且我想把时间缩短到 10 秒以下。但到目前为止,我的尝试都没有结果。到目前为止,绝大多数处理时间都发生在序列化过程中。

【问题讨论】:

    标签: c# asp.net xml rest active-directory


    【解决方案1】:

    瓶颈是 lambda 查询:

    IEnumerable<string> groupNames = groups.Select(x => x.SamAccountName);
    

    在大多数调用中,更换此选项可将加载时间缩短 10 倍。以前需要 132 秒的最重负载现在需要 17 秒。

    我知道这不是一个完整的答案,因为我不知道为什么这个 lambda 查询如此无能。但是加载时间肯定是有罪的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      • 2017-12-11
      相关资源
      最近更新 更多