FStop.fm — нишевая социальная сеть, которую я создал в прошлом году, чтобы связывать фотографов с моделями. По сути, это смесь Tinder и Instagram с функциями, которые предназначены для визуальных художников, желающих общаться друг с другом. Я написал еще один пост о некоторых уроках, которые я извлек, выводя FStop на рынок; этот пост подробно расскажет о некоторых технических проблемах, с которыми я столкнулся.
Масштабирование в Azure
FStop живет в Azure как служба приложений. Когда это получило освещение в прессе, сопутствующий всплеск трафика привел к падению моего маленького веб-приложения. Это произошло потому, что я не настроил автомасштабирование в своем экземпляре службы приложений более низкого уровня.
Azure позволяет смехотворно легко масштабировать службу приложений. Я установил несколько правил, основанных на потреблении ЦП и памяти, и через несколько минут мы снова были в сети. Базовая конфигурация выглядит примерно так, что было достижимо за несколько кликов:
Обратите внимание, что это всего лишь служба приложения. FStop работает на базе Azure SQL, который так же легко масштабируется несколькими щелчками мыши на портале Azure.
Устранение неполадок в работе службы приложений
Веб-приложение FStop иногда отключалось на несколько минут. Сначала я думал, что это характерно только для более низких уровней службы приложений, но оказалось, что приложение регулярно аварийно завершает работу.
Чтобы диагностировать это, я использовал расширение службы приложений под названием «Диагностика сбоев»:
После добавления этого расширения и перехода к нему из портала вы можете настроить расширение для захвата дампов из процесса w3wp.exe — рабочего процесса IIS, обеспечивающего отзывчивость вашего веб-приложения.
Это показало, что уведомления по электронной почте вызывали сбои. По какой-то причине клиент .NET SMTP задыхался от O365 и вызывал проблемы с исчерпанием портов, которые приводили к сбою приложения. Вместо того, чтобы тратить часы на устранение неполадок, я решил убить двух зайцев и заменить SmtpClient реализацией SendGrid — что-то, что все равно было в моем невыполненном списке и решило проблемы с производительностью и масштабированием.
Непрерывное бесшовное развертывание
DevOps от FStop выглядит примерно так:
- Я проверяю код.
- Выполняется сборка TFS, а затем выполняются модульные/интеграционные тесты.
- Приложение развертывается в промежуточном слоте службы приложений, строка подключения которой указывает на зеркало рабочей базы данных.
- Выполняется полный набор тестов пользовательского интерфейса Selenium.
- Зеркальное отображение рабочей базы данных, промежуточная стадия повышается до рабочей, и выполняются все необходимые миграции базы данных.
- Дымовые тесты Selenium запускаются в рабочей среде и откатывают развертывание, если что-то пойдет не так.
Это позволяет мне постоянно продвигаться к производству столько, сколько я хочу, не беспокоясь о прерывании чьего-либо опыта. Замена рабочей среды на промежуточную — это то, что обеспечивает эффективное бесшовное развертывание, и — в худшем случае — я могу восстановить зеркало рабочей среды и переключиться обратно на промежуточную, если что-то пойдет не так.
Некоторые ошибки:
- Возможно, вам потребуется установить ключ компьютера в файле web.config, чтобы обеспечить по-настоящему бесшовную замену при использовании объединительной платы SignalR любого типа.
- Возможно, вам потребуется реализовать настраиваемое действие прогрева в вашем файле web.config, чтобы обеспечить по-настоящему беспрепятственный обмен.
Репликация Azure SQL/аварийное восстановление
Однажды днем я начал получать электронные письма и жалобы в социальных сетях на то, что FStop не работает на всех платформах. Моя служба мониторинга времени безотказной работы также начала взрывать мой почтовый ящик.
Выяснилось, что центр обработки данных Azure в выбранном мной регионе (восток США) простоял. Это длилось несколько часов, и я ничего не мог сделать. Это было очень напряжно, так как это произошло сразу после запуска нашего мобильного приложения. Время не могло быть хуже.
Я не тратил время на снижение этого риска, потому что никогда раньше с ним не сталкивался. Оказывается, есть много способов удовлетворить потребность в высокой доступности, однако мне просто нужен недорогой план смягчения последствий; Здесь я не использовал высокочастотную торговую платформу или решение для медицинской телеметрии. Это была просто социальная сеть, которая в худшем случае доставляла неудобства некоторым людям. Я решил создать отказоустойчивую базу данных с помощью георепликации, на которую я мог бы направлять запросы, если это когда-нибудь повторится.
Масштабирование служб приложений с помощью SignalR
Обмен сообщениями и уведомления FStop зависят от SignalR для их функциональности в реальном времени.
После масштабирования FStop до двух экземпляров службы приложений в результате горизонтального масштабирования мои тесты обмена сообщениями в реальном времени завершались неудачно. Что было действительно интересно, так это то, что сбои были вызваны тем, что почти ровно 50% отправленных сообщений не были получены целевым экземпляром браузера. Я бы отправил десять сообщений из браузера А в браузер Б; Браузер B получит только пять или шесть из них.
Это было вызвано тем, что если браузер A устанавливает соединение с экземпляром 1 моего веб-приложения, а браузер B устанавливает соединение с экземпляром 2 моего веб-приложения, SignalR не сможет синхронизировать эти сообщения без посторонней помощи. Именно здесь в игру вступает объединительная плата SignalR. Звучит просто, но мне понадобилось несколько часов, чтобы рвать на себе волосы, прежде чем я понял корреляцию между 50% отказом и двумя случаями в масштабе.
Всего за пару строк кода и пару щелчков мыши на портале Azure я смог развернуть объединительную панель SignalR на Redis, чтобы решить эту проблему, пройти тесты и развернуть код.
Создание трансляций
Пользователи FStop хотели иметь возможность отправлять «кастинги» другим пользователям. Это будет работать следующим образом:
- Джону, фотографу, пришлось укомплектовать съемку, для которой нужны были две модели бледно-блондинки.
- Джон открывает приложение FStop и соответствующим образом настраивает фильтр поиска: белый, высокий, блондин, в пределах 30 миль от центра Лос-Анджелеса.
- Джон нажимает значок «Трансляция» и составляет свое сообщение. Он выбирает количество пользователей, которым нужно отправить сообщение (не более 50), и нажимает «Отправить». Затем он получает подтверждение того, что эта трансляция скоро будет рассмотрена и одобрена сотрудником FStop.
- Сотрудник FStop видит очередь ожидающих трансляций и при необходимости одобряет/отклоняет.
- После утверждения кастинг отправляется 50 моделям, которые соответствуют критериям поиска Джона. Трансляция также размещена на FStop Feed.
Это простой набор требований, но было интересно выяснить, как упростить его в масштабе и по низкой цене. Общая архитектура выглядит так:
По сути, это механизм организации очереди, который обрабатывается веб-заданием Azure. Затем я создал небольшую панель инструментов в административной панели FStop, которая позволяет нам одобрять и отслеживать кастинги:
Обратите внимание, что доллары отражают будущие намерения монетизации на основе воздействия — чем большему количеству людей вы транслируете, тем больше вы платите. Это не реализовано.
Очистка кеша SPA
FStop был создан как одностраничное приложение с использованием DurandalJS. Он связан и минимизирован как две полезные нагрузки — биты spa и биты поставщика:
Это стало возможным благодаря готовой комплектации ASP.NET. Но что, если Джон загрузит FStop.fm, а я запущу обновление? У него будут устаревшие биты, если он не обновится. Я решил эту проблему, добавив заголовок «BuildVersion» к своим ответам:
Имея эту информацию, клиенту просто нужно кэшировать свою первоначальную версию при загрузке, а затем проверять заголовок BuildVersion в последующих ответах сервера, чтобы определить, следует ли пользователю запрашивать перезагрузку приложения:
if (!warned){ var newVersionNumber = response.getResponseHeader("BuildVersion"); if (newVersionNumber != cachedVersionNumber){ doSomething(); } }
Близкий поиск
Учитывая тысячу пользователей с местоположениями, сохраненными в виде координат широты/долготы, как вы собираетесь возвращать результаты для тех, кто хочет видеть пользователей в радиусе 50 миль от их координат?
Быстро вычислить близость на лету сложно, особенно с десятками или сотнями тысяч пользователей.
Шаг 1. Сохраните их местоположение как широту/долготу
Вам нужно получить в свои руки координаты широты / долготы. Я делаю это на клиенте — мобильные приложения получают это от GPS или почтовых индексов, введенных вручную; веб-приложения получают его через сторонний API-вызов API геокодирования Google.
Шаг 2. Посмотрите, как это здорово
Поиск координат, попадающих в этот красный кружок, в большом наборе данных обходится дорого. Поиск этого красного круга в этом синем поле гораздо более доступен. Если производительность важнее точности, вы можете даже просто поискать синее поле. Код, который я использую для этого, приведен ниже:
var dapperQuery = $"SELECT {userColumns} FROM [USER] WHERE {filterCriteria}"; dapperQuery = $"dapperQuery AND " + "([LocationLong] > @longMin and [LocationLong] < @longMax and [LocationLat] > @latMin and [LocationLat] < @latMax) AND " + // FIRST LINE "((geography::STPointFromText([LocationPoint], 4326).STDistance(@userPoint)) <= @searchRadius)"; // SECOND LINE
Это была одна из проблем, которые убили пользовательский опыт, когда я увеличил количество пользователей с 500 до 5 тысяч в день, когда FStop начал освещаться в прессе. Такая упаковка запроса привела к значительной коррекции производительности.
Денормализация лайков
FStop Feed позволяет пользователям «лайкать» сообщения:
Но фид сортирует посты по сложному алгоритму, который учитывает возраст, лайки с течением времени, активность комментариев и репутацию отправившего пользователя.
Выполнение Join
между Posts
и Likes
, скорее всего, замедлит работу страницы ленты, особенно когда обрабатываются тысячи сообщений и лайков. Поэтому я решил денормализовать эти биты и хранить Likes
прямо в таблице Posts
в виде UserIdsUpvoted
. Это подскажет мне, сколько лайков у любого поста, а также пользователей, которым он понравился. На мой взгляд, денормализация модели предметной области — это прекрасно, если это делается во имя производительности.
Спасибо за чтение!
Мик