Разработка серверной частимобильных приложений. Разработка мобильных приложений: Синхронизация с сервером Разработка серверной части приложения

22.04.2021 Безопасность

БЭКАПЫ

Зачем нужны бэкапы на мобильной платформе

Специалисты знают, насколько порой ненадежны мобильные приложения на 1С: в любой момент могут возникнуть ошибки, из-за которых базы пользователей просто разрушатся. Одновременно мы сталкиваемся с ненадежностью самих устройств: их можно разбить, потерять, их могут украсть, а пользователи хотят сохранить свои данные. И вплоть до версии 8.3.9 мы не имели платформенного механизма сохранения бэкапа.

Поскольку раньше у пользователей не было кнопки «сохранить копию», разработчикам приложения Boss пришлось самим делать бэкапы. Как мы это сделали?

Сами данные базы мы сохраняем в виде ХML.

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

Таким образом, разработчики дополнительно себя страхуют. Если что-то пошло не так, и вдруг сломался механизм создания копий на Гугл-Диске или Яндекс-Диске, всегда можно сказать пользователю, что в данный момент разработчик разбирается с ошибкой, а пока он может сохранить данные альтернативным способом. И пользователи остаются довольны, потому что они могут быть спокойны за свои данные.

Обязательно нужно сделать акцент на облачные сервисы , потому что если устройство потеряется или разобьется, а пользователь сохранял копию на этом же устройстве, то данные будут утеряны.

Также мы обязательно напоминаем пользователю о необходимости создания бэкапов.

Как сохранять копии, если меняется конфигурация?

Когда мы говорим о массовом решении, о приложении, которое постоянно меняется, развивается и дорабатывается, надо учитывать поведение клиентов. Пользователь может захотеть восстановить бэкап, сохраненный в старой версии приложении, где не было каких-то реквизитов. И тогда возникает задача: прочитать данные, затем дозаполнить данные по логике обновления со старой версии приложения. Как это сделать? Помимо данных, сохранить еще и саму структуру данных, чтобы потом знать, как их читать.

Есть несколько вариантов хранения этой структуры данных, в том числе ее можно хранить в самой конфигурации. То есть при выпуске каждой новой версии, сохранять структуру метаданных предыдущей версии в макет в конфигурации.

Не стоит забывать, что в мобильном приложении конфигурация не должна разрастаться просто так, мы должны дорожить местом в ней, должны делать ее наиболее компактной. Но приложение ведь развивается, и таких макетов будет много и со временем их будет становиться всё больше.

Поэтому в случае с мобильным приложением предпочтителен другой путь - сохранять структуру метаданных непосредственно в файле с данными . На выходе у нас получается вот такой файлик, где вначале мы храним какие-то вспомогательные данные - версию конфигурации, схему конфигурации, границы последовательностей, в уже после записываем сами данные пользователей в формате XML. Причем, в разделе файла "Вспомогательные данные" можно также хранить и другие важные данные, которые по каким-то причинам не получилось записать в XML.

Берем ту схему данных, которую сохранили в файл, и на ее основании строим пакет XDTO для чтения файла. Создаем аналогичный объект в базе данных, заполняем его, выполняем обработки по дозаполнению при обновлении, и сохраняем уже готовый объект в базу данных.

Ниже на картинке можно посмотреть подсказку, как красиво записать модель XDTO данных конфигураций. В компании, выпустившей приложение Boss, экспериментировали с этим, находили несколько способов, но остановились именно на таком варианте записывания схемы метаданных. Когда открывается сам файл с данными, там виден обычный структурированный XML, читабельный, в котором перечислены все метаданные приложения.

// Запись схемы конфигурации МодельXDTO = ФабрикаXDTO.ЭкспортМоделиXDTO("http://v8.1c.ru/8.1/data/enterprise/current-config"); ФабрикаXDTO.ЗаписатьXML(ФайлВыгрузки, МодельXDTO); // Чтение схемы конфигурации МодельXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ФабрикаXDTO.Тип("http://v8.1c.ru/8.1/xdto","Model")); ФабрикаВыгрузки = Новый ФабрикаXDTO(МодельXDTO);

Чтобы обезопасить пользователя, нужно обязательно переспрашивать его, а нужно ли ему восстановление бэкапа. Может, он просто экспериментировал и нажимал на все подряд кнопочки в приложении:) И сейчас текущие данные у него могут потеряться. Поэтому мы всегда при выполнении потенциально "опасных" действий уточняем, а действительно ли он этого хочет, и как это должно происходить. Пользователь должен отдавать себе отчет в своих действиях.

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

Однако пользователи используют бэкапы не всегда так, как мы от них ожидаем:) Они очень часто используют их для того, чтобы просто «откатить» данные назад. Это правда очень странное поведение, но пользователям мобильных приложений лень разбираться, где они могли допустить ошибку при вводе данных, и они просто откатывают данные назад и заново заводят данные за текущий день. Проанализировав статистику работы с приложением Boss, мы осознали, что это нормальная практика и такое поведение пользователей встречается чаще, чем мы могли предположить.

И если у вас используется синхронизация с другими устройствами, то вы должны это обработать. Здесь есть несколько путей решения:

  • разорвать связь с сервером, уточняя, что данные на нем останутся такими, как были, а копия восстановится только на устройстве пользователя;
  • лучше для пользователя - дать ему восстановить копию сразу на всех устройствах, предварительно прописав такие механизмы.

Тут есть еще один момент. До сих пор мы бэкапы сохраняли сами, контролировали весь процесс, прямо в коде отлавливали действия пользователя, когда он нажимал кнопку «сохранить копию». Все это можно потом обработать. В платформе 8.3.9 появилась возможность сохранять бэкапы именно средствами платформы. И пользователь делает это без нашего ведома. Если используется синхронизация с центральной базой, то нужно обязательно обработать такой сценарий. Мы должны как-то на своем сервере узнать, что пользователь восстановил ранее сохраненную копию и должны выдать ему какое-то решение. Мы не можем себе позволить, чтобы данные рассинхронизировались.

ОБМЕНЫ

Когда мы говорим про частное решение на мобильной платформе, то у нас, как правило, есть заказчик, который, например, хочет использовать мобильную платформу для своих торговых агентов, и чтобы они обменивались данными с центральной базой. Здесь все просто: одна база данных, несколько устройств, вы поднимаете сервер, настраиваете связь с ним. Так что проблема обмена между устройствами решается легко.

Но если мы говорим о массовом приложении, где много баз данных, у каждой из которых очень много пользователей, ситуация усложняется. Пользователи скачали приложение с маркета, и они хотят синхронизироваться друг с другом. Например, муж скачал приложение для учета личных финансов, и теперь хочет, чтобы жена тоже подключилась, и они вместе работали в одном приложении. Пользователей много, приложение развивается, растет, и появляется необходимость в большом-пребольшом количестве баз данных. Как это все организовать? Пользователи же не будут персонально обращаться к разработчикам, чтобы те создали для них отдельную базу и включили возможность синхронизации. Они хотят нажать на кнопочку, и чтобы все сразу же заработало. В тот же момент.

