/// <summary> /// Получение и чтение книг клиентом без обработки общих исключений /// </summary> private void TryGettingAndReadingBook() { int MilisecondOfSecond = 1000; ReadingActiveBook(); while (true) { if (ClientServer.QueueBook.Count == 0) { WaitHandler.Reset(); WaitHandler.WaitOne(); } MutexJointOperation.WaitOne(); string bookJSON = ClientServer.QueueBook.Dequeue(); BookSDB book = JsonSerializer.Deserialize <BookSDB>(bookJSON); double timeSleep = ServerDB.TimeReadingBook(ClientServer, book, DateTime.UtcNow.ToLocalTime()); DateTime timeComleteReadActive = DateTime.UtcNow.ToLocalTime().AddSeconds(timeSleep); SendBookClient(bookJSON, timeComleteReadActive); MutexJointOperation.ReleaseMutex(); Thread.Sleep((int)timeSleep * MilisecondOfSecond); MySqlConnection.ImplementationBook(ClientServer.Address, DateTime.UtcNow.ToLocalTime()); } }
/// <summary> /// Распределение книги из RabbitMQ без обработки общих исключений /// </summary> /// <param name="sender">Вызывающий объект</param> /// <param name="e">Сообщение из RabbitMQ</param> private void TryDistributionBookRequest(object sender, BasicDeliverEventArgs e) { MutexOnlyMethod.WaitOne(); BookSDB book = WorkRabbit.ConvertMessageRabbit <BookSDB>(e.Body); DateTime timeRead; string clientAddress = WorkClientGroup.SearchFastReader(book, out timeRead); if (clientAddress == null) { WorkRabbit.PublishUnallocatedQueue(e); WorkRabbit.BasicAckRabbit(sender, e); } else { string bookJSON = WorkRabbit.ConvertMessageRabbit <string>(e.Body); AddBookServer(clientAddress, bookJSON, timeRead); try { WorkRabbit.BasicAckRabbit(sender, e); } catch (RabbitmqException) { WorkMongo.DeleteLastBook(clientAddress); StopServer(); } WorkClientGroup.InformSendingBook(clientAddress); } MutexOnlyMethod.ReleaseMutex(); }
/// <summary> /// Поиск клиента, прочитающего книгу быстрее всех /// </summary> /// <param name="book">Книга</param> /// <param name="timeRead">Дата-время прочтения книги</param> /// <returns>Электронный адресс клиента</returns> public string SearchFastReader(BookSDB book, out DateTime timeRead) { string clientAddress = null; DateTime timeReadEnd = new DateTime(); //промежуточное время окончания чтения книги DateTime timeReadNow = new DateTime(); //время начала чтения книги timeRead = new DateTime(); //время окончания чтения книги bool first = true; var selectedClients = from client in ClientDictionary from lela in client.Value.GetClientServerDB().LevelLanguages where lela.Language == book.Language select client.Value.GetClientServerDB(); foreach (var clientServer in selectedClients) { DateTime nowTime = DateTime.UtcNow.ToLocalTime(); timeReadNow = clientServer.TimeRead > nowTime ? clientServer.TimeRead : nowTime; timeReadEnd = timeReadNow.AddSeconds(ServerDB.TimeReadingBook(clientServer, book, timeReadNow)); if (first || timeReadEnd < timeRead) { if (first) { first = false; } clientAddress = clientServer.Address; timeRead = timeReadEnd; } } return(clientAddress); }
/// <summary> /// Добавление книги /// </summary> /// <param name="address">Электронный адресс клиента</param> /// <param name="bookJSON">Книга в JSON</param> /// <param name="dataGetting">Дата-время получения книги</param> public void AddBook(string address, string bookJSON, DateTime dataGetting) { BookSDB bookObject = JsonSerializer.Deserialize <BookSDB>(bookJSON); string sqlCommand = "INSERT INTO books (AddressClient, Language, Name, Pages, DataGetting) " + "VALUES(@AddressClient, @Language, @Name, @Pages, @DataGetting); "; try { ConnectionMySQL.Open(); MySqlCommand command = new MySqlCommand(sqlCommand, ConnectionMySQL); command.Parameters.AddWithValue("@AddressClient", address); command.Parameters.AddWithValue("@Language", (int)bookObject.Language); command.Parameters.AddWithValue("@Name", bookObject.Name); command.Parameters.AddWithValue("@Pages", bookObject.Pages); command.Parameters.AddWithValue("@DataGetting", dataGetting); command.ExecuteNonQuery(); } catch (Exception exception) { Log.Error(exception.ToString); StopApplication(); } finally { ConnectionMySQL.Close(); } }
/// <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="conn">Соединение с RabbitMQ</param> /// <param name="collection">Коллекция MongoDB</param> /// <param name="connectionString">Строка подключения MySQL</param> private static void ObjectOverload(IConnection conn, IMongoCollection <ClientServerDB> collection, string connectionString) { //извлечение всех объектов из MongoDB var filter = new BsonDocument(); var people = collection.Find(filter).ToList(); foreach (ClientServerDB clientServerDB in people) { //количество книг в очереди int queueBookCount = clientServerDB.queueBook.Count; //расчет начала чтения книг из очереди DateTime timeReadIntermediate = clientServerDB.TimeReadActive; if (timeReadIntermediate < DateTime.Now) { timeReadIntermediate = DateTime.Now; } //перерасчет даты освобождения от чтения for (int i = 0; i < queueBookCount; i++) { string book = clientServerDB.queueBook.Dequeue(); clientServerDB.queueBook.Enqueue(book); BookSDB bookSDB = JsonSerializer.Deserialize <BookSDB>(book); double timeSleep = clientServerDB.TimeReadingBook(bookSDB.Language, bookSDB.Pages, timeReadIntermediate); timeReadIntermediate = timeReadIntermediate.AddSeconds(timeSleep); } clientServerDB.InitComponentReload(conn, collection, connectionString, timeReadIntermediate); //запись даты освобождения от чтения в MongoDB var filterDB = Builders <ClientServerDB> .Filter.Eq("Address", clientServerDB.Address); var updateTimeRead = Builders <ClientServerDB> .Update.Set(x => x.TimeRead, clientServerDB.TimeRead.AddHours(3)); collection.UpdateOne(filterDB, updateTimeRead); //добавления объекта в список и запуск потока получения и чтения книг listClient.Add(clientServerDB.Address, clientServerDB); clientServerDB.readingBook.Start(); } }
/// <summary> /// Отправка книги по электронной почте /// </summary> /// <param name="address">wmail адресс</param> /// <param name="bookSDB">книга</param> private void SendEmail(string address, BookSDB bookSDB) { }
/// <summary> /// Метод получения и чтения книг /// </summary> public void ReadingBook() { //проверка на перезагрузку объекта и ожидание прочтения текущей книги при необходимости if (ReloadObject) { ReloadObject = false; DateTime DTNow; if (TimeReadActive > DateTime.Now) { TimeSpan timeSleepActive = TimeReadActive - DateTime.Now; Thread.Sleep(timeSleepActive); DTNow = DateTime.Now; } else { DTNow = TimeReadActive; } //занесение даты прочтения книги в MySQL string sqlExpressionReload = "UPDATE books SET DataReading = @DTNow WHERE AddressClient = @AddressClient AND DataReading IS NULL"; using (MySqlConnection ConnMySQL = new MySqlConnection(ConnectionString)) { ConnMySQL.Open(); MySqlCommand commandReload = new MySqlCommand(sqlExpressionReload, ConnMySQL); MySqlParameter AddressClientBookParam = new MySqlParameter("@AddressClient", Address); commandReload.Parameters.Add(AddressClientBookParam); MySqlParameter DataReadingBookParam = new MySqlParameter("@DTNow", DTNow); commandReload.Parameters.Add(DataReadingBookParam); commandReload.ExecuteNonQuery(); } } while (true) { if (queueBook.Count != 0) { mutexSend.WaitOne(); //получение книги из очереди и дессериализация string book = queueBook.Dequeue(); BookSDB bookSDB = JsonSerializer.Deserialize <BookSDB>(book); //подсчет времени ожидания и даты окончания чтения книги double timeSleep = TimeReadingBook(bookSDB.Language, bookSDB.Pages, DateTime.Now); DateTime timeReadActive = DateTime.Now.AddSeconds(timeSleep); SendEmail(Address, bookSDB); //Занесение книги в MySQL DateTime DataGetting = DateTime.Now; string sqlExpression = "insert_book"; object idBook; using (MySqlConnection ConnMySQL = new MySqlConnection(ConnectionString)) { ConnMySQL.Open(); MySqlCommand command = new MySqlCommand(sqlExpression, ConnMySQL); command.CommandType = CommandType.StoredProcedure; MySqlParameter AddressClientParam = new MySqlParameter("@AddressClientP", Address); command.Parameters.Add(AddressClientParam); MySqlParameter LanguageParam = new MySqlParameter("@LanguageP", (int)bookSDB.Language); command.Parameters.Add(LanguageParam); MySqlParameter NameParam = new MySqlParameter("@NameP", bookSDB.Name); command.Parameters.Add(NameParam); MySqlParameter PagesParam = new MySqlParameter("@PagesP", bookSDB.Pages); command.Parameters.Add(PagesParam); MySqlParameter DataGettingParam = new MySqlParameter("@DataGettingP", DataGetting); command.Parameters.Add(DataGettingParam); idBook = command.ExecuteScalar(); } //занесение книги в MongoDB var filter = Builders <ClientServerDB> .Filter.Eq("Address", Address); var updateTimeReadActive = Builders <ClientServerDB> .Update.Set(x => x.TimeReadActive, timeReadActive.AddHours(3)); CollectionMongo.UpdateOne(filter, updateTimeReadActive); var updateBook = Builders <ClientServerDB> .Update.Pull(x => x.queueBook, book); CollectionMongo.UpdateOne(filter, updateBook); mutexSend.ReleaseMutex(); //чтение книги Thread.Sleep((int)timeSleep * 1000); //занесение даты прочтения книги в MySQL DateTime DataReading = DateTime.Now; using (MySqlConnection ConnMySQL = new MySqlConnection(ConnectionString)) { ConnMySQL.Open(); MySqlCommand commandReading = new MySqlCommand(); commandReading.Connection = ConnMySQL; commandReading.CommandText = "UPDATE books SET DataReading = @DataReading WHERE Id = @Id"; MySqlParameter DataReadingParam = new MySqlParameter("@DataReading", DataReading); commandReading.Parameters.Add(DataReadingParam); MySqlParameter idParam = new MySqlParameter("@Id", idBook); commandReading.Parameters.Add(idParam); commandReading.ExecuteNonQuery(); } } else { break; } } }
/// <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(); }