|
|
Опубликовано 11.04.2018 17:33:18
|
|
|
|

Чтобы было проще понять, о чём эта статья, давайте начнём с изменения средней дневной статистики Stack Overflow. Следующие данные приведены из статистики по состоянию на 12 ноября 2013 года:
- Балансировщик нагрузки принимал 148 084 833 HTTP-запроса
- Из них 36 095 312 были загружены страницами
- Для отправки используется 833 992 982 627 байт (776 ГБ) HTTP-трафика
- Всего было получено 286 574 644 032 байта (267 ГБ) данных
- Всего было отправлено 1 125 992 557 312 байт (1 048 ГБ) данных
- 334 572 103 SQL-запроса (включая только HTTP-запросы)
- 412 865 051 запроса на Redis
- 3 603 418 запросов на движок тегов
- На SQL-запросы ушло 558 224 585 мс (155 часов)
- Запросы Redis занимали 99 346 916 мс (27 часов)
- Потратил 132 384 059 мс (36 часов) на запрос движка метки
- Обработка процесса заняла 2 728 177 045 мс (757 часов) ASP.Net процесс
Следующие данные показывают изменения в статистике по состоянию на 9 февраля 2016 года, чтобы вы могли сравнить:
- Количество HTTP-запросов, полученных балансировщиком нагрузки: 209 420 973 (+61 336 090)
- 66 294 789 (+30 199 477) из которых загружается страница
- Отправленные данные HTTP: 1 240 266 346 053 (+406 273 363 426) байт (1,24 ТБ)
- Общее количество полученных данных: 569 449 470 023 байта (+282 874 825 991) (569 ГБ)
- Общее количество отправленных данных: 3 084 303 599 266 (+1 958 311 041 954) байт (3,08 ТБ)
- SQL-запросы (только из HTTP-запросов): 504,816,843 (+170,244,740)
- Посещения кэша Redis: 5,831,683,114 (+5,418,818,063)
- Эластичные поиски: 17 158 874 (не отслеживались в 2013 году)
- Запросы на движок тегов: 3 661 134 (+57 716)
- Совокупное время, затраченное на выполнение SQL-запросов: 607 073 066 (+48 848 481) мс (168 часов)
- Время попаданий в кэш Redis занято: 10 396 073 (-88 950 843) мс (2,8 часа)
- Время, затраченное на запросы движка тегов: 147 018 571 (+14 634 512) мс (40,8 часа)
- Время, потраченное на обработку ASP.Net: 1 609 944 301 (-1 118 232 744) мс (447 часов)
- 22,71 (-5,29) MS 49 180 275 страниц выпусков в среднем время рендера (из которых 19,12 мс расходуется за ASP.Net)
- 11,80 (-53,2) мс. 6 370 076 среднего времени рендеринга первых страниц (из которых 8,81 мс расходуется за ASP.Net)
Возможно, вы задаётесь вопросом, почему ASP.Net обрабатывает на 61 миллион запросов больше в день, сокращая время обработки на 757 часов (по сравнению с 2013 годом). Это в основном связано с обновлениями наших серверов в начале 2015 года, а также с множеством работы по оптимизации производительности внутри приложения. Не забывайте: производительность — это всё ещё преимущество. Если вам интересны конкретные детали железа, не волнуйтесь, я расскажу детали оборудования серверов, используемых для работы этих сайтов, в виде приложения в следующей статье (обновлю эту ссылку, когда придёт время).
Так что же изменилось за последние два года? Ничего особенного, просто замена некоторых серверов и сетевого оборудования. Вот обзор серверов, на которых сегодня работали ваш сайт (обратите внимание, как они изменились с 2013 года).
- 4 сервера Microsoft SQL Server (2 из которых используют новое оборудование)
- 11 веб-серверов IIS (новое оборудование)
- 2 сервера Redis (новое железо)
- 3 сервера Tag Engine (2 из которых используют новое оборудование)
- 3 сервера Elasticsearch (такие же, как выше)
- 4 сервера для балансировки нагрузки HAProxy (2 добавлены для поддержки CloudFlare)
- 2 сетевых устройства (ядро Nexus 5596 + 2232TM Fabric Extender, все устройства обновлены до пропускной способности 10 Гбит/с)
- 2 x Fortinet 800C Firewall (заменяет Cisco 5525-X ASA)
- 2 маршрутизатора Cisco ASR-1001 (заменяют маршрутизаторы Cisco 3945)
- 2 роутера Cisco ASR-1001-x (НОВЫЕ!) )
Что нам нужно, чтобы Stack Overflow работал? С 2013 года мало что изменилось, но благодаря оптимизациям и новому железу, о котором я говорил выше, нам теперь нужен только один веб-сервер. Мы уже непреднамеренно проверяли эту ситуацию и несколько раз добились успеха. Обратите внимание: я просто сказал, что это работает, я не говорил, что это хорошая идея. Но каждый раз, когда это происходит, это довольно интересно.
Теперь, когда у нас есть базовые данные по идеям для масштабирования серверов, давайте посмотрим, как мы создали эти классные веб-страницы. Мало какие системы существуют полностью независимо (и наша не исключение), и без целостного подхода, интегрирующего эти аспекты, смысл архитектурного планирования значительно уменьшается. Наша цель — понять общую ситуацию. В будущем будет много статей, которые глубоко погружаются в каждую конкретную область. Эта статья представляет собой лишь краткое изложение логической структуры ключевого оборудования, а следующая статья содержит конкретные детали об этом оборудовании.
Если хотите увидеть, как сегодня выглядит это железо, вот несколько фотографий, которые я сделал шкафа A (шкаф B точно такой же), когда я обновлял сервер в феврале 2015 года:
Теперь давайте перейдём к архитектурной структуре. Ниже приведено краткое изложение логической архитектуры основных существующих систем:
Основные принципы
Вот несколько распространённых принципов, которые не нужно вводить по очереди:
- Всё имеет резервные копии.
- Все серверы и сетевые устройства имеют как минимум два соединения с пропускной способностью 10 Гбит/с.
- Все серверы имеют два источника питания, которые обеспечивают питание через два блока ИБП, два генератора позади них и два источника напряжения в сети.
- На всех серверах есть резервная копия, расположенная в стойках A и B.
- Все серверы и сервисы имеют двойное резервное копирование в отдельном дата-центре (в Колорадо), хотя я в основном освещаю Нью-Йорк.
- Всё имеет резервные копии.
интернет
Сначала вам нужно найти наш сайт — это функция DNS. Поиск сайтов быстрый, поэтому теперь мы передаём это CloudFlare, потому что у них есть DNS-серверы во всех уголках мира. Мы обновляем DNS-записи через API, и они отвечают за «управление» DNS. Однако в наших злодейских умах у нас всё ещё есть собственные DNS-серверы из-за глубоко укоренившихся проблем с доверием. Когда апокалипсис становится апокалиптическим — возможно, из-за проблем с GPL, Punyon или кэшированием — и люди всё ещё хотят программировать, чтобы отвлечь их внимание, мы переключаемся на собственные DNS-серверы.
Как только ваш браузер находит наше укрытие, HTTP-трафик от наших четырёх провайдеров (Level 3, Zayo, Cogent и Lightower в Нью-Йорке) поступает в один из наших четырёх продвинутых роутеров. Мы используем протокол Border Gateway Protocol (BGP, очень стандартный протокол) для обмена одноранговым трафиком от сетевых провайдеров, чтобы контролировать его и предоставлять наиболее эффективный способ доступа к нашим сервисам. Маршрутизаторы ASR-1001 и ASR-1001-X делятся на две группы, каждая из которых должна использовать активный/активный режим для обработки трафика от обоих сетевых провайдеров — здесь есть резервные копии. Хотя все они имеют одинаковую физическую пропускную способность 10 Гбит/с, внешний трафик всё равно независим от трафика с внешнего VLAN и подключён к балансировке нагрузки отдельно. После того как трафик пройдёт через роутер, вы подойдёте к балансировщику нагрузки.
Думаю, пришло время упомянуть, что у нас есть MPLS с пропускной способностью 10 Гбит/с между двумя дата-центрами, хотя это напрямую не связано с сервисами сайта. Мы используем эту технологию для удаленного репликации и быстрого восстановления данных с целью решения определённых чрезвычайных ситуаций. «Но, Ник, в этом нет никакой избыточности!» С технической точки зрения вы правы (в положительном смысле), на этом уровне это действительно одна точка отказа. Но подождите! Через сетевого провайдера у нас также есть два дополнительных резервных маршрута OSPF (MPLS — первый выбор, а это второй и третий варианты из-за стоимости). Каждый из упомянутых ранее наборов устройств будет подключён к дата-центру Колорадо соответствующим образом для балансировки нагрузки сетевого трафика в случае отказа. Конечно, мы могли бы соединить эти два набора устройств друг с другом, чтобы было четыре набора путей, но забудьте и перейдём дальше.
Балансировка нагрузки (HAProxy)
Балансировка нагрузки реализована с помощью HAProxy 1.5.15, работающей на CentOS 7 (нашей любимой версии Linux). И добавить защищённый протокол передачи TLS (SSL) на HAProxy. Мы также следим за HAProxy 1.7, который сразу же обеспечит поддержку протокола HTTP/2.
В отличие от других серверов с двумя сетевыми соединениями LACP со скоростью 10 Гбит/с, каждый балансировщик нагрузки имеет два подключения по 10 Гбит/с: одно для внешней сети, другое для DMZ. Эти серверы имеют 64 ГБ и более памяти для более эффективной работы с SSL-протоколом. Когда мы можем кэшировать и повторно использовать больше TLS-сессий в памяти, мы потребляем меньше вычислительных ресурсов при подключении к одному клиенту. Это значит, что мы можем восстанавливать сессии быстрее и дешевле. Память настолько дешёвая, что выбор — это просто.
Сам баланс нагрузки легко настроить. Мы слушаем разные сайты на разных IP-адресах (в основном для управления сертификатами и DNS), а затем направляем трафик на разные бэкенды (в основном на основе заголовков хостов). Единственное, что мы делаем здесь — ограничиваем скорость и собираем часть заголовочной информации (с веб-слоя) для входа в системные сообщения HAProxy, таким образом мы можем записывать метрики производительности для каждого запроса. Мы подробно об этом расскажем позже.
Веб-уровень (IIS 8.5, ASP.Net MVC 5.2.3 и .Net 4.6.1)
Балансировка нагрузки распределяет трафик между 9 так называемыми основными веб-серверами (01-09) и двумя веб-серверами разработки (10-11, наша тестовая среда). Основной сервер запускает Stack Overflow, Careers и все сайты Stack Exchange, а meta.stackoverflow.com и meta.stackexchange.com работают на двух других серверах. Основное приложение для вопросов и ответов является мультиарендатором, то есть одно приложение обрабатывает все запросы с сайта вопросов и ответов. Другими словами, мы можем запускать всё приложение для вопросов и ответов на одном пуле приложений на одном сервере. Другие приложения, такие как Careers, API v2, Mobile API и другие, являются независимыми. Вот что вы видите в IIS для мастер-серверов и разработчиков:
Вот распределение веб-сегмента Stack Overflow, как это видно в Opserver (наш внутренний мониторинговый панель):
А вот как расходуют ресурсы этих веб-серверов:
Я подробнее расскажу в следующей статье, почему мы слишком много ресурсов предоставляем, сосредотачиваясь на прокатном билде, свободе и дублировании.
Сервисный уровень (IIS, ASP.Net MVC 5.2.3, . NET 4.6.1 и HTTP. SYS)
Рядом с веб-уровнем находится сервисный уровень. Они также работают поверх IIS 2012 в Windows 8.5R2. Этот слой запускает некоторые внутренние сервисы, поддерживающие веб-слой и другие внутренние системы производственной среды. Два основных сервиса: «Stack Server», который запускает движок тегов и основан на http.sys (не IIS); API Providence (на основе IIS). Интересный факт: мне пришлось сопоставить эти два процесса для подключения к разным сокетам, потому что Stack Server часто получал доступ к кэшам L2 и L3 при обновлении списка проблем каждые две минуты.
Машины, использующие эти сервисы, критически важны для движка тегов и бэкенд-API, поэтому они должны быть избыточными, но не в 9 раз избыточными. Например, мы загружаем все статьи и их теги из базы данных каждые n минут (сейчас 2 минуты), что немало. Мы не хотим повторять эту операцию загрузки 9 раз на веб-уровне, 3 раза для нас достаточно безопасно. Мы также используем различные аппаратные конфигурации для этих серверов, чтобы лучше оптимизировать вычислительные и загрузочные характеристики движка тегов и эластичных индексных задач (которые также работают на этом уровне). Сам «движок тегов» — довольно сложная тема, которая будет рассмотрена в отдельной статье. Основной принцип таков: когда вы заходите в адрес /questions/tagged/java, вы заходите в движок тегирования, чтобы получить соответствующие ему вопросы. Движок обрабатывает всё сопоставление тегов, кроме /search, поэтому везде, включая новую навигацию, данные получают через этот сервис.
Кэширование и публикация/подписка (Redis)
В некоторых местах мы использовали Redis, и он обладает надёжной стабильностью. Хотя в месяц происходит до 160 миллиардов операций, объём процессора на экземпляр не превышает 2%, что обычно ниже:
Мы используем Redis для систем кэширования уровней L1/L2. Уровень «L1» — это HTTP-кэш, работающий на веб-сервере или подобном приложении. Уровень «L2» предназначен для получения данных через Redis после отказа предыдущего кэша. Наши данные хранятся в формате Protobuf, реализованном через protobuf-dot-net, написанное Марком Гравелом. Для клиента Redis мы использовали библиотеку StackExchange.Redis — открытую библиотеку, разработанную внутри компании. Если веб-сервер не попадает в оба кэша L1 и L2, он получает данные из своих источников данных (запросы базы данных, вызовы API и т.д.) и сохраняет результаты в локальный кэш и Redis. Следующий сервер может отсутствовать в кэше L1 при получении тех же данных, но он будет получать данные в L2/Redis, что устраняет необходимость в запросах базы данных или вызовах API.
Мы также запускаем множество сайтов для вопросов и ответов, каждый из которых имеет свой префикс L1/L2: key в кэше L1 и идентификатор базы данных в кэше L2/Redis. Мы рассмотрим эту тему в будущих статьях.
В дополнение к двум основным серверам Redis (один мастер и один слейв), которые запускают все экземпляры сайтов, мы также настроили экземпляр для машинного обучения (в основном для ограничения памяти) с использованием двух других выделенных ведомых серверов. Эта группа серверов используется для предоставления услуг, таких как рекомендация вопросов на главной странице и улучшение подбора работ. Эта платформа называется Providence, и Кевин Монтроуз писал о ней.
Основной сервер Redis имеет 256 ГБ оперативной памяти (около 90 ГБ), а сервер Providence — 384 ГБ оперативной памяти (около 125 ГБ).
Redis предназначен не только для кэширования, он также имеет механизм публикации и подписки, при котором один сервер может публиковать сообщение, а другие подписчики могут получать его (включая Redis от клиентов на сервере). Мы используем этот механизм для очистки кэша L1 на других сервисах, чтобы поддерживать согласованность кэша на веб-сервере. Но у неё есть ещё одно важное применение: веб-сокеты.
Websockets (NetGain)
Мы используем websockets для запуска обновлений в реальном времени пользователям, таких как уведомления в верхней панели, голоса, новая навигация, новые ответы, комментарии и многое другое.
Сам сокетный сервер работает на веб-уровне, используя нативные сокеты. Это очень небольшое приложение, основанное на нашей открытой библиотечной реализации: StackExchange.NetGain. В часы пик у нас было около 500 000 одновременных websocket-соединений, что очень много браузеров. Интересный факт: некоторые из этих браузеров работают уже более 18 месяцев, и вам нужно найти кого-то, чтобы проверить, живы ли эти разработчики. Следующий график показывает модель параллелизма веб-сокетов на этой неделе:
Зачем использовать websockets? В нашем масштабе это гораздо эффективнее, чем опросы. Таким образом, мы сможем просто передавать больше данных с меньшими ресурсами и обеспечивать более актуальное время для пользователей. Однако этот подход не лишён проблем: временные порты, исчерпанные файловые дефилы на балансировщиках нагрузки — очень интересные проблемы, о которых мы поговорим позже.
Поиск (Elasticsearch)
Спойлер: здесь особо нечего радоваться. Веб-слой использует Elasticsearch 1.4 и реализует сверхлегкий, высокопроизводительный клиент StackExchange.Elastic. В отличие от большинства вещей, мы не планируем открывать исходный код для этой части, просто потому что она открывает очень небольшое количество API, которые нам нужно использовать. Уверен, что публичность перевешивает потери и только запутает разработчиков. Мы используем elastic:/search в этих местах для расчёта связанных вопросов и даём советы при их задании.
Каждый эластичный кластер (по одному на каждый дата-центр) содержит 3 узла, каждый со своим индексом. На сайте Careers также есть дополнительные индексы. Менее стандартная часть нашей конфигурации в эластичных кругах — это то, что наш кластер из трёх серверов немного мощнее обычной: каждый сервер использует SSD-хранилище, 192 ГБ памяти, двойная сеть с пропускной способностью 10 Гбит/с.
В том же домене приложений Stack Server (да, нас тут разбрасывал .Net Core) также размещен движок тегов, который также использует Elasticsearch для непрерывного индексирования. Здесь мы используем небольшой трюк, например, используя ROWVERSION в SQL Server (источнике данных) для сравнения с документом «последнего места» в Elastic. Поскольку он, по-видимому, последовательный, нам легко сканировать и индексировать контент, если он изменён после последнего визита.
Главная причина, по которой мы используем Elasticsearch вместо таких технологий, как полнотекстовый поиск SQL, — это его масштабируемость и экономическая эффективность. SQL относительно дорогой на процессорах, а Elastic — гораздо дешевле и в последнее время появилось много новых функций. Почему бы не использовать Solr? Нам нужно искать по сети (с несколькими индексами одновременно), и Solr не поддерживает такой сценарий на момент принятия решений. Причина, по которой мы ещё не использовали 2.x, в том, что типы в 2.x сильно изменились, а значит, нам приходится переиндексировать всё, если мы хотим обновиться. У меня просто нет времени планировать изменения требований и миграцию.
База данных (SQL Server)
Мы используем SQL Server как единый источник истины. Все данные в Elastic и Redis поступают с SQL Server. У нас есть два кластера SQL Server, и мы настроены на группы доступности AlwaysOn. У каждого кластера есть основной сервер в Нью-Йорке (который берет на себя почти всю нагрузку) и сервер реплики, а также сервер реплики в Колорадо (наш центр восстановления после аварий). Все операции копирования асинхронны.
Первый кластер представляет собой набор серверов Dell R720xd, каждый с 384 ГБ памяти, PCIe SSD с 4 ТБ и двумя 12-ядерными процессорами. В него входят Stack Overflow, Sites (плохое название, объясню позже), PRIZM и базу данных Mobile.
Второй кластер — это набор серверов Dell R730xd, каждый с 768 ГБ памяти, PCIe SSD с 6 ТБ и двумя 8-ядерными процессорами. Этот кластер содержит все остальные базы данных, включая Careers, Open ID, Chat, журналы исключений и другие сайты вопросов и ответов (например, Super User, Server Failure и др.).
На уровне базы данных мы хотим держать загрузку процессора на очень низком уровне, хотя на практике загрузка процессора будет немного выше, когда возникают запланированные проблемы с кэшированием (которые мы устраняем). В настоящее время NY-SQL02 и 04 являются основными серверами, а 01 и 03 — репликами, и мы только что перезагрузили их сегодня из-за обновления SSD. Вот как они выступили за последние 24 часа:
Наше использование SQL очень простое. Простое — значит быстро. Хотя некоторые операторы запросов могут быть искажены, наше взаимодействие с SQL происходит довольно нативно. У нас есть некоторая устаревшая Linq2Sql, но все наши новые разработки используют Dapper — наш открытый микро-ORM-фреймворк, который использует POCO. Позвольте объяснить иначе: у Stack Overflow в базе данных только одна сохранённая процедура, и я собираюсь удалить эту последнюю оставшуюся процедуру и заменить её кодом.
Библиотека
Давайте передумаем, вот что может помочь вам более напрямую. Я уже упоминал некоторые из них, но дам вам список множества открытых .Net-библиотек, которые мы поддерживаем и которыми пользуются все. Мы открываем их исходный код, потому что у них нет основной бизнес-ценности, но они могут помогать разработчикам по всему миру. Надеюсь, вы сможете использовать их сейчас:
- Dapper (.Net Core) — высокопроизводительный микро-ORM-фреймворк для ADO.Net
- StackExchange.Redis – высокопроизводительный клиент Redis
- MiniProfiler — лёгкий профайлер, который мы используем на каждой странице (также поддерживает Ruby, Go и Node)
- Исключительно — для логирования ошибок в SQL, JSON, MySQL и других
- Jil — высокопроизводительная сериализация и десериализация JSON
- Sigil – помощник генерации .Net CIL (используется, когда C# недостаточно быстр)
- NetGain — высокопроизводительный веб-сокетный сервер
- Opserver — панель мониторинга, которая напрямую опрашивает большинство систем и может получать информацию с Orion, Bosun или WMI
- Боцман — система мониторинга на фоне, написана на Go
|
Предыдущий:enum enum проверяет, включено ли значение в enumСледующий:Как быстро найти MB
|