Как поступить? Тут на помощь приходит механизм разделения данных. Он позволяет организовать единую базу данных, где есть одна общая конфигурация, но при этом в рамках одной общей базы хранится неограниченно много баз пользователей.

Самое приятное, что можно динамически, программно, без нашего участия добавлять пользователей. Реально пользователи просто нажимают на кнопочку «зарегистрироваться на сервере», и все само происходит: ему создается персональная база на сервере, и он может тут же начинать работать в ней.

Как это сделать? Первое и самое просто решение - написать свою серверную базу с этим механизмом. Когда наша компания начинала делать приложение Boss и обмены в нем, в первой версии мы так и сделали: написали серверную базу с механизмом разделения данных. Все работало, тем более что ничего сложного не было - разделителем баз является общий реквизит.

Но потом мы поняли, что изобретаем велосипед:) На самом деле есть уже готовое решение, причем в нем уже учтены моменты, о которых мы еще даже не думали. Это 1С:Фреш.

Здесь продуманна масштабируемость сервиса: что делать, когда будет очень много данных и баз, как расти с этим всем. Здесь есть момент о создании резервных копий областей данных: то есть мы не просто делаем бэкап одной общей базы данных, мы делаем копии конкретного пользователя. Причем, механизм там такой, что копии делаются только тогда, когда они реально нужны. Если пользователь не заходил неделю в базу, то мы не делаем ему копии, потому что там ничего не изменилось. Еще одна фишка Фреш - в сервисе реализован механизм для снижения нагрузки на сервер, а это очень важно, когда у вас много баз.

В целом Фреш для нас - что-то новое и интересное. Потихоньку мы пытаемся разобраться в нем, но в большинстве своем мы просто довольны его работой.

Передача данных. Как реализовать ее для обмена между устройствами

Платформа предоставляет два механизма - это SOAP и http сервисы. Тут есть нюансы, как обращаться к этим сервисам, когда задействован механизм разделения данных. В частности, нужно добавить параметры, которые указывают конкретный номер области, к которой вы обращаетесь, потому что по имени пользователя платформа не может определить, к какой базе обращаться. Кроме того, один и тот же пользователь может работать с несколькими базами в рамках единой базы (см. картинку).

Что касается сервисов, в приложении Boss реализован мгновенный обмен: один пользователь вводит данные, а другой их получает. Пользователи мобильных приложений привыкли ведь, что все происходит моментально, поэтому мы задумались, каким лучше сервисом пользоваться - SOAP или http. Ключевую роль сыграла скорость соединения. В http скорость соединения гораздо выше, а подключаясь по SOAP, мы получаем описание сервиса, которое тяжелое и долго грузится. У платформы есть способ хранить описание сервиса, но из-за параметров, которые мы добавляем динамически, мы не можем использовать WS-ссылки. Кроме того, обращение к http-сервисам удобнее и гибче, по нашему опыту.

Итак, наша цель - реализовать обмен в режиме реального времени. То есть мы стараемся не делать так, чтобы пользователю пришлось зайти куда-то, нажать на какую-то кнопку, думать о том, насколько актуальные у него данные, должен ли он проводить актуализацию…У пользователей данные всегда должны быть актуальны. Они так привыкли, работая в мессенджерах - один данные отправил, другой их тут же получил. Все происходит моментально. То же самое касается приложений, касающихся бизнеса: один продавец оформил продажу, другой должен тут же увидеть актуальную ситуацию, никаких действий при этом не совершая.

Поэтому в приложении Boss используются для обменов фоновые задания. После каждой записи данных в базу, запускается фоновое задание, которое инициирует обмен. Первая часть - отправить данные на сервер. Затем другие устройства должны узнать, что есть новые данные. Для этого мы используем PUSH-уведомления. Эта схема уже работающая и она работает достаточно быстро.

Но мы захотели еще быстрее, поскольку работаем в режиме реального времени и данных у нас, как правило, немного. У нас маленькие XML, но при этом мы отправляем с первого устройства на сервер сообщение с этими данными, сервер отправляет PUSH на другое устройство, а то второе устройство после получения PUSH-а инициирует обмен со своей стороны, обращается к серверу и запрашивает данные, получает эти данные и затем отправляет ответ, что данные получены. Это долго, а ведь самих данных было совсем немного.

Мы задумались, как этот процесс можно ускорить.

Для этого мы разобрались, что содержит в себе PUSH, как его можно еще использовать. Оказалось, что PUSH содержит такие поля, как данные и текст. В документации iOS и Android указаны ограничения по размеру PUSH-сообщений, но нам этого показалось мало и мы захотели сами опытным путем во всем разобраться. И мы проверили, что для iOS сумма допустимых символов составляет 981 знак, а для Android - 3832 символа. В последнем случае ограничением вполне можно пользоваться, в такой объем можно впихнуть один или несколько объектов базы. И тогда разработчики компании изменили немного схему. Когда данных немного, мы отправляем их с одного устройства, получаем их на сервере, там упаковываем их в PUSH и прямо в нем же отправляем на другое устройство. Схема стала короче, а обмен стал происходить еще быстрее:)

Важный момент использования PUSH - не раздражаем пользователей.

Очень легко избавиться от такой ситуации: просто не отправлять пользователю много PUSH-сообщений:) Если он работает сейчас в приложении, отправлять много сообщений можно. Когда платформа работает, пользователь не видит PUSH, у него все происходит автоматически. А вот когда приложение закрыто, у клиента появляется много непрочитанных сообщений. Поэтому ни в коем случае нельзя отправлять следующие PUSH, пока не получен ответ от устройства, что приложение работает, активно и предыдущий PUSH уже обработан.

Еще один нюанс обмена - это работа через веб. Нам нужно использовать асинхронность максимально. Вы не можете работать как обычно - написали код - вызвали функцию - подождали, пока она выполнится - получили ответ - и все ок. Если вы работаете через веб, вы все равно столкнетесь с определенными ограничениями, например, с нестабильным интернетом, срабатываением таймаутов при выполнении длительных операций. Поэтому надо заранее продумывать архитектуру.

Посмотрим на примере регистрации устройства, что происходит в приложении, когда пользователь хочет зарегистрироваться. Он ведет учет какое-то время, он ввел достаточно много данных, но потом он хочет, чтобы продавец тоже работал с этой базой. Пользователь нажимает на кнопку «зарегистрироваться». Вначале все было очень просто: взяли его данные, записали на сервере, и, пожалуйста, можно работать и подключать пользователей. Но потом мы столкнулись с ситуацией, когда у некоторых пользователей базы данных на устройстве к моменту регистрации уже сильно разростались. И эта схема уже не работала, т.к. пока шла запись всей базы на сервере, срабатывал таймаут соединения или просто обрывался интернет. Поэтому мы заменили один синхронный вызов на множество коротких. Сейчас данные делятся, а не передаются все за один раз. Мы не ждем ни в коем случае, пока сервер будет обрабатывать и записывать данные. Отправили данные, получили ответ, что данные получены, закрыли соединение. Периодически надо опрашивать сервер, что там и как происходит, а тем временем на сервере работает фоновое задание, которое записывает полученные данные. Таким образом, получается много вызовов сервера, но у нас есть гарантия того, что все пройдет хорошо. И ни таймауты, ни нестабильность интернета не помешают произвести выгрузку всех данных на сервер.

