.NET Разработчик
6.7K subscribers
475 photos
4 videos
14 files
2.37K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2263. #SystemDesign
Нельзя Реализовать Доставку Ровно-Один-Раз

Люди часто имеют фундаментальные заблуждения о том, как ведут себя распределённые системы. Например, в распределённой системе вы не можете иметь доставку сообщения ровно-один-раз (exact-once). Браузер и сервер, сервер и БД, сервер и очередь сообщений - это распределённые системы. Невозможно реализовать семантику доставки exact-once ни в одной из них.

Существует три типа семантики доставки:
- максимум-раз (at-most-once),
- хотя-бы-раз (at-least-once)
- только-раз (exact-once).
Первые два осуществимы и широко используются.

В письме, которое я вам отправляю, я прошу вас позвонить мне, как только вы его получите. Вы этого не делаете. Либо вам не понравилось моё письмо, либо оно потерялось на почте. Я могу отправить 1 письмо и надеяться, что вы его получите, или отправить 10 и предположить, что вы получите хотя бы 1. Но отправка 10 писем на самом деле не даёт никаких дополнительных гарантий. В распределённой системе мы пытаемся гарантировать доставку сообщения, ожидая подтверждения того, что оно получено, но что угодно может пойти не так: сообщение потеряется, подтверждение потеряется, получатель сломается, он просто медленный, есть сетевые задержки и т.п.

Атомарные протоколы вещания гарантируют надёжную и упорядоченную доставку сообщений. Но мы не можем доставлять сообщения надёжно и упорядоченно из-за разрывов сети и сбоев без высокой степени координации. Эта координация имеет свою цену (задержка и доступность), при этом всё ещё полагаясь на семантику at-least-once.

Репликация состояний стейт-машины — хороший пример. Изменения состояния идемпотентны, и многократное применение одного и того же изменения состояния не приводит к несоответствиям, пока порядок применения соответствует порядку доставки. Т.е. гарантия семантики at-least-once достаточна и упрощает реализацию. Но, если у наших сообщений есть побочные эффекты, всё пропало.

Есть несколько вариантов отправки подтверждения получателем:
1. Перед обработкой
Отправитель получает подтверждение, и удаляет (отмечает доставленным) сообщение. Но, если получатель выходит из строя до или во время обработки, данные теряются навсегда. Это семантика доставки at-most-once.
2. После обработки
Если процесс сбоит после обработки сообщения, но до доставки подтверждения, отправитель выполнит повторную доставку. Это доставка at-least-once.

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

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

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

Итого
Не существует такого понятия, как доставка exact-once. Мы должны выбрать меньшее из двух зол, которое в большинстве случаев является доставкой at-least-once. Это можно использовать для имитации семантики exact-once, гарантируя идемпотентность или иным образом устраняя побочные эффекты от операций.

Источник: https://bravenewgeek.com/you-cannot-have-exactly-once-delivery/
👍16👎1
День 2623. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Начало

Путь от провала на собеседовании до проектирования масштабируемых производственных систем.
Автор оригинала: Soma Sharma

Собеседование, которое изменило всё
Google. Второй раунд. Собеседование по системному проектированию: «Спроектируйте ленту Instagram для 2 миллиардов пользователей». У меня в голове всё помутнело. Я начал мямлить что-то о REST API. Упомянул MySQL. Поговорил о кэшировании… А потом тишина. Я понятия не имел, как это делать.
Шесть месяцев спустя я успешно прошёл собеседования по проектированию систем в Meta, Amazon и Netflix. Вот путь от полной растерянности до уверенного в себе системного проектировщика.

1. Я признал, что ничего не знал (и это нормально)
Годами я избегал проектирования систем. Я видел видео под названием «Разрабатываем Твиттер» и думал: «Это для архитекторов, а не для разработчиков, как я». Нет. Проектирование систем — это для всех, кто хочет понять, как ПО работает в масштабе. Я перестал спрашивать: «Достаточно ли я умён для этого?» и начал спрашивать: «Что нужно изучить в первую очередь?»

Проектирование систем — это набор концепций:
1) Инфраструктура:
- Как запросы проходят через интернет;
- Что происходит, когда вы вводите URL-адрес;
- DNS, балансировщики нагрузки, CDN.

2) Данные:
- Где хранить различные типы данных;
- Как сделать БД быстрыми;
- Когда использовать SQL, а когда NoSQL.

3) Масштабирование:
- Обработка 1 тыс пользователей против 1 млн;
- Стратегии кэширования;
- Горизонтальное и вертикальное масштабирование.

4) Надёжность:
- Что происходит при сбоях серверов;
- Как избежать единой точки отказа;
- Стратегии резервного копирования и восстановления.

