/// <summary> /// Разгрузка очередей перегруженных клиентов /// </summary> /// <param name="client">Потенциальный клиент на разгруженные книги</param> /// <param name="alignmentCoefficient">Коэффициент выравнивания загружености клиентов</param> /// <returns>Очередь разгруженых книг</returns> public Dictionary <string, Queue <string> > UnloadingQueuesClients(ClientSDB client, double alignmentCoefficient) { Dictionary <string, Queue <string> > unloadingBooksQueue = new Dictionary <string, Queue <string> >(); DateTime reloadTimeRead = СalculationСriticalTimeRead(alignmentCoefficient); var selectedClient = from clientServer in ClientDictionary from lelaClientServer in clientServer.Value.GetClientServerDB().LevelLanguages from lelaClient in client.LevelLanguages where lelaClientServer.Language == lelaClient.Language && clientServer.Value.GetClientServerDB().TimeRead > reloadTimeRead select clientServer.Value; foreach (var selectImitationActivity in selectedClient) { selectImitationActivity.SetWaitOneMutexJointOperation(); ClientServerDB selectClientServer = selectImitationActivity.GetClientServerDB(); unloadingBooksQueue.Add(selectClientServer.Address, new Queue <string>()); while (selectClientServer.TimeRead > reloadTimeRead) { if (selectClientServer.QueueBook.Count > 0) { string book = selectClientServer.QueueBook.Dequeue(); unloadingBooksQueue[selectClientServer.Address].Enqueue(book); SetRecalculateTimeRead(selectClientServer.Address); } } } return(unloadingBooksQueue); }
/// <summary> /// Удаление клиента из систем сервера /// </summary> /// <param name="address">Электронный адресс клиента</param> private void DeleteClientServer(string address) { if (WorkClientGroup.ClientAvailability(address)) { ClientServerDB clientServer = WorkClientGroup.CloneClient(address); WorkClientGroup.DeleteClient(address); WorkMongo.DeleteClient(address); try { WorkMySQL.DeleteClient(address); WorkRabbit.RedistributionQueue(clientServer.QueueBook, WorkRabbit.ExchangeBook, WorkRabbit.RoutingBook); } catch (MySqlException) { WorkMongo.AddClient(clientServer); StopServer(); } catch (RabbitmqException) { WorkMongo.AddClient(clientServer); WorkMySQL.SetSubscriptionOn(address); StopServer(); } } }
/// <summary> /// Добавление информации о клиенте /// </summary> /// <param name="clientServer">Объект клиента</param> public void AddClientInfo(ClientServerDB clientServer) { string sqlCommand = "INSERT INTO clients (Address, Surname, Name, PagesPerDay, ReadingIntervalActive, " + "ReadingIntervalPassive, DataRegistration, Subscription) " + "VALUES (@Addres, @Surname, @Name, @PagesPerDay, @ReadingIntervalActive, " + "@ReadingIntervalPassive, @DataRegistration, @Subscription)"; try { ConnectionMySQL.Open(); MySqlCommand command = new MySqlCommand(sqlCommand, ConnectionMySQL); command.Parameters.AddWithValue("@Addres", clientServer.Address); command.Parameters.AddWithValue("@Surname", clientServer.Surname); command.Parameters.AddWithValue("@Name", clientServer.Name); command.Parameters.AddWithValue("@PagesPerDay", clientServer.PagesPerDay); command.Parameters.AddWithValue("@ReadingIntervalActive", clientServer.ReadingIntervalActive); command.Parameters.AddWithValue("@ReadingIntervalPassive", clientServer.ReadingIntervalPassive); command.Parameters.AddWithValue("@DataRegistration", clientServer.DataRegistration); command.Parameters.AddWithValue("@Subscription", "Подписан"); command.ExecuteNonQuery(); } catch (Exception exception) { Log.Error(exception.ToString); throw new MySqlException(); } finally { ConnectionMySQL.Close(); } }
/// <summary> /// Добавление клиента в системы сервера /// </summary> /// <param name="client">Клиент</param> private void AddClientServer(ClientSDB client) { if (!WorkClientGroup.ClientAvailability(client.Address) && !WorkMySQL.ClientAvailability(client.Address)) { ClientServerDB clientServer = new ClientServerDB(client); WorkClientGroup.AddClient(clientServer); WorkMongo.AddClient(clientServer); try { WorkMySQL.AddClient(clientServer); WorkRabbit.RedistributionQueue(WorkRabbit.QueueUnallocated, WorkRabbit.ExchangeBook, WorkRabbit.RoutingBook); } catch (MySqlException) { WorkMongo.DeleteClient(clientServer.Address); StopServer(); } catch (RabbitmqException) { WorkMongo.DeleteClient(clientServer.Address); WorkMySQL.FullDeleteClient(clientServer.Address); StopServer(); } } }
/// <summary> /// Добавление языков клиента и уровня их владения /// </summary> /// <param name="clientServer">Объект клиента</param> public void AddLevelLanguages(ClientServerDB clientServer) { foreach (LevelLanguageSDB ll in clientServer.LevelLanguages) { AddLevelLanguage(clientServer, ll); } }
/// <summary> /// Возвращение поверхностной копии клиента /// </summary> /// <param name="address">Электронный адресс клиента</param> /// <returns>Поверхностной копия клиента</returns> public ClientServerDB CloneClient(string address) { ClientServerDB clientServer = null; if (ClientDictionary.ContainsKey(address)) { clientServer = (ClientServerDB)ClientDictionary[address].GetClientServerDB().Clone(); } return(clientServer); }
/// <summary> /// Конструктор объекта имитации деятельности клиента /// </summary> /// <param name="clientServer"></param> /// <param name="serverDB"></param> public ImitationClientActivity(ClientServerDB clientServer, ControlServerDB serverDB) { ServerDB = serverDB; ClientServer = clientServer; ImitationActivity = new Thread(new ThreadStart(GettingAndReadingBook)); MutexJointOperation = new Mutex(); MySqlConnection = new MySqlSDB(ServerDB); MongoConnection = new MongoDbSDB(ServerDB); WaitHandler = new AutoResetEvent(true); }
/// <summary> /// Добавление клиента /// </summary> /// <param name="clientServer">Объект клиента</param> public void AddClient(ClientServerDB clientServer) { try { CollectionMongo.InsertOne(clientServer); } catch (Exception exception) { Log.Error(exception.ToString); ServerDB.StopServer(); } }
/// <summary> /// Создание рассширеного объекта клиента для записи в MongoDB (корректировка времени) /// </summary> /// <returns></returns> public ClientServerDB ConvertMongoDate() { ClientServerDB clientserverDBMongo = new ClientServerDB(); clientserverDBMongo.Surname = Surname; clientserverDBMongo.Name = Name; clientserverDBMongo.Address = Address; clientserverDBMongo.PagesPerDay = PagesPerDay; clientserverDBMongo.ReadingIntervalActive = ReadingIntervalActive; clientserverDBMongo.ReadingIntervalPassive = ReadingIntervalPassive; clientserverDBMongo.LevelLanguages = LevelLanguages; clientserverDBMongo.DataRegistration = DataRegistration.AddHours(3); clientserverDBMongo.queueBook = queueBook; clientserverDBMongo.TimeReadActive = TimeReadActive.AddHours(3); clientserverDBMongo.TimeRead = TimeRead.AddHours(3); return(clientserverDBMongo); }
/// <summary> /// Перерасчет даты-времени освобождения клиента от чтения /// </summary> /// <param name="clientServer">Клиент сервера</param> /// <returns>Даты-время освобождения клиента от чтения</returns> public DateTime RecalculateTimeRead(string address) { ClientServerDB clientServer = ClientDictionary[address].GetClientServerDB(); int queueBookCount = clientServer.QueueBook.Count; DateTime nowTime = DateTime.UtcNow.ToLocalTime(); DateTime timeReadRecalculation = clientServer.TimeReadActive < nowTime ? nowTime : clientServer.TimeReadActive; for (int i = 0; i < queueBookCount; i++) { string bookJSON = clientServer.QueueBook.Dequeue(); clientServer.QueueBook.Enqueue(bookJSON); BookSDB book = JsonSerializer.Deserialize <BookSDB>(bookJSON); double timeSleep = ServerDB.TimeReadingBook(clientServer, book, timeReadRecalculation); timeReadRecalculation = timeReadRecalculation.AddSeconds(timeSleep); } return(timeReadRecalculation); }
/// <summary> /// Расчет времени чтения книги /// </summary> /// <param name="clientServer">Клиент сервера</param> /// <param name="book">Книги</param> /// <param name="dataTime">Дата-время, относительно которого производится расчет</param> /// <returns>Время чтения книги (в секундах)</returns> public double TimeReadingBook(ClientServerDB clientServer, BookSDB book, DateTime dataTime) { double timeReadingBook; //время чтения книги int level = 0; //уровень владения языком foreach (LevelLanguageSDB lela in clientServer.LevelLanguages) { if (lela.Language == book.Language) { level = lela.Level; break; } } if (level == 0) { throw new Exception($"Клиент не владеет языком {book.Language}"); } TimeSpan interval = dataTime - clientServer.DataRegistration; //интервал времени от данного до времени регистрации int readingIntervalActiveSecond = clientServer.ReadingIntervalActive * DaysSecond; //время активного чтения в цикле int readingIntervalPassiveSecond = clientServer.ReadingIntervalPassive * DaysSecond; //время пассивного чтения чтения в цикле int cycleReading = readingIntervalActiveSecond + readingIntervalPassiveSecond; //время цикла чтения double levelPagesPerCycle = clientServer.ReadingIntervalActive * clientServer.PagesPerDay * (level / 10.0); //количество читаемых страниц в цикл double balanseInterval = interval.TotalSeconds % (double)cycleReading; //текущее время нового цикла double remainingActiveCycle = readingIntervalActiveSecond - balanseInterval; //оставшееся время цикла активного чтения double timeReadingBookNCCycle = (double)(book.Pages % levelPagesPerCycle) / (double)levelPagesPerCycle //время чтения неполного цикла * (double)readingIntervalActiveSecond; int TimeReadingBookFullCycle = (int)(book.Pages / levelPagesPerCycle) * cycleReading; //время чтения полных циклов if (balanseInterval >= readingIntervalActiveSecond) { timeReadingBook = TimeReadingBookFullCycle + timeReadingBookNCCycle + (cycleReading - balanseInterval); } else { if (remainingActiveCycle > timeReadingBookNCCycle) { timeReadingBook = TimeReadingBookFullCycle + timeReadingBookNCCycle; } else { timeReadingBook = TimeReadingBookFullCycle + timeReadingBookNCCycle + readingIntervalPassiveSecond; } } return(timeReadingBook); }
/// <summary> /// Добавление языка и уровня владения /// </summary> /// <param name="clientServer">Объект клиента</param> /// <param name="ll">Объект языка и уровня владения</param> public void AddLevelLanguage(ClientServerDB clientServer, LevelLanguageSDB ll) { string sqlCommand = "INSERT INTO levellanguages (AddressClient, Language, Level) " + "VALUES (@AddressClient, @Language, @Level)"; try { ConnectionMySQL.Open(); MySqlCommand command = new MySqlCommand(sqlCommand, ConnectionMySQL); command.Parameters.AddWithValue("@AddressClient", clientServer.Address); command.Parameters.AddWithValue("@Language", (int)ll.Language); command.Parameters.AddWithValue("@Level", ll.Level); command.ExecuteNonQuery(); } catch (Exception exception) { Log.Error(exception.ToString); throw new MySqlException(); } finally { ConnectionMySQL.Close(); } }
/// <summary> /// Добавление клиента /// </summary> /// <param name="clientServer">Объект клиента</param> public void AddClient(ClientServerDB clientServer) { ImitationClientActivity clientActivity = new ImitationClientActivity(clientServer, ServerDB); ClientDictionary.Add(clientServer.Address, clientActivity); }
/// <summary> /// Добавление клиента /// </summary> /// <param name="clientServer">Объект клиента</param> public void AddClient(ClientServerDB clientServer) { AddClientInfo(clientServer); AddLevelLanguages(clientServer); }
/// <summary> /// Изменение даты-времени освобождения от чтения на перерасчитаное /// </summary> /// <param name="address"></param> public void SetRecalculateTimeRead(string address) { ClientServerDB clientServer = ClientDictionary[address].GetClientServerDB(); clientServer.TimeRead = RecalculateTimeRead(address); }
/// <summary> /// Добавление, удаление клиентов /// </summary> /// <param name="e">Объект сообщения</param> /// <param name="channel">Канал связи с RabbitMQ</param> /// <param name="conn">Соединение с RabbitMQ</param> /// <param name="collection">Коллекция MongoDB</param> /// <param name="connectionString">Строка подключения MySQL</param> private static void AddingDeletedClient(BasicDeliverEventArgs e, IModel channel, IConnection conn, IMongoCollection <ClientServerDB> collection, string connectionString) { mutexDAD.WaitOne(); //получение и распределение операций (добавление или отписка клиента) var body = e.Body; var bodytype = e.BasicProperties.Type; var message = Encoding.UTF8.GetString(body); //если добавление клиента if (bodytype == "Adding") { //сериализация клиента ClientSDB clientSDB = JsonSerializer.Deserialize <ClientSDB>(message); //проверка клиента на наличие в базе if (!listClient.ContainsKey(clientSDB.Address)) { //проверка в MySQL был ли клиент раннее подписан string sqlcommand = "SELECT Address FROM clients WHERE Address = @Address"; bool HasRows; using (MySqlConnection connMySQL = new MySqlConnection(connectionString)) { connMySQL.Open(); MySqlCommand commandchek = new MySqlCommand(sqlcommand, connMySQL); MySqlParameter AddressCheckParam = new MySqlParameter("@Address", clientSDB.Address); commandchek.Parameters.Add(AddressCheckParam); MySqlDataReader reader = commandchek.ExecuteReader(); HasRows = reader.HasRows; } if (!HasRows) { //создание рассширеного объекта клиента и добавление его в список ClientServerDB clientServerDB = new ClientServerDB(clientSDB, conn, collection, connectionString); listClient.Add(clientServerDB.Address, clientServerDB); //добавление клиента в MongoDB ClientServerDB clientServerDBMongo = clientServerDB.ConvertMongoDate(); collection.InsertOne(clientServerDBMongo); //добавление клиента в MySQL using (MySqlConnection connMySQL = new MySqlConnection(connectionString)) { connMySQL.Open(); MySqlCommand command = new MySqlCommand(); command.Connection = connMySQL; command.CommandText = "INSERT INTO clients (Address, Surname, Name, PagesPerDay, ReadingIntervalActive, " + "ReadingIntervalPassive, DataRegistration, Subscription) " + "VALUES (@Addres, @Surname, @Name, @PagesPerDay, @ReadingIntervalActive, @ReadingIntervalPassive, @DataRegistration, @Subscription)"; MySqlParameter AddressParam = new MySqlParameter("@Addres", clientServerDB.Address); command.Parameters.Add(AddressParam); MySqlParameter SurnameParam = new MySqlParameter("@Surname", clientServerDB.Surname); command.Parameters.Add(SurnameParam); MySqlParameter NameParam = new MySqlParameter("@Name", clientServerDB.Name); command.Parameters.Add(NameParam); MySqlParameter PagesPerDayParam = new MySqlParameter("@PagesPerDay", clientServerDB.PagesPerDay); command.Parameters.Add(PagesPerDayParam); MySqlParameter ReadingIntervalActiveParam = new MySqlParameter("@ReadingIntervalActive", clientServerDB.ReadingIntervalActive); command.Parameters.Add(ReadingIntervalActiveParam); MySqlParameter ReadingIntervalPassiveParam = new MySqlParameter("@ReadingIntervalPassive", clientServerDB.ReadingIntervalPassive); command.Parameters.Add(ReadingIntervalPassiveParam); MySqlParameter DataRegistrationParam = new MySqlParameter("@DataRegistration", clientServerDB.DataRegistration); command.Parameters.Add(DataRegistrationParam); MySqlParameter SubscriptionParam = new MySqlParameter("@Subscription", "Подписан"); command.Parameters.Add(SubscriptionParam); command.ExecuteNonQuery(); foreach (LevelLanguageSDB ll in clientServerDB.LevelLanguages) { MySqlCommand commandll = new MySqlCommand(); commandll.Connection = connMySQL; commandll.CommandText = "INSERT INTO levellanguages (AddressClient, Language, Level) VALUES (@AddressClient, @Language, @Level)"; MySqlParameter AddressClientParam = new MySqlParameter("@AddressClient", clientServerDB.Address); commandll.Parameters.Add(AddressClientParam); MySqlParameter LanguageParam = new MySqlParameter("@Language", (int)ll.Language); commandll.Parameters.Add(LanguageParam); MySqlParameter LevelParam = new MySqlParameter("@Level", ll.Level); commandll.Parameters.Add(LevelParam); commandll.ExecuteNonQuery(); } } } } } //если отписка клиента else if (bodytype == "Deleted") { //проверка клиента на наличие в базе if (listClient.ContainsKey(message)) { //отписка клиента и удаление его из списка listClient[message].Unsubscribe(); listClient.Remove(message); //удаление клиента из MongoDB collection.DeleteOne(p => p.Address == message); //изменение статуса клиента на "Отписан" в MySQL using (MySqlConnection connMySQL = new MySqlConnection(connectionString)) { connMySQL.Open(); MySqlCommand command = new MySqlCommand(); command.Connection = connMySQL; command.CommandText = "UPDATE clients SET Subscription = 'Отписан' WHERE Address = @Address"; MySqlParameter AddressParam = new MySqlParameter("@Address", message); command.Parameters.Add(AddressParam); command.ExecuteNonQuery(); } } } channel.BasicAck(e.DeliveryTag, false); mutexDAD.ReleaseMutex(); }
/// <summary> /// Распределение книг /// </summary> /// <param name="e">Объект сообщения</param> /// <param name="channel">Канал связи с rabbitmq</param> /// <param name="collection">Коллекция MongoDB</param> private static void DistributionBook(BasicDeliverEventArgs e, IModel channel, IMongoCollection <ClientServerDB> collection) { mutexDAD.WaitOne(); //получение и дессериализация книги var body = e.Body; var message = Encoding.UTF8.GetString(body); BookSDB book = JsonSerializer.Deserialize <BookSDB>(message); ClientServerDB clientSDB = null; DateTime timeRead = new DateTime(); //промежуточное время окончания чтения книги DateTime timeReadMain = new DateTime(); //время оканчания чтения книги DateTime timeReadNow = new DateTime(); //время начала чтения книги bool first = true; foreach (var client in listClient) { foreach (LevelLanguageSDB lela in client.Value.LevelLanguages) { //проверка клиента на знание языка if (book.Language == lela.Language) { //определение времени начала чтения книги if (client.Value.TimeRead > DateTime.Now) { timeReadNow = client.Value.TimeRead; } else { timeReadNow = DateTime.Now; } //промежуточное определение времени окончания чтения книги timeRead = timeReadNow.AddSeconds(client.Value.TimeReadingBook(book.Language, book.Pages, timeReadNow)); if (first || timeRead < timeReadMain) { //если клиент прочитает книгу быстрее предыдущего клиента, он становится активным клиентом if (first) { first = false; } clientSDB = client.Value; timeReadMain = timeRead; } } } } //если подходящих клиентов не найденоб отправка книги в очередь не распределенных книг if (clientSDB == null) { channel.BasicPublish("ServerDB", "UnallocatedBook", null, e.Body); Console.WriteLine("UnallocatedBook" + " " + message); } else { //изменение времени оканчания чтения клиента и добавление книги ему в очередь clientSDB.TimeRead = timeReadMain; clientSDB.queueBook.Enqueue(message); //изменение времени оканчания чтения клиента и добавление книги в MongoDB var filter = Builders <ClientServerDB> .Filter.Eq("Address", clientSDB.Address); var updateTimeRead = Builders <ClientServerDB> .Update.Set(x => x.TimeRead, clientSDB.TimeRead.AddHours(3)); collection.UpdateOne(filter, updateTimeRead); var updateBook = Builders <ClientServerDB> .Update.AddToSet(x => x.queueBook, message); collection.UpdateOne(filter, updateBook); //если клиент в данный момент не читает и у него в очереди нет книг, запуск потока получения и чтения книг if (clientSDB.readingBook.ThreadState == ThreadState.Unstarted || clientSDB.readingBook.ThreadState == ThreadState.Stopped) { if (clientSDB.readingBook.ThreadState == ThreadState.Stopped) { clientSDB.readingBook = new Thread(new ThreadStart(clientSDB.ReadingBook)); } clientSDB.readingBook.Start(); } Console.WriteLine(clientSDB.Address + " " + message + clientSDB.TimeRead); } channel.BasicAck(e.DeliveryTag, false); mutexDAD.ReleaseMutex(); }