ОБНОВЛЕНИЯ

Обмен между устройствами с разными версиями приложения

Поскольку мы говорим о массовом приложении, которое выпускается в маркеты, надо учитывать некоторые особенности процесса обновлений и обмена данными.

Если вы выпустили приложение для одного предприятия и решили его обновить, то обычно вы просто даете команду, чтобы все сотрудники дружно установили новое приложение. С пользователями, которые скачали приложение из маркета, так сделать нельзя. Вы вообще не можете им указывать, что им делать. К примеру, они работают в приложении и не хотят обновлять его ни сейчас, ни вообще никогда. У них не стоит автообновление, поэтому совершенно обычная ситуация, когда к центральной базе подключено несколько устройств, и все они с разными версиями. Еще одна причина такого явления - время публикации в маркетах: оно разное для iOS и Android. Мы часто внедряем ключевые штуки, например, исправляем критические ошибки, и не хотим ждать, пока iOS проверяет две недели новую версию, мы хотим хотя бы только для Android, но выпустить обновление прямо сейчас.

Мы не имеем права командовать пользователями. Если они хотят, то обновляются, а если нет - то ничего не делают. На картинке видно соотношение установок приложения Boss по версиям в GooglePlay, а также статистика с нашего сервера - реальное соотношение версий приложения, которые установлены на устройствах, обменивавшихся с сервером данными в течение последней недели. Вот такой набор, с которым надо работать. Это разные версии и разные метаданные. И нам надо организовать нормальный обмен при этом:)

Перед разработчиками стоят следующие задачи:

  • Надо, чтобы все это работало. Пользователи не должны чувствовать дискомфорта от того, что они забыли обновиться. Они вообще не должны этого замечать. Обновились - стало лучше, ну и хорошо.
  • Мы должны обеспечить сохранность данных. Например, у одного пользователя появился справочник и новый реквизит, а у другого еще нет. При этом если пользователь, у которого новых реквизитов нет, изменит у себя на устройстве что-то, то на других устройствах данные не должны пропасть.
  • Надо обеспечить актуализацию данных, когда мы переходим на новую версию. Когда пользователь решит, что он готов обновиться, у него автоматически должны появиться все новые сведения, которых у него не было только потому, что у него была старая версия.

Как мы это сделали?

1. Мы используем на сервере 2 плана обмена. Первый - для обмена между устройствами, а второй - для обновлений. Например, мы отправили справочник пользователю, но у него нет единиц измерения, то есть неполные данные. Мы должны запомнить это. А когда он обновится, мы должны отправить ему все те сведения, которых у него не было. Для этого и нужен второй план обмена.

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

Как мониторить массовые ошибки при обмене и на сервере

Во-первых, надо контролировать доступность самого сервера. С серверами такое бывает - они падают. Мы не выдумывали ничего особенного для мониторинга, а просто нашли бота в телеграмме, который кричит, если что-то не так. Он каждую минуту проверяет работоспособность сервера, и если вдруг сервер недоступен, начинает кричать, админы это видят и поднимают сервер.

Также мы собираем лог ошибок из журнала регистрации. Тоже ничего сверхъестественного - просто каждые три часа собираем лог ошибок, отправляем их на почту, периодически их просматриваем. Это помогает видеть частые проблемы и какие-то исключительные ситуации. Не сложно просматривать почту, отслеживать и быстро исправлять ошибки. Но это позволяет оперативно выявлять и решать проблемы, которые могут разрастаться с ростом баз данных.

Еще важный момент - обязательно давать пользователю возможность "пожаловаться". Это улучшает наш статус в их глазах и спасает нас. Есть пользователи, как мы их называем, "истерички", которые при малейшей ошибке начинают отправлять нам на почту кучу сообщений, что ничего не работает, база не загружается, все страшно плохо. Но иногда они реально нас спасают, потому что находят порой такие баги, которые остальные еще чудом не обнаружили, серьезные баги.

Пользователя нельзя пугать. Ни страшными сообщениями, ничем другим. Им надо красиво все объяснять и предлагать пожаловаться. А мы обещаем все решить. Тогда и пользователи довольные, ведь они видят, что о них заботятся, и сразу верят, что им помогут:)

Данная статья написана по итогам доклада, прочитанного на конференции INFOSTART EVENT 2016 DEVELOPER. Больше статей можно прочитать .

В 2020 году приглашаем всех принять участие в 7 региональных митапах, а также юбилейной INFOSTART EVENT 2020 в Москве.

Разработка серверной части приложения

Введение

Присутствие в интернете стало необходимостью для современных компаний. Без этого невозможно выстроить полноценного взаимодействия с клиентами. Часто для решения подобной задачи прибегают к созданию клиент-серверных приложений. Каждое из них состоит из клиентской части и Back-end. Под последним термином подразумевается серверная часть приложения. Если в дальнейшем нужно самостоятельно изменять контент мобильной программы, то Back-end должен быть создан особенно качественно. Компания Appomart гарантирует выполнение поставленных задач в соответствии предъявляемым требованиям. Поэтому, заказывая создание серверных приложений, можете быть уверены в надлежащем результате.

Для чего нужен Back-end?

Разработка клиент-серверных приложений подразумевает создание двух частей. Первая, Front-end, принимает запросы от пользователей. Она видна с экранов мобильных устройств клиентов. Вторая, серверное приложение, обрабатывает полученные запросы, выполняет роль административной панели. Здесь хранятся базы данных, логика программы. Без этого не будет работать ни одно клиент-серверное приложение. По сути Back-end - это сердце программы. Это интеллект, который отвечает за обработку запросов клиентов, скорость работы приложения. Поэтому важно, чтобы архитектура серверного приложения была продумана до мелочей, чтобы даже высоконагруженные сервисы работали бесперебойно и быстро.

Как выбрать язык программирования?

В ходе подготовки технического задания (части рабочей документации по проекту), архитектор проектирует систему баз данных и связей, описывает объекты и их свойства, а также разрабатывает необходимые методы сервера (запросы, которыми будут "пользоваться" мобильные приложения обращаясь к серверу).

Важность документации и "брошенные" проекты

В Appomart довольно часто обращаются заказчики, которых "бросили" по тем или иным причинам другие подрядчики. И мы берем чужой, порою даже некорректно работающий проект, осуществляем его аудит и последующую доработку и поддержку. В процессе изучения исходного кода и материалов, полученных от заказчика, мы сталкиваемся с тем, что многие разработчики намеренно не документируют методы сервера, чтобы привязать к себе клиента, за счет несоизмеримости трудозатрат передачи проекта в поддержку другому разработчику, ввиду отсутствия документации к серверной части, а порой просто из-за непрофессионализма. Данный факт, к сожалению, является не только печальным но и распространенным. Заказчику, в этом случае, необходимо оплачивать разработку документации по существующему проекту, а также аудит исходного кода, прежде чем можно будет судить о работоспособности, удобстве и целесообразности поддержки проекта. Сотрудники Appomart всегда ведут электронную документацию методов серверной части в формате, поддерживаемом Postman и Swagger, для последующего использования.