2. Я разбил «проектирование систем» на части
1) Основы (недели 1–2)
Что происходит, когда вы набираете google.com?
- Поиск DNS (домен → IP-адрес);
- Установление TCP-соединения;
- Отправка HTTP-запроса;
- Обработка запроса сервером;
- Отправка ответа;
- Отображение страницы браузером.

Изученные концепции:
- DNS (телефонная книга интернета);
- Балансировка нагрузки (распределение трафика);
- CDN (сети доставки контента);
- Протоколы HTTP/HTTPS.

2) Данные и хранилища (недели 3–4)
SQL или NoSQL — когда что использовать?
Изученные концепции:
- SQL: структурированные данные, связи, ACID-транзакции;
- NoSQL: гибкая схема, горизонтальное масштабирование, итоговая согласованность;
- Индексирование: ускорение запросов в 100 раз;
- Репликация: копирование данных для повышения надёжности;
- Шардинг: разделение данных между несколькими БД.
Вывод: нет «лучшей» БД, есть подходящая для конкретного случая.

3) Методы масштабирования (недели 5–6)
Как системы обрабатывают миллионы пользователей?
- Вертикальное масштабирование: большие серверы (ограниченно, дорого);
- Горизонтальное масштабирование: больше серверов (неограниченно, сложно).

Кэширование:
- Хранение часто используемых данных в памяти (Redis, Memcached);
- 90% запросов попадают в кэш, а не в БД;
- Ускоряет работу систем в 100 раз.

Балансировка нагрузки:
- Распределение запросов между несколькими серверами: по очереди, по наименьшему количеству соединений, по хэшу IP;
- Предотвращает перегрузку какого-либо отдельного сервера.

Очереди сообщений:
- Разделение сервисов;
- Обработка пиковых нагрузок;
- Асинхронная обработка задач.

4) Архитектурные шаблоны (недели 7–8)
Монолитная архитектура:
Простота разработки и развёртывания;
Лёгкость понимания;
Сложность масштабирования отдельных частей независимо друг от друга;
Сбой в одном месте ведёт к сбою всей системы.

Микросервисы:
Независимое масштабирование компонентов;
Разные команды отвечают за разные сервисы;
Сложное развёртывание;
Проблемы распределённых систем.

Архитектура, управляемая событиями:
- Сервисы взаимодействуют посредством событий;
- Слабая связанность;
- Kafka, RabbitMQ для передачи сообщений.

Архитектура — это не вопрос «что лучше», а вопрос «что подходит для вашего масштаба и команды».

Продолжение следует…

Источник:
https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍17
День 2624. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Продолжение

Начало

3. Я перестал смотреть обучающие видео и начал смотреть, как люди думают
Переломный момент: пробные собеседования. Вместо того чтобы смотреть, как кто-то объясняет концепции, я смотрел, как люди решают задачи в реальном времени.

Обучающие видео учат вас, что делать. Пробные собеседования учат, как думать. Кандидаты на пробных собеседованиях:
- Задавали уточняющие вопросы;
- Совершали ошибки и исправляли их;
- Обсуждали компромиссы;
- Меняли подходы в процессе проектирования.
Я научился процессу, а не только ответам.

Что я узнал, наблюдая?
1) Всегда задавайте уточняющие вопросы
- Каков масштаб? (1000 пользователей? 1 миллиард?)
- Каковы основные функции?
- Что важнее: скорость или стабильность?
- Для чего мы оптимизируем (время или размер)?

2) Чётко определите требования
Функциональные:
- Публикация фото;
- Подписка на других;
- Просмотр ленты.

Нефункциональные:
- 100 миллионов активных пользователей в день;
- Загрузка ленты менее чем за 200 мс;
- 99,9% времени безотказной работы;
- 10 миллионов фотографий загружается ежедневно.

3) Всегда обсуждайте компромиссы
Никогда не говорите: «Мы будем использовать Redis для кэширования».
Говорите: «Мы будем использовать Redis для кэширования, потому что он работает в оперативной памяти (быстро), но нам понадобится стратегия резервного копирования, поскольку данные изменчивы».

4. Я начал рисовать (хотя не умею)
Неожиданное открытие: эскизы помогли во всём разобраться.
Я не художник. Мои диаграммы — это просто прямоугольники и стрелки. Но рисунок «клиент → балансировщик нагрузки → серверы приложений → база данных» позволяет наглядно представить абстрактные понятия.

1) Поток запроса стал реальным
Когда я нарисовал путь запроса пользователя, стало:
- Видно, где возникают узкие места;
- Понятно, почему помогает кэширование;
- Очевидны точки отказа.

