none
Разработка службы в Visual Studio RRS feed

  • Pergunta

  • Написал службу, которая опрашивает БД и в зависимости от значения столбца Enabled добавляет или удаляет пользователя из группы AD. Служба работает, однако грузит процессор на 20%. Ниже привожу класс в котором выполняются все действия службы

    class AccessControl
        {
            public void AddUserToGroup(string username, string groupname) // Добавляет юзера в группу
            {
                    using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "MARINA"))
                    {
                        GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupname);
                        group.Members.Add(pc, IdentityType.SamAccountName, username);
                        group.Save();
                    }
            }
            public void RemoveUserFromGroup(string username, string groupname) // Удаляет юзера из группы
            {      
                    using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "MARINA"))
                    {
                        GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupname);
                        group.Members.Remove(pc, IdentityType.SamAccountName, username);
                        group.Save();
                    }
                
            }
            public bool IsUserFromGroup(string username, string groupname) // Проверяет членство юзера в группе
            {
                using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "MARINA"))
                {
                    GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupname);
                    bool res = group.Members.Contains(pc, IdentityType.SamAccountName, username);
                    return res;
                }
    
            }
            public void Start()
            {
                AccessControls(true); // Запускает цикл 
            }
            public void Stop()
            {
                AccessControls(false); // Останавливает цикл
            }
            public void AccessControls(bool stat) // Основная функция службы
            {
    
                while (stat == true)
                {
                    string connectionString = @"Data Source=MBS1\MARINA_SQL;Initial Catalog=INTERNET_SQL;Integrated Security=True"; // Строка подключения к БД
                    string queryString = "SELECT UserName FROM dbo.UsersWithAccess WHERE Enabled = 1"; // Запрос списка пользователей, которым разрешен доступ в инет
                    List<String> usernames = new List<String>();
                    using (SqlConnection connection = new SqlConnection(connectionString))
                    {
                        SqlCommand command = new SqlCommand(queryString, connection);
                        connection.Open();
                        SqlDataReader reader = command.ExecuteReader();
                        while (reader.Read())
                        {
                            usernames.Add(reader.GetString(0)); // Сохранение списка в массив
                        }
                    }
                    foreach (String username in usernames) // Перебор элементов массива
                    {
                        bool result = IsUserFromGroup(username, "internet_full_permit"); // Проверка членства пользователя в группе
                        if (result == false)
                        {
                            AddUserToGroup(username, "internet_full_permit"); // Добавление юзера в группу
                        }
                    }
    
                    queryString = "SELECT UserName FROM dbo.UsersWithAccess WHERE Enabled = 0"; // Запрос списка пользователей которым запрещен доступ в инет
                    List<String> usernames2 = new List<String>();
                    using (SqlConnection connection = new SqlConnection(connectionString))
                    {
                        SqlCommand command = new SqlCommand(queryString, connection);
                        connection.Open();
                        SqlDataReader reader = command.ExecuteReader();
                        while (reader.Read())
                        {
                            usernames2.Add(reader.GetString(0)); // Сохранение списка в массив
                        }
                    }
                    foreach (String username in usernames2) // Перебор элементов массива
                    {
                        bool result = IsUserFromGroup(username, "internet_full_permit"); // Проверка членства пользователя в группе
                        if (result == true)
                        {
                            RemoveUserFromGroup(username, "internet_full_permit"); // Удаление пользователя из группы
                        }
                    }
                }
            }
        }

    Помимо этого при работе службы процесс lsass.exe начинает также грузить процессор примерно на 10%. 

    Пробовал запускать службу как на DC с Win Server 2008 R2, так и на своем компе с Win7 Pro - на компе служба не создает такой нагрузки на процессор, как на DC или рядовом сервере.

    На компе - служба - 3...5%, lsass.exe - 0%
    На DC - служба 16...20%, lsass.exe - 10...12%

    Подскажите пожалуйста, как можно уменьшить нагрузку на процессор создаваемую этой службой?




    • Editado R2DDD sábado, 5 de dezembro de 2020 19:46
    sábado, 5 de dezembro de 2020 18:40

Respostas

  • Следует перестать крутить бесконечный цикл без перерывов. Такой цикл по определению будет на 100% загружать одно ядро. Возможно больше если где то используется многопоточность.

    В простейшем случае это можно сделать добавив период ожидания после каждого опроса. Я так думаю вполне допустимо чтоб опрос происходил раз в несколько секунд, например 10. Для реализации достаточно вставить данный код в любое место цикла, обычно в конец:

    Thread.Sleep(10000); // Подождем 10 секунд чтоб не грузить процессор.

    Так же у вас неправильно реализована остановка цикла.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Marcado como Resposta R2DDD sábado, 5 de dezembro de 2020 23:41
    sábado, 5 de dezembro de 2020 20:02
    Moderador