Как проверить подрядчика до подписания договора?

Мы призываем Вас внимательно выбирать подрядчика, и ориентироваться не только на заманчивую цену, но и на перечень документов, которые вы получите вместе с проектом, а также условия передачи исходного кода, и покрытие кода комментариями, схемы баз данных (будь то Mongo DB или MySQL). Залогом успеха, как правило становится грамотная рабочая документация, которая явно указывает на требования к передаваемой Вам по завершению каждого из этапов работы материалов.

Особенности разработки

PHP для серверной части

Создание серверной части приложений (не путать с серверами как "железом" или компьютерами, так как речь идет о программной части) требует специфических профессиональных навыков и знания языка программирования, который применяется на стороне сервера. Если рассматривать примеры клиент-серверных приложений, то видно, что популярностью пользуется PHP. Это бесспорный лидер в области разработки серверных приложений. На этом языке написано в той или иной конфигурации более половины сайтов в мире. PHP удобен для разработки и поддержки, и кроме того существуют специальные framework-и для ускорения разработки на PHP.

Framework

Framework (программная платформа) - используется для систематизации и повышения уровней абстракции, что позволяет сделать проект более гибким и масштабируемым. Однако, стоит понимать, что framework должен быть выбран корректно, на основе глубинного анализа рабочей документации проекта, без которой невозможно разработать качественный продукт.

Delphi, JAVA, Python

Есть и другие языки, которые используются для создания Back-end. Так, распространены созданные в среде Delphi серверные приложения. С ее помощью программа получает улучшенную отладку, в среде также просто сформировать уникальные программы, предусмотрено визуальное создание, что дает возможность сделать красивый, понятный и удобный интерфейс. Также популярность получили серверные приложения на Java. Такие легко дополняются, легко исполняются на любых платформах и отличаются достойным уровнем безопасности. Еще одним популярным языком считается Python. Серверные приложения с его помощью создаются быстро, просто, без серьезных затрат.

Распространение

Создание клиент-серверных приложений пользуется спросом в корпоративной среде. Часто подобные программы используются для рабочих групп или создания информационных систем внутри предприятия. Подавляющая часть мобильных приложений для поддержания связи с клиентом также имеет подобную архитектуру. Популярность обусловлена тем, что использование возможностей сервера позволяет обеспечить контроль и целостность системы, уменьшая при этом нагрузку сети.

Создадим клиент-серверное приложение Android, iOS качественно и в срок

Разработка "под ключ"

Программисты Appomart опытны и квалифицированы, чтобы реализовать задачи самых разнообразных уровней. Мы одинаково хорошо реализуем социальные сети, высоконагруженные бизнес-проекты, или программную часть для небольших стартапов. При необходимости мы создадим клиентскую часть приложения под управлением Android, iOS в соответствии с имеющимися потребностями, предъявляемыми требованиями.

Back-end в Appomart

Наши программисты работают с различными технологиями и делают это в равной степени хорошо. В Appomart вы можете заказать клиент-серверное приложение на Java, PHP и Node.JS. Системные требования анализируются для каждого из проектов индивидуально, что позволяет обеспечить оптимальную производительность программы. Создадим клиент-серверное приложение Java, PHP и Node.JS с нуля или возьмем в саппорт существующее для улучшений и обновлений. Если Вас интересует разработка новой серверной части или поддержка существующей - оставьте заявку, чтобы получить подробный расчет стоимости работ и вариантов сотрудничества.

Значительная часть современных приложений для мобильных платформ (iOS, Android и т.п.) работает в паре с сервером. Приложение с устаревшими данными теряет свою полезность. Поэтому важно обеспечить постоянное обновление данных с сервера на устройство. Это касается оффлайн приложений, которые должны работать и без интернета. Для полностью онлайн приложений, которые не работают (или бесполезны) без интернета (например, Foursquare, Facebook) есть своя специфика, которая выходит за рамки текущей статьи.

Я расскажу на примере одного нашего оффлайн приложения, какие подходы мы использовали для синхронизации данных. В первых версиях мы разработали простые алгоритмы и, в дальнейшем, с опытом, мы их совершенствовали. Аналогичная последовательность представлена и в статье – от простых очевидных практик к более сложных.

Следует уточнить, что в статье рассматривается передача данных только в одну сторону: от сервера к устройству. Здесь сервер является источником данных.

Общие положения для всех подходов

Для примера, мы будем рассматривать передачу на устройство справочника блюд (“dishes”). Будем считать, что устройство делает запрос на url “/service/dishes/update”, обмен идет по протоколу http в формате JSON (www.json.org). На сервере есть таблица “dishes” с полями: id (идентификатор записи), name (наименование блюда), updated (момент обновления блюда, лучше сразу делать поддержку timezone, “YYYY-MM-DDThh:mm:ssTZD”, например, “1997-07-16T19:20:30+01:00”), is_deleted (признак удаленной записи).

Ремарка касаемо наличия последнего поля. По умолчанию его значение равно 0. В приложении, где сущности синхронизируются между клиентом и сервером, физически удалять данные с сервера не рекомендуется (чтобы не было ошибок). Поэтому у удаленных блюд выставляется is_deleted = 1. По приходу на устройство сущности с is_deleted = 1 она удаляется с устройства.

При любом подходе, который будет рассматриваться ниже, сервер возвращает на устройства JSON массив объектов (может быть пустой):

[
{id: ,name: ,updated:,isDeleted: },…
]

Пример ответа сервера:

[
{id: 5625,name: "Bread",updated: "2013-01-06 06:23:12",isDeleted: 0},
{id: 23,name: "Cooked semolina",updated: "2013-02-01 14:44:21",isDeleted: 0},{

name: "Fish-soup",

updated: "2013-08-02 07:05:19",

Принципы обновления данных на устройстве

  1. Если пришел элемент, который есть на устройстве, и isDeleted = 0, то он обновляется
  2. Если пришел элемент, которого нет на устройстве, и isDeleted = 0, то он добавляется
  3. Если пришел элемент, который есть на устройстве, и isDeleted = 1, то он удаляется
  4. Если пришел элемент, которого нет на устройстве, и isDeleted = 1, то ничего не делается

Подход 1: Синхронизируется всегда все

Это самый простой метод. Устройство запрашивает список блюд у сервера и сервер отсылает весь список целиком. Каждый раз список приходит весь. Не сортированный.

Пример запроса: null, либо “{}”

Достоинства:

  • логика на сервере простая – отдаем всегда все
  • логика на устройстве простая – перезаписываем всегда все

Недостатки:

  • если запрашивать список часто (каждые 10 минут), то будет большой интернет трафик
  • если запрашивать список редко (раз в день), то будет нарушена актуальность данных

Область применения:

  • для приложений с небольшим трафиком
  • передача очень редко меняющихся данных (список городов, категорий)
  • передача настроек приложения
  • на начале проекта для самого первого прототипа мобильного приложения

Подход 2: Синхронизируется только обновленное

Устройство запрашивает список блюд, обновленный с предыдущей синхронизации. Список приходит отсортированный по “updated” в порядке возрастания (необязательно, но удобно). Устройство хранит значение “updated” у самого последнего присланного блюда и при следующем запросе шлет его серверу в параметре “lastUpdated”. Сервер присылает список блюд, которые новее “lastUpdated” (updated > lastUpdated). При первом запросе на сервер “lastUpdated” = null.

Пример запроса: { lastUpdated: “2013-01-01 00:00:00” }

На схеме: “last_updated” – значение, которое хранится на устройстве. Обычно на устройстве создается отдельная таблица для хранения этих значений “last_updated” по каждой сущности (блюда, города, организации и т.п.)

Этот подход годится для синхронизации простых линейных списков, у которых правила прихода на устройство одинаковые для всех устройств. Для более избирательной синхронизации см “Подход 5: Синхронизация со знанием того, что уже есть на устройстве”.

Обычно этот подход перекрывает большинство нужд. На устройство приходят только новые данные, синхронизироваться можно хоть каждую минуту – трафик будет небольшой. Однако тут возникают проблемы, связанные с ограничениями мобильных устройств. Это память и процессор.

Подход 3: Синхронизация порциями

У мобильных устройств мало оперативной памяти. Если в справочнике 3000 блюд, то парсинг большой json строки от сервера в объекты на устройстве может вызвать нехватку памяти. В этом случае приложение либо аварийно завершится, либо не сохранит эти 3000 блюд. Но даже если устройство смогло переварить такую строку, то производительность приложения в моменты синхронизации в фоне будет низкая (лаги интерфейса, не плавная прокрутка и т.п.) Поэтому необходимо запрашивать список более мелкими порциями.

Для этого устройство передает еще один параметр (“amount”), который определяет размер порции. Список присылается обязательно отсортированный по полю “updated” по возрастанию. Устройство, аналогично предыдущему подходу, запоминает значение “updated” у последней присланной сущности и передает его в поле “lastUpdated”. Если сервер прислал ровно это же количество сущностей, то устройство продолжает синхронизацию и снова делает запрос, но уже с обновленным “lastUpdated”. Если сервер прислал меньше сущностей, это значит, больше новых данных у него нет, и синхронизация завершается.

На схеме: “last_updated” и “amount” – значения, которые хранятся в мобильном приложении . “last_item” – последняя присланная с сервера сущность (блюдо). Именно новее этого значения будет запрошен следующий список.

Пример запроса: { lastUpdated: “2013-01-01 00:00:00”, amount: 100 }

Достоинства:

  • На устройство приходит столько данных, сколько оно в состоянии обработать за один раз. Размер порции определяется практическими тестами. Простые сущности можно синхронизировать по 1000 штук за раз. Но бывает и такое, что сущности с большим количеством полей и сложной логикой обработки сохранения синхронизируются нормально не более 5 штук.

Недостатки:

  • Если будет 250 блюд с одинаковым updated, то при amount = 100 последние 150 не придут на устройства. Такая ситуация вполне реальна и описана в следующем подходе.

Подход 4: Корректная синхронизация порциями

В предыдущем подходе возможна ситуация, что если в таблице есть 250 блюд с одинаковым “updated” (например, “2013-01-10 12:34:56”) и размер порции равен 100, то придут только первые 100 записей. Остальные 150 будут отсечены жестким условием (updated > lastUpdated). Почему так произойдет? При запросе первых 100 записей lastUpdated установится в “2013-01-10 12:34:56”, и следующий запрос будет иметь условие (updated > “2013-01-10 12:34:56”). Не поможет даже смягчение условия (updated >= “2013-01-10 12:34:56”), потому что устройство тогда будет бесконечно запрашивать первые 100 записей.

Ситуация с одинаковым “updated” не настолько редкая. Например, при импорте данных из текстового файла поле “updated” было выставлено в NOW(). Импорт файла с тысячами строк может занять меньше секунды. Может случиться и так, что весь справочник будет иметь одинаковый “updated”.

Чтобы это исправить надо использовать какое-то поле блюда, которое было бы уникальным хотя бы в пределах одного момента (“updated”). Поле “id” уникально вообще по всей таблице, так что следует дополнительно в синхронизации использовать именно его.

Итого, реализация этого подхода выглядит так. Сервер отдает список отсортированный по “updated” и “id”, а устройства запрашивают данные с помощью “lastUpdated” и нового параметра “lastId“. У сервера условие выборки усложняется: ((updated > lastUpdated) OR (updated = lastUpdated and id > lastId)).

На схеме: “last_updated”, “last_id” и “amount” – значения, которые хранятся в мобильном приложении. “last_item” – последняя присланная с сервера сущность (блюдо). Именно новее этого значения будет запрошен следующий список.

Подход 5: Синхронизация со знанием того, что уже есть на устройстве

Предыдущие подходы не учитывают факта, что сервер в реальности не знает насколько успешно данные сохранились на устройстве. Устройство могло просто не сохранить часть данных из-за невыясненных ошибок. Поэтому неплохо было бы получать от устройства подтверждение, что все (или не все) блюда сохранились.

Кроме этого, пользователь приложения может так настроить приложение, что ему нужна будет только часть данных. Например, пользователь хочет синхронизировать блюда только из 2 городов из 10. Описанными выше синхронизациями этого не добиться.

Идея подхода следующая. Сервер хранит у себя (в отдельной таблице “stored_item_list”) информацию о том, какие блюда есть на устройстве. Это может быть просто список пар “id – updated”. В этой таблице хранятся все списки пар “id – updated” блюд для всех устройств.

Информацию об имеющихся на устройстве блюдах (список пар “id – updated“) устройство отсылает серверу вместе с запросом на синхронизацию. При запросе сервер проверяет то, какие блюда должны быть на устройстве и какие есть сейчас. После этого на устройство отсылается разница.

Как сервер определяет, какие блюда должны быть на устройстве? В простейшем случае сервер делает запрос, который возвратит ему список пар “id – updated” всех блюд (например, SELECT id, updated FROM dishes). На схеме это делает “WhatShouldBeOnDeviceMethod()” метод. В этом недостаток подхода – серверу приходится вычислять (порой делая тяжелые sql-запросы), что должно быть на устройстве.

Как сервер определяет какие блюда есть на устройстве? Он делает запрос в таблицу “stored_item_list” по этому устройству и получает список пар “id – updated”.

Анализируя эти два списка, сервер решает, что следует послать на устройство, а что – удалить. На схеме это “delta_item_list”. Поэтому в запросе нет “lastUpdated” и “lastId”, их задачу выполняют пары “id – updated”.

Как сервер узнает об имеющихся на устройстве блюдах? В запросе к серверу добавляется новый параметр “items”, который содержит список id блюд, которые были присланы на устройство в прошлой синхронизации (“device_last_stored_item_list”). Конечно, можно отсылать список id всех блюд, которые есть на устройстве, и не усложнять алгоритм. Но если на устройстве 3000 блюд и они будут каждый раз все отсылаться, то расходы трафика будут очень велики. В подавляющем количестве синхронизаций параметр “items” будет пустой.

Сервер должен постоянно обновлять у себя “stored_item_list” данными, которые пришли с устройства в параметре “items”.

Следует реализовать механизм очистки серверных данных в stored_item_list. Например, после переустановки приложения на устройстве сервер будет считать, что на устройстве все еще актуальные данные. Поэтому при установке приложения устройство должно как-то проинформировать сервер чтобы он очистил stored_item_list по этому устройству. В нашем приложении мы посылаем дополнительный параметр “clearCache” = 1 в этом случае.

Заключение

Сводная таблица по характеристикам этих подходов:

Подход Объем трафика (5 – большой) Трудоемкость разработки (5 – высокая) Использование памяти устройства (5 – высокое) Корректность данных на устройстве (5 – высокая) Можно выделить конкретное устройство
1 Синхронизируется всегда все 5 1 5 5 нет
2 Синхронизируется только обновленное 1 2 5 3 нет
3 Синхронизация порциями 1 3 1 3 нет
4 Корректная синхронизация порциями 1 3 1 3 нет
5 Синхронизация со знанием того, что уже есть на устройстве 2 5 2 5 да

“Корректность данных на устройстве” – это вероятность того, что на устройстве есть все данные, которые отсылались сервером. В случае подходов №1 и №5 есть 100% уверенность, что устройство имеет все данные, которые нужны. В остальных случаях такой гарантии нет. Это не говорит о том, что остальные подходы использовать нельзя. Просто если на устройстве часть данных пропадет, то исправить это с сервера (а тем более узнать про это на стороне сервера) не получится.

Возможно, при наличии безлимитых тарифов на интернет и бесплатного wifi проблема ограничения трафика, генерируемого мобильным приложением, станет менее актуальна. Но пока приходится идти на всякие ухищрения, придумывать более “умные” подходы, которые позволяют снизить сетевые расходы и увеличить производительность приложений. Не всегда это работает. Порой бывает “чем проще – тем лучше”, зависит от ситуации. Надеюсь, из этой статьи можно подобрать подход, который пригодится.

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

Наша компания предлагает услуги создания серверной части мобильных бизнес-приложений и клиентских веб-сервисов, работающих в средах с высокой нагрузкой. При разработке каждого проекта мы стараемся применить индивидуальный подход, чтобы полученный продукт стал наиболее оптимальным решением конкретных целей заказчика.

Если Вы используете сложное приложение, которое хранит и/или обрабатывает данные на сервере, то за ним стоит Back-end – программный комплекс, размещенный на веб-сервере и работающий с приложением, которое в этом случае называется Front-end. Размещенное на сервере приложение может работать одновременно с большим количеством клиентов, что накладывает требования на высокую скорость и безопасность его функционирования.

Часто серверная часть приложения пишется на языке PHP, как наиболее популярном для подобных решений. Для реализации простых серверных задач могут использоваться стандартные системы, а вот для более специфичных уже требуется разработка собственного решения либо надстроек над стандартными системами.

Принципы разработки сервера для мобильного приложения

Наши программисты работают с технологиями, позволяющими реализовать широкий диапазон решений для любой, даже очень высокой нагрузки и различных направлений. Также мы создаем отдельные серверные решения для индивидуальных задач.

Организационный контроль

Каждый проект создается отдельной группой специалистов, ответственных за все этапы разработки и сдачу проекта в срок.

Программирование

Проектирование архитектуры сервера – самый важный шаг, в процессе которого создаются базы данных и формируются нужные алгоритмы.

Тестирование

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

Техническая поддержка

Наши сотрудники выполняют полноценную техподдержку программ, что позволяет оперативно устранять недостатки и вносить обновления.

Особенности разработки

Для того чтобы грамотно разработать серверную часть приложения, требуются определенные навыки и знание языка программирования, используемого на сервере. Как показывает практика, клиент-серверные приложения создаются на языке PHP. Он является бесспорным лидером в данной области. На PHP написано больше половины сайтов во всем мире, он удобен для разработки и поддержки.

Framework

Данная программная платформа позволяет сделать проект более масштабируемым и гибким. Тем не менее, фреймворк должна быть выбрана максимально корректно, поэтому требуется проведения глубокого анализа рабочей документации проекта, что впоследствии поможет разработать продукт высокого качества.

Существуют другие языки, используемые для разработки Back-end. К примеру, популярны серверные приложения, созданные в среде Delphi. За счет нее программа имеет улучшенную отладку. Кроме того, в ней проще разработать уникальные программы, она предусматривает визуальное создание. Все это позволяет сделать понятный и удобный интерфейс.

Не менее популярны серверные приложения на Java. Они без проблем дополняются, легко исполняются на разных платформах, имеют высокий уровень безопасности.

Еще один часто используемый язык. С его помощью серверные приложения создаются легко, быстро и без лишних затрат.

Практически все современные компании имеют свои виртуальные офисы. Веб-сайт может быть либо визитной карточкой, либо порталом или онлайн-каталогом с возможностью оформления заказов.

Бизнес-процессы в данном случае зависимы от веб-серверов, а именно их возможности противостоять атакам, попыткам взлома и внешним негативным воздействиям, а также производительности, достаточной для множества одновременно принимаемых запросов.

Этапы разработки веб-сервиса

Создавая приложения для разных сегментов рынка, свою работу мы организуем по единому принципу – разбиваем весь процесс на отдельные шаги, о ходе выполнения и результатах которых сообщается заказчикам. Таким образом, и сервер для мобильного приложения разрабатывается аналогично.

1. Разработка идеи

До 2-х недель

На этом этапе происходит создание основы, дающей представление от том, что будет заложено и в каком направлении будет развиваться.

2. Оценка проекта

2-3 недели

Наши специалисты проводят оценку сроков и стоимости работ, затем составляется предварительное предложение, касающееся разработки.

3. Техзадание и договор

До 2-х недель

После обсуждения с заказчиком всех нюансов процесса и составления подробного ТЗ подготавливается и подписывается договор.

4. Разработка интерфейса

2-3 недели

Созданием интерфейсов, необходимых при написании программных модулей, занимаются дизайнеры.

6. Тестирование

2-3 недели

Всесторонняя проверка полученного программного решения производится тестировщиками посредством набора соответствующих инструментов.

7. Завершение проекта

До 2-х недель

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

Наша команда

Благодаря анализу коммерческой деятельности и потребностей своих клиентов мы создаем реально работающие продукты, способствующие решению целого ряда бизнес-задач. Использование современных технологий обеспечивает широкий спектр возможностей для реализации серверного программного обеспечения, что гарантирует высокую производительность соответствующих мобильных приложений. Наш коллектив представляют:

Менеджеры проектов

Эти сотрудники взаимодействуют и с клиентами, и с разработчиками, обеспечивая коммуникацию между ними. Они контролируют выполнение как уже запланированных действий, так и необходимых доработок.

Дизайнеры

В своей работе наши специалисты учитывают требования построения интерфейсов для операционных систем iOS и Android, поэтому выпускаемые приложения корректно работают на разных устройствах.

Разработчики

В целях оптимизации производительности мобильных приложений программисты анализируют их системные требования и создают специализированное серверное ПО.

Тестировщики

Тщательное тестирование – залог качества готового продукта и гарантия безопасности хранящихся и обрабатываемых данных. Эти специалисты используют разные инструменты и эффективную методику.

Какие сервисы мы создаем

Будучи встроенным в сайт ПО или самостоятельной программой, веб-сервис служит для выполнения задач, связанных с рекламой, аналитикой, планированием и продвижением бизнеса. В связи с этим необходимо определиться, ресурс какого вида станет оптимальным решением.

Информационные проекты

Предназначены для размещения разнопланового контента.

Тематические сайты

Практически все их страницы посвящены одной тематике. Спрос на них по-прежнему достаточно велик.

Новостные сайты

Информируют о различных новостях в рамках одной или нескольких тематик, отражающих основные жизненные сферы.

Блоги

Уровень популярности этих ресурсов постоянно растет. Как и новостные сайты, они доносят ту или иную информацию интернет-сообществу, но в данном случае авторы выражают свое личное мнение.

Социальные проекты

К ним относятся специализированные соц. сети, сообщества, форумы и т.д.

Форумы

Создаются для обсуждения различных новостей, товаров/услуг и проч. Могут быть как узконаправленными, так и разноплановыми.

Социальные сети

Эти ресурсы имеют многомиллионную аудиторию. Их основная задача – предоставить интернет-пользователям возможность общаться в режиме онлайн посредством текстовых / голосовых сообщений и видеосвязи.

Различные веб-сервисы

Получившие сегодня широкое распространение, они делятся на несколько видов.

Каталоги

Почтовые сервисы

Предоставляют пользователям все возможности и преимущества электронной почты, в числе которых просмотр, отправка, редактирование писем и документов и др.

Поисковые системы

Служат для поиска сайтов и различной информации по определенным запросам.

Доски объявлений

Это веб-ресурсы, где пользователи сети размещают свои объявления о купле-продаже и услугах в рамках различных тематик.

Сайты-хостинги

Предназначены для временного хранения файлов. В некоторых из них предусмотрена возможность ознакомиться с данными перед загрузкой.

Часто задаваемые вопросы

Ниже мы предлагаем ответы на вопросы, которые часто задают нашим специалистам. Если интересующую информацию Вы здесь не обнаружите, пожалуйста, задайте свой вопрос в этой форме , и мы обязательно на него ответим.

Сколько времени может занять создание приложения и веб-сервера?

В среднем эта работа длится от 9 до 20 недель. Все зависит от сложности реализуемой задачи.

Разработка серверной части клиент-серверного приложения начинается с проектирования архитектуры. От архитектуры зависим многое: начиная от расширяемости приложения и заканчивая его производительностью и удобством поддержки/сопровождения.

Прежде всего следует определить, как будут размещены данные на сервере и как будут обрабатываться запросы, приходящие со стороны клиента. Также необходимо продумать организацию серверного кэширования данных.

Необходимо определиться с протоколами обмена данными и форматами передачи данных.

API (application programming interface) – интерфейс прикладного программирования. Если выражаться более понятным языком, то это набор запросов к серверу, который последний понимает и может дать корректный ответ. API определяет функциональность серверной логики, при этом API позволяет абстрагироваться от того, как именно эта функциональность реализована. Другими словами, API – это необходимая часть общей клиент-серверной инфраструктуры.

Сравнить JSON и XML. Привести пример протоколов в зависимости от типа приложения.

Многопоточность

Одним из ключевых аспектов в современном программировании является многопоточность. При помощи многопоточности мы можем выделить в приложении несколько потоков, которые будут выполнять различные задачи одновременно.

Многопоточность – свойство платформы (например, операционной системы, виртуальной машины и т. д.) или приложения, состоящее в том, что процесс, порождённый в операционной системе, может состоять из нескольких потоков, выполняющихся «параллельно», то есть без предписанного порядка во времени.

Сутью многопоточности является квазимногозадачность на уровне одного исполняемого процесса, то есть все потоки выполняются в адресном пространстве процесса. Кроме этого, все потоки процесса имеют не только общее адресное пространство, но и общие дескрипторы файлов. Выполняющийся процесс имеет как минимум один (главный) поток.

Многопоточность (как доктрину программирования) не следует путать ни с многозадачностью, ни с многопроцессорностью, несмотря на то, что операционные системы, реализующие многозадачность, как правило, реализуют и многопоточность.

К достоинствам многопоточности в программировании можно отнести следующее:

Упрощение программы в некоторых случаях за счёт использования общего адресного пространства.

Меньшие относительно процесса временные затраты на создание потока.

Повышение производительности процесса за счёт распараллеливания процессорных вычислений и операций ввода-вывода.

Поток (threаd) – это управляемая единица исполняемого кода. В многозадачной среде, основанной на потоках, у всех работающих процессов обязательно имеется основной поток, но их может быть и больше. Это означает, что в одной программе могут выполняться несколько задач асинхронно. К примеру, редактирование текста в текстовом редакторе во время печати, т.к эти две задачи выполняются в различных потоках.

На обычном процессоре управление потоками осуществляется операционной системой. Поток исполняется до тех пор, пока не произойдёт аппаратное прерывание, системный вызов или пока не истечёт отведённое для него операционной системой время. После этого процессор переключается на код операционной системы, который сохраняет состояние потока (его контекст) или переключается на состояние другого потока, которому тоже выделяется время на исполнение. При такой многопоточности достаточно большое количество тактов процессора тратится на код операционной системы, переключающий контексты. Если поддержку потоков реализовать аппаратно, то процессор сам сможет переключаться между потоками, а в идеальном случае – выполнять несколько потоков одновременно за каждый такт.

– Временная многопоточность (один поток)

– Одновременная многопоточность (несколько потоков одновременно)

Многопоточность, как широко распространённая модель программирования и исполнения кода, позволяет нескольким потокам выполняться в рамках одного процесса. Эти потоки выполнения совместно используют ресурсы процесса, но могут работать и самостоятельно. Многопоточная модель программирования предоставляет разработчикам удобную абстракцию параллельного выполнения. Однако, пожалуй, наиболее интересное применение технологии имеется в том случае, когда она применяется к одному процессу, что позволяет его параллельное выполнение на многопроцессорной системе.

Это преимущество многопоточной программы позволяет ей работать быстрее на компьютерных системах, которые имеют несколько процессоров, процессор с несколькими ядрами или на кластере машин - из-за того, что потоки выполнения программ естественным образом поддаются действительно параллельному выполнению процессов. В этом случае программисту нужно быть очень осторожным, чтобы избежать состояния гонки, и другого неинтуитивного поведения. Для того, чтобы правильно манипулировать данными, потоки выполнения должны часто проходить через процедуру рандеву, чтобы обрабатывать данные в правильном порядке. Потокам выполнения могут также потребоваться мьютексы (которые часто реализуются с использованием семафоров), чтобы предотвратить одновременное изменение общих данных или их чтение во время процесса изменения. Неосторожное использование таких примитивов может привести к тупиковой ситуации.

Другим использованием многопоточности, применяемым даже для однопроцессорных систем, является возможность для приложения реагирования на ввод данных. В однопоточных программах, если основной поток выполнения заблокирован выполнением длительной задачи, всё приложение может оказаться в замороженном состоянии. Перемещая такие длительные задачи в рабочий поток, который выполняется параллельно с основным потоком, становится возможным для приложений продолжать реагировать на действия пользователя во время выполнения задач в фоновом режиме. С другой стороны, в большинстве случаев многопоточность – не единственный способ сохранить чувствительность программы. То же самое может быть достигнуто через асинхронный ввод-вывод или сигналы в UNIX.

Всего различают две разновидности многозадачности: на основе процессов и на основе потоков . Отличия многозадачности на основе процессов и потоков сводится к следующему: многозадачность на основе процессов организуется для параллельного выполнения программ, а многозадачность на основе потоков – для параллельного выполнения отдельных частей одной программы.

Всего различают два типа потоков:

Потоки переднего плана (foreground threads) или приоритетный. По умолчанию каждый поток, создаваемый через метод Thread.Start(), автоматически становится потоком переднего плана. Данный тип потоков обеспечивают предохранение текущего приложения от завершения. Среда CLR не остановит приложение до тех пор, пока не будут завершены все приоритетные потоки.

Фоновые потоки (background threads). Данный вид потоков называется также потоками-демонами, воспринимаются средой CLR как расширяемые пути выполнения, которые в любой момент времени могут игнорироваться. Таким образом, если все потоки переднего плана прекращаются, то все фоновые потоки автоматически уничтожаются при выгрузке домена приложения. Для создания фоновых потоков необходимо присвоить свойству IsBackground значение true.

Рассказать про состояния потоков: запущенный, приостановленный, запущенный, но ожидающий чего-то.

Проблема синхронизации потоков и общие ресурсы.

Взаимодействие потоков

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

Мьютекс – это объект синхронизации, который устанавливается в особое сигнальное состояние, когда не занят каким-либо потоком. Только один поток владеет этим объектом в любой момент времени, отсюда и название таких объектов (от английского mutually exclusive access - взаимно исключающий доступ) - одновременный доступ к общему ресурсу исключается. После всех необходимых действий мьютекс освобождается, предоставляя другим потокам доступ к общему ресурсу. Объект может поддерживать рекурсивный захват второй раз тем же потоком, увеличивая счетчик, не блокируя поток, и требуя потом многократного освобождения. Такова, например, критическая секция в Win32. Тем не менее, есть и такие реализации, которые не поддерживают такое и приводят к взаимной блокировке потока при попытке рекурсивного захвата. Это FAST_MUTEX в ядре Windows.

Семафоры представляют собой доступные ресурсы, которые могут быть приобретены несколькими потоками в одно и то же время, пока пул ресурсов не опустеет. Тогда дополнительные потоки должны ждать, пока требуемое количество ресурсов не будет снова доступно. Семафоры очень эффективны, поскольку они позволяют одновременный доступ к ресурсам.

События . Объект, хранящий в себе 1 бит информации «просигнализирован или нет», над которым определены операции «просигнализировать», «сбросить в непросигнализированное состояние» и «ожидать». Ожидание на просигнализированном событии есть отсутствие операции с немедленным продолжением исполнения потока. Ожидание на непросигнализированном событии приводит к приостановке исполнения потока до тех пор, пока другой поток (или же вторая фаза обработчика прерывания в ядре ОС) не просигнализирует событие. Возможно ожидание нескольких событий в режимах «любого» или «всех». Возможно также создание события, автоматически сбрасываемого в непросигнализированное состояние после пробуждения первого же – и единственного – ожидающего потока (такой объект используется как основа для реализации объекта «критическая секция»). Активно используются в MS Windows, как в режиме пользователя, так и в режиме ядра. Аналогичный объект имеется и в ядре Linux под названием kwait_queue.

Критические секции обеспечивают синхронизацию подобно мьютексам, за исключением того, что объекты, представляющие критические секции, доступны в пределах одного процесса. События, мьютексы и семафоры также можно использовать в однопроцессном приложении, однако реализации критических секций в некоторых ОС (например, Windows NT) обеспечивают более быстрый и более эффективный механизм взаимно-исключающей синхронизации – операции «получить» и «освободить» на критической секции оптимизированы для случая единственного потока (отсутствия конкуренции) с целью избежать любых ведущих в ядро ОС системных вызовов. Подобно мьютексам объект, представляющий критическую секцию, может использоваться только одним потоком в данный момент времени, что делает их крайне полезными при разграничении доступа к общим ресурсам.

Условные переменные (condvars). Сходны с событиями, но не являются объектами, занимающими память – используется только адрес переменной, понятие «содержимое переменной» не существует, в качестве условной переменной может использоваться адрес произвольного объекта. В отличие от событий, установка условной переменной в просигнализированное состояние не влечет за собой никаких последствий в случае, если на данный момент нет потоков, ожидающих на переменной. Установка события в аналогичном случае влечет за собой запоминание состояния «просигнализировано» внутри самого события, после чего следующие потоки, желающие ожидать события, продолжают исполнение немедленно без остановки. Для полноценного использования такого объекта необходима также операция «освободить mutex и ожидать условную переменную атомарно». Активно используются в UNIX-подобных ОС. Дискуссии о преимуществах и недостатках событий и условных переменных являются заметной частью дискуссий о преимуществах и недостатках Windows и UNIX.

Порт завершения ввода-вывода (IO completion port, IOCP). Реализованный в ядре ОС и доступный через системные вызовы объект «очередь» с операциями «поместить структуру в хвост очереди» и «взять следующую структуру с головы очереди» - последний вызов приостанавливает исполнение потока в случае, если очередь пуста, и до тех пор, пока другой поток не осуществит вызов «поместить». Самой важной особенностью IOCP является то, что структуры в него могут помещаться не только явным системным вызовом из режима пользователя, но и неявно внутри ядра ОС как результат завершения асинхронной операции ввода-вывода на одном из дескрипторов файлов. Для достижения такого эффекта необходимо использовать системный вызов «связать дескриптор файла с IOCP». В этом случае помещенная в очередь структура содержит в себе код ошибки операции ввода-вывода, а также, для случая успеха этой операции - число реально введенных или выведенных байт. Реализация порта завершения также ограничивает число потоков, исполняющихся на одном процессоре/ядре после получения структуры из очереди. Объект специфичен для MS Windows, и позволяет обработку входящих запросов соединения и порций данных в серверном программном обеспечении в архитектуре, где число потоков может быть меньше числа клиентов (нет требования создавать отдельный поток с расходами ресурсов на него для каждого нового клиента).

Пул потоков

Рассказать про пул потоков