2) Компоненты обрели смысл
Рисование поможет понять:
- Зачем размещать кэш между приложением и БД;
- Где разместить очередь сообщений;
- Когда следует разделять на микросервисы.

3) Компромиссы стали очевидны
Рисование двух подходов рядом покажет:
- Какой дизайн проще;
- Какой масштабируется лучше;
- Какой стоит дороже.

Процесс рисования
1) Нарисуйте счастливый сценарий (всё работает);
2) Добавьте сценарии сбоев (что ломается?);
3) Добавьте решения (кэширование, репликация, балансировка нагрузки);
4) Оцените показатели (запросы в секунду, хранилище, пропускная способность).

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

Продолжение следует…

Источник:
https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍10
День 2625. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Продолжение

Начало
Продолжение

5. Я проектировал реальные системы (на бумаге, а затем в коде)
Теория без практики бесполезна. Освоив основы, я перестал просто наблюдать. Я начал проектировать системы. Каждое воскресенье я выбирал реальную систему: WhatsApp, YouTube, Uber, Instagram, TinyURL, Netflix, Twitter, Dropbox.

Шаблон разработки системы
1) Функциональные требования (Что нужно делать?)
Пример (Twitter):

- Публиковать твиты (280 символов);
- Подписываться на пользователей;
- Просматривать ленту (твиты от людей, на которых вы подписаны);
- Лайки/ретвиты.

2) Нефункциональные требования (Масштабируемость, производительность, доступность)
- 300 млн активных пользователей;
- 500 млн твитов в день;
- Загрузка ленты <300 мс;
- 99,95% времени безотказной работы.

3) Оценка (приблизительные расчёты)
Хранилище:
- 500 млн твитов в день × 280 символов × 365 дней = 51 ТБ/год (только текст);
- Фотографии: 200 млн/день × 200 КБ = 40 ТБ/день;
- Видео: отдельный расчёт.

Трафик:
500 млн твитов/день ÷ 86400 секунд = ~5800 твитов/секунду.
Пик: 5x среднее значение = 29000 твитов/секунду

4) Высокоуровневый дизайн (нарисуйте архитектуру)
Клиент (веб/мобильное приложение) -> Балансировщик нагрузки -> API-серверы (сервис твитов, сервис ленты, сервис пользователей) -> Кэш (Redis) + БД (PostgreSQL, Cassandra) -> Объектное хранилище (S3 для медиафайлов).

5) Детальный дизайн (подробный анализ компонентов)
Как сгенерировать ленту?
- Подход 1: Пул-модель (интенсивное чтение)
Когда пользователь открывает приложение, запросить все подписки, получить последние твиты, объединить и отсортировать по времени.
Проблема: медленно для пользователей, подписанных на тысячи человек.

- Подход 2: Пуш-модель (интенсивная запись)
Когда кто-то публикует твит, отправить его во все ленты подписчиков. Быстрое чтение (предварительно вычислено).
Проблема: дорого для знаменитостей (миллионы подписчиков).

- Подход 3: Гибридный
Пул для твитов знаменитостей. Пуш для обычных пользователей.

6) Масштабирование и оптимизация
- Добавить кэширование (Redis);
- Шардирование базы данных (по user_id);
- Асинхронная обработка (очереди сообщений);
- CDN для медиафайлов.

7) Обработка сбоев
- Что если БД выйдет из строя? (Репликация)
- Что если сервер приложения выйдет из строя? (Проверки работоспособности балансировщика нагрузки)
- Что если кэш выйдет из строя? (стратегия Cache-aside).

Я проектировал по одной системе в неделю в течение 12 недель. К 5й системе я перестал гуглить решения, а стал мыслить самостоятельно. К 10й я смог сравнивать различные подходы и обосновывать свой выбор. В этом разница между запоминанием и пониманием. Я также читал решения распространённых задач проектирования систем, чтобы выявить недостатки своего решения и улучшить его.

Продолжение следует…

Источник:
https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍22
День 2626. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Продолжение

Часть 1
Часть 2
Часть 3

6. Применение в работе (реальные системы и ограничения)
Теория бесполезна, пока вы не создадите что-то реальное. На работе я был в команде, разрабатывавшей систему обработки платежей:
- Большой объём транзакций;
- Требуется строгая согласованность данных;
- Нулевая терпимость к потере данных;
- Сложная бизнес-логика.

Что я применил
1) Архитектурное решение - разделить монолит на сервисы:
- платежи;
- уведомления;
- бухгалтерия;
- обнаружение мошенничества.
Почему: Каждый сервис имеет разные потребности в масштабировании.

2) Стратегия коммуникаций:
- Синхронная: REST API для операций, ориентированных на пользователя.
- Асинхронная: Kafka для внутренних событий:
Платеж обработан
→ Уведомить пользователя;
→ Обновить учётную запись;
→ Провести проверку на мошенничество.

3) Согласованность данных:
Проблема: деньги не могут быть дублированы или потеряны.
Решение:
- Ключи идемпотентности (предотвращение дублирования транзакций);
- Паттерн Saga для распределённых транзакций;
- Паттерн Источники Событий для аудита.

4) Надёжность:
- Прерыватели цепи (предотвращение каскадных сбоев)
- Повторные попытки с экспоненциальной задержкой;
- Очереди недоставленных сообщений.

5) Мониторинг:
- Отслеживание задержек (P50, P95, P99);
- Оповещения об уровне ошибок;
- Уровень успешности транзакций;
- Панель мониторинга в реальном времени.

Создание реальных систем научило меня:
1) Идеальных проектов не существует
- Каждый выбор — это компромисс;
- Бизнес-ограничения имеют значение;
- Технический долг неизбежен.

2) Эксплуатация важнее проектирования
- Мониторинг и оповещения критически важны;
- Отладка распределённых систем сложна;
- Необходимо планирование отката.

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

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

7. Я обучал других (лучший способ учиться)
Обучение выявляет пробелы в понимании.

Что я начал делать
1) Наставлял младших инженеров:
- Объяснял кэширование, базы данных, масштабирование;
- Разбирал реальные системные проекты;
- Отвечал на их вопросы «почему».

2) Проводил внутренние технические презентации:
- «Как работает наша платежная система»;
- «Введение в очереди сообщений»;
- «Базы данных: когда что использовать».

3) Писал посты в блоге с диаграммами:
- Помогает систематизировать мысли;
- Приходится ясно объяснять;
- Появляется обратная связь от читателей.

4) Проводил пробные собеседования с коллегами:
- Практика в формулировании мыслей;
- Умение справляться с трудностями;
- Разные подходы к решению.

Почему обучение сработало?
Если вы не можете объяснить что-то просто, значит, вы недостаточно хорошо это понимаете.

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

Преподавание превращает понимание в мастерство.

Окончание следует…

Источник:
https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍9
День 2627. #Карьера #SystemDesign
Как я Освоил Системное Проектирование. Окончание

Часть 1
Часть 2
Часть 3
Часть 4

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

Книги:
1) «System Design. Подготовка к сложному интервью» Сюй А.
- Красивые диаграммы;
- Более 15 вариантов системного проектирования.

2) «Высоконагруженные приложения. Программирование, масштабирование, поддержка» Клеппман М.
- Глубокий технический анализ;
- Читать после изучения основ;
- Отличный справочник.

Сдвиг мышления, который изменил всё
Перед изучением системного проектирования.
Синдром самозванца:
- «Я недостаточно опытен»
- «Это слишком сложно»
- «Я никогда не пойму распределённые системы»

Тревога перед собеседованием:
- Надеюсь, я сталкивался с этой проблемой;
- Паника, когда застреваю;
- Заучивание решений.

Карьерный застой:
- Ограничен задачами по программированию;
- Неучастие в принятии архитектурных решений;
- Мимо руководящих должностей.

После освоения системного проектирования
Уверенность:
- Возможность обсуждать архитектуру с кем угодно;
- Понимание компромиссов;
- Мышление в масштабе.

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

На собеседованиях по системному проектированию (и в реальной работе):
Плохой ответ: «Мы будем использовать микросервисы и Kafka».
Хороший ответ: «Мы могли бы использовать микросервисы для независимого масштабирования, но, учитывая размер нашей команды (5 инженеров) и текущий масштаб (10 000 пользователей), хорошо структурированный монолит с чёткими границами модулей будет проще в обслуживании. Мы можем выделить сервисы позже, когда достигнем 100 тыс пользователей или нам потребуется независимое развёртывание».

Что изменилось:
- Продемонстрировал понимание компромиссов;
- Учел команду и масштаб;
- Предоставил обоснование;
- Обсудил эволюцию.

Интервьюерам не нужны идеальные проекты. Им нужно увидеть, как вы мыслите.

Итого
Проектирование систем — это навык, а не талант.
Вам не нужны:
- 10 лет опыта;
- Степень в области компьютерных наук;
- Фотографическая память;
- Природный талант.

Нужно только:
- Любопытство изучать, как всё работает;
- Готовность к систематическому обучению;
- Практика проектирования реальных систем;
- Терпение.

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

Источник: https://medium.com/javarevisited/how-i-learned-system-design-861efe86f173
👍16