Todas as Respostas

  • Следует перестать крутить бесконечный цикл без перерывов. Такой цикл по определению будет на 100% загружать одно ядро. Возможно больше если где то используется многопоточность.

    В простейшем случае это можно сделать добавив период ожидания после каждого опроса. Я так думаю вполне допустимо чтоб опрос происходил раз в несколько секунд, например 10. Для реализации достаточно вставить данный код в любое место цикла, обычно в конец:

    Thread.Sleep(10000); // Подождем 10 секунд чтоб не грузить процессор.

    Так же у вас неправильно реализована остановка цикла.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    • Marcado como Resposta R2DDD sábado, 5 de dezembro de 2020 23:41
    sábado, 5 de dezembro de 2020 20:02
    Moderador
  • Спасибо за совет, помогло. Да, 10 секунд вполне допустимо.

    Скажите пожалуйста, а каким образом тогда правильнее выходить из цикла?


    • Editado R2DDD sábado, 5 de dezembro de 2020 23:52
    sábado, 5 de dezembro de 2020 23:43
  • Спасибо за совет, помогло. Да, 10 секунд вполне допустимо.

    Скажите пожалуйста, а каким образом тогда правильнее выходить из цикла?


    Вы вообще не выходите из цикла, просто вызывайте метод второй раз и он возвращает управление ничего не сделав.

    Чтоб остановить цикл нужно использовать переменную в классе которая будет проверяться в потоке где крутится цикл, а изменятся в потоке который будет останавливать цикл:

    private bool keepRunning = true;

    void Stop()

    {

     keepRunning  = false;

     // Тут можно подождать семафор который подтвердит выход из цикла.

    }

    void Runner() 

    {

      while (keepRunning)

     {

         // Тут надо делать что надо, но не очень долго за раз.

     }

     // Тут можно установить семафор чтоб подтвердить выход из цикла.

    }


    This posting is provided "AS IS" with no warranties, and confers no rights.

    domingo, 6 de dezembro de 2020 00:06
    Moderador
  • А возможно ли свести все к одному запросу к БД?

    Попробовал сделать путем добавления результата запроса в коллекцию, однако получил ошибку: "Индекс за пределами диапазона. Индекс должен быть положительным числом, а его размер не должен превышать размер коллекции. Имя параметра: index". Ошибка возникает на строке 

    usernames[usernames.Count - 0][0] = reader[0].ToString();
    Вот фрагмент кода 
    List < String[]> usernames = new List<String[]>();
                    using (SqlConnection connection = new SqlConnection(connectionString))
                    {
                        SqlCommand command = new SqlCommand(queryString, connection);
                        connection.Open();
                        SqlDataReader reader = command.ExecuteReader();
                        while (reader.Read())
                        {
                            usernames.Add(new string[2]); // Сохранение списка в массив
                            usernames[usernames.Count - 0][0] = reader[0].ToString();
                            usernames[usernames.Count - 1][1] = reader[1].ToString();
                        }
                    }


    domingo, 6 de dezembro de 2020 17:59
  • В общем случае это это зависит от схемы базы и запроса. Смотрите сами что возвращает запрос.

    Что до исключения, то проблема может быть тут:

    usernames[usernames.Count - 0]

    В списке для начала находится один элемент с индексом 0, при этом Count = 1.

    1 - 0 = 1

    Это обращение к элементу с индексом 1, а у вас его нет. Получается выход за пределы списка.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    domingo, 6 de dezembro de 2020 20:19
    Moderador
  • В базе 2 таблицы, никак между собой не связанные. Первая - та, которая опрашивается службой. Вторая - лог. 

    Пробовал изменить usernames[usernames.Count - 0] на usernames[usernames.Count - 1] и т.д. остальные подобные строки - безрезультатно. Исключение стало возникать на 2 строке.

    Запрос простейший SELECT UserName, Enabled FROM dbo.UsersWithAccess

    У UserName тип данных nvarchar(50) а у Enabled - int

    domingo, 6 de dezembro de 2020 20:36
  • Используйте отладку чтоб найти ошибку. Может опять с индексом промахнулись, а может запрос возвращает не то что вы ожидали.

    This posting is provided "AS IS" with no warranties, and confers no rights.

    domingo, 6 de dezembro de 2020 22:42
    Moderador
  • Это как раз в отладчике ошибка появляется
    segunda-feira, 7 de dezembro de 2020 14:10