Железнодорожные перевозки, как грузовые, так и пассажирские — один из самых востребованных видов транспорта в нашей стране. То, что поезда ездят только по рельсам, с одной стороны упрощает и снижает вариативность модели, с другой — добавляет массу зависимостей. Если на путях случается какая-нибудь непредвиденная ситуация — это может иметь значительные последствия для всей сети. Отклонение от графика одного состава или авария на рельсах могут повлиять на движение целого направления. Это сказывается как на компаниях, которые не получат вовремя груз, так и на пассажирах, которые могут опоздать на работу, пропустить нужную электричку, сесть не в тот поезд, или проторчать на платформе лишние полчаса (а то и больше).
Меня зовут Александр Подлевских, я ведущий разработчик в компании Туту.ру, тимлид в команде «Электричек», и в этой статье я расскажу, как мы прогнозируем отклонения электричек от графика — опоздания и опережения. А ещё о том, что такое ГВЦ РЖД, как технически устроена система пригородного ж/д-транспорта, и как мы рассказываем об опозданиях пассажирам.
Что такое фактическое следование электричек и поездов
Главный вычислительный центр РЖД (ГВЦ РЖД) — это организация, главная задача которой — информационное обеспечение ОАО «РЖД». По сути ГВЦ — это IT РЖД.
В системе ГВЦ содержатся и обрабатываются колоссальные объемы данных. В предыдущей статье я описывал взаимодействие Туту.ру с ГВЦ, работу с данными базового расписания электропоездов и с вариантами их движения. Вскоре после этого мы подключили и использование данных о фактическом следовании пригородных поездов.
Данные о фактическом следовании представляют из себя набор записей в базе данных, каждая из которых — факт прохождения состава по контрольной точке в маршруте. Эти факты фиксируются датчиками, расположенными на подъезде и на выезде со станций. Всего датчики бывают четырех видов:
- АСОУП (автоматизированная система оперативного управления перевозками). По сути это система, которая агрегирует данные с путей и позволяет проводить контроль за всеми перевозками. В частности, соблюдение планов формирования, массы и длины грузовых поездов, прогнозирование прибытия грузов, учет перехода поездов, вагонов и контейнеров через стыковые пункты дорог и отделений (АСОУП). Отсюда в ГВЦ и поступают данные о том, проехала электричка станцию или нет.
- СЦБ (устройства сигнализации, централизации и блокировки) — совокупность технических средств, используемых для регулирования и обеспечения безопасности движения поездов (Устройства сигнализации, централизации и блокировки). По сути эти устройства представляют собой датчики на железнодорожных стрелках, данные с которых поступают в ГВЦ.
- САИПС (Система автоматизации идентификации подвижного состава) — устройства, стоящие вдоль путей и фиксирующие проходящие мимо вагоны.
- ГИС (Геоинформационная система РЖД) — это геолокационная система на основе ГЛОНАССа, которая позволяет следить за показателями работы ж/д-транспорта, скорости на участках, нарушениями безопасности движения и другое.
По каждой из станций данные в ГВЦ могут поступать из нескольких источников — в зависимости от того, какое оборудование на ней установлено. Кроме того, различные источники могут обладать разной точностью. Например, мы проанализировали, что у СЦБ вероятность ошибок существенно ниже, чем у САИПС. Поэтому, если по одному и тому же событию есть данные из обоих источников, ты мы отдаем приоритет СЦБ. И на данный момент именно СЦБ является основным источником данных. Кроме того, мы предоставляем источник данных по каждому времени на страницах маршрута. Это история, в первую очередь, для продвинутых пользователей, которые имея эти данные, могут понимать примерную точность прогноза.
Все эти данные собираются в базу DB2 в системе ГВЦ, а уже оттуда для собственных нужд их забирают партнеры.
Взаимодействие с ГВЦ
Правила обеспечения антитеррористической безопасности требуют использование данных фактического следования ж/д-транспорта не менее, чем 10 минутной давности. Кроме того, поскольку система сильно нагруженная, то партнеры (такие, как Туту.ру) могут обращаться к ней не чаще, чем раз в несколько минут. Из-за этого пользователям доступно не совсем фактическое следование состава, а то, как он следовал 10 минут назад. Более того, данные поступают только от датчиков на станциях, а на мелких остановках (где даже платформы может не быть) или просто по пути, их практически нет.
А ещё бывают ситуации, когда данные проследования станции привязываются не к тому составу. Как правило, подобные ситуации возникают нечасто и исправляются сотрудниками ГВЦ достаточно оперативно. Тем не менее, чтобы минимизировать подобные случаи в нашем локальном хранилище данных, приходится каждый раз подстраховываться и запрашивать у ГВЦ большую глубину данных, чем необходимо. И даже в этом случае, мы не можем быть уверены в данных на 100% — слишком много переменных и всяких «но». Но степень ошибки ничтожно мала (примерно 1 ошибка на 100 000 записей).
Использование
Для получения и хранения данных фактического следования использовался механизм подключения к ГВЦ, аналогичный получению данных плановых расписаний (сам механизм я описывал в предыдущей статье). А для их использования у нас на сайте во многом пригодились ранее построенные связи наших локальных данных и базы ГВЦ, а также связи станций (алгоритм также описан в предыдущей статье). С помощью этого соответствия удавалось связать примерно 90% наших сущностей с данными фактического следования. Для остальных поездов мы находили новые связи с помощью поиска по номерам поездов и станций следования.
Итак, в чем состояла задача:
У нас есть страница расписания на сайте (вот, например, Москва — Петушки), для отображения которой опрашивается сервис «расписания». Он принимает на вход идентификаторы станций отправления и назначения и дату поездки, а в ответ предоставляет набор объектов, содержащих данные электричек, проходящих между этими станциями. В него входят идентификаторы поезда — номер, начальная/конечная станции, график движения, а также информация о конкретной поездке электрички от одной станции до другой — время прибытия/отправления, платформы. Полных данных маршрута электрички (время прохождения каждой промежуточной станции) ответ не содержит — такой объем информации был бы слишком большим, вычисления производились дольше и просто это излишне.
Пометку о возможном опоздании/опережении нам нужно было вычислять на основании данных фактического следования, которые сравниваются с данными расписания из нашей локальной базы. Порой расчет основывается не на последней записи о прохождении контрольной точки поездом, а на нескольких. Так бывает, когда разные датчики работают с разной степенью точности. Например, по какой-то станции появляется сильное отличие фактического следования поезда от планового. Вот тогда мы и смотрим и на время прохождении предыдущей станции.
То есть перед нами стояла задача показать пользователям информацию о возможном отклонении электричек от графика.
Данных фактического следования только от ГВЦ для решения этой задача нам не хватило бы. И мы сделали микросервис, задачей которого является — возвращать по идентификатору поезда полный маршрут, включающий данные не только планового расписания, но и фактические данные прохождения станций.
Алгоритм работы
Алгоритм работы сервиса такой: на вход получаем идентификатор поезда и по нему делаем запрос в сервис расписания для получения актуального маршрута. Далее еще один запрос — в сервис, хранящий связи между нашими локальными поездами и поездами в системе ГВЦ. Если связь есть, то из сервиса хранения фактических данных получаем всю информацию о прохождении поездом контрольных точек. Если связей в нашем хранилище нет, или по этим связям нет фактических данных, то делаем запрос на получение данных по номеру поезда и по проходящим станциям. Если во втором случае данные нашлись, то устанавливаем новую связь между найденным поездом и нашим, для которого выполнялся поиск.
Для того, чтобы корректно подмешать данные фактического следования к маршруту, необходимо понимать, какой факт к какой станции относится. В данных фактического следования станцию идентифицирует код ЕСР — номер станции в системе Экспресс-3, в данных наших маршрутов у станций собственные идентификаторы.
Для мэтчинга станций делаются запросы еще в 3 сервиса: сервис связей наших станций и станций в ГВЦ. По этим идентификаторам делается запрос в сервис локального хранения данных ГВЦ для получения полной информации о станциях (с идентификаторами ЕСР). Кроме того, делается запрос в сервис локальных станций, из которого получаются коды Экспресс-3. Зная связи станций, мы строим маршрут электрички на текущую дату с плановым прохождением станций и данными фактического прохождения этих станций.
В первой версии сервис именно так и работал — при открытии страниц запросы делались для каждого поезда в расписании, и никакого кеширования не было. При этом подавляющее большинство запросов у нас приходится на московские направления «на сегодня» (тут ежедневно курсирует более 100 электропоездов).
В общем, при включении механизма на продакшн на 5% пользователей, мы получили нагрузку примерно 6000 rps суммарно на все используемые сервисы. К такому инфраструктура не была готова. Нужно было оптимизировать работу, сократить суммарное количество запросов. Эту задачу мы решили так:
- Ограничили количество поездов, для которых делаются запросы. Они теперь уходят не на все электрички в текущий день, а только на ближайшие: которые отправятся в течение нескольких часов или уже отправились. Это сократило запросы примерно в 2 раза. Но этого было мало.
- Далее мы сделали загрузку данных не по каждому отдельному поезду, а по пачке поездов. Были попытки варьирования количества элементов, но всплыла другая проблема — если увеличивать размер пачки, это существенно замедляет загрузку страниц, так как данные генерируются не параллельно, а последовательно.
- Следующий способ — закешировать статические данные (связи поездов и станций). Это улучшение уже существенно облегчило общую картину — мы смогли включить выдачу на 100% пользователей. Однако в пиковые часы (утро понедельника и вечер пятницы) все еще были проблемы и периодически ресурсы, выделенные под серверы, заканчивались.
- Тогда мы добавили кеш с итоговыми данными: маршрут электрички + фактическое следование со всеми вычисленными соответствиями. Это существенно ускорило среднюю загрузку страниц. Но, поскольку свежие данные фактического следования мы получаем раз в несколько минут, а сервис, производящий итоговые вычисления, не имеет представления о том, по каким электричкам были получены новые данные, а по каким нет, пришлось проверять время с момента последней загрузки данных. Если это время больше того, что было ранее, кеш сбрасывается полностью. Это дало эффект, и среднее время загрузки страниц сильно сократилось. Но в моменты получения данных и, соответственно, сброса кеша, из-за огромного количества запросов время загрузки все равно подскакивало. В пиковые часы график был похож на кардиограмму.
- И, наконец, финальный кеш был добавлен в сервис хранения данных — и вот он полностью выровнял ситуацию. То есть теперь результат запроса в базу кешируется, а при получении новых данных обновляется только по тем поездам, по которым эти данные были получены.
В итоге, схема работы вычисления маршрута электрички с фактическими данными выглядит следующим образом:
Она работает на каждой странице расписания на текущую дату для 30-50% поездов и дает оверхед на загрузку страницы до 100 мс по 90 процентилю.
Анализ данных
На основании полученных данных можно сделать вывод, опаздывает электричка или идет с опережением графика.
В первую очередь, мы анализируем, есть ли вообще данные фактического следования для конкретного поезда, когда они должны уже быть. Если их нет, то мы пишем, что возможно опоздание, потому что состав должен был стартовать с начальной станции уже больше 5 минут назад, но этого не произошло.
Если данные есть, то анализируются данные по последней или последним двум станциям (для надежности), по которым есть данные фактического следования. Если есть отклонения от графика, то поезду ставится пометка «возможно опоздание/сильное опоздание/опережение». Кроме того, пишется причина — например, что поезд на одну из предыдущих станций прибыл с задержкой на несколько минут.
Ещё одно небольшое, но полезное улучшение заключается вот в чем. Допустим, у нас есть поезд Семёнов — Нижний Новгород, который проходит через 2 станции — Тарасиха и Киселиха. Из Семёнова электричка стартует в 9:20, до Тарасихи доезжает в 9:43, до Киселихи — в 10:11, а в Нижний Новгород прибывает уже в 10:32, точнее должна прибыть. Но по фактическим данным мы видим, что поезд только до Тарасихи добрался в 10:02 (то есть он уже опаздывает на 19 минут). Замерзший пассажир на безлюдной платформе в Киселихе смотрит в расписание в 10:25, и, если бы мы показывали только данные расписания, то человек решил бы, что поезд уже уехал. Но, так как отставание от графика в 19 минут нагнать на практике не удается, то мы показываем эту электричку, как ещё не ушедшую из Киселихи.
Если по последним полученным данным поезд идет по графику, то анализируются следующие станции, по которым данные фактического следования обычно приходят. Если данных по ним вовремя нет, то, скорее всего, электричка задержалась где-то на перегоне и мы показываем предупреждение о том, что она еще не прибыла на промежуточную станцию, хотя уже должна.
Но и это ещё не всё. Бывает, что человек смотрит расписание из Москвы, стоя на платформе Чухлинка. Тут история с фактическим следованием не сработает, так как Москва — начальная точка пути. Тут либо никакой информации нет, либо она есть, но это значит, что поезд уже ушел. В этом случае мы анализируем обороты движения составов.
Обороты
В большинстве случаев один электропоезд в день совершает не одну поездку, а курсирует по маршруту целый день. При этом для каждой поездки у него меняется номер, может поменяться бригада машинистов. Во всех системах с расписанием (и нашей, и в ГВЦ) это разные объекты. Однако, во многих случаях в системах есть информация, в какой рейс будет отправлен электропоезд в дальнейшем. Сам состав каждый день может быть разным, но именно связка рейсов фиксируется. И, если поезд едет с опозданием в первой поездке, то и вторая поездка также начнется с опозданием. Из этих правил бывают исключения, например, если опоздание очень сильное, то во вторую поездку могут пустить другой состав, но на практике это происходит довольно редко.
Имея данные о таких связях, мы анализируем еще и данные о фактическом следовании поездов, которые обслуживаются тем же составом, и показываем предупреждение о возможном опоздании. Но делается это только если опоздание предыдущего рейса может повлиять на тот, который смотрит пользователь.
В общем, следующий раз, когда соберётесь из Тарасихи в Киселиху — будьте уверены, об опоздании поезда мы вас оповестим.
Автор: Александр Подлевских