Skip to content

Instantly share code, notes, and snippets.

@pavlov200912
Last active July 1, 2025 09:29
Show Gist options
  • Save pavlov200912/c290a3d773776378a39fa8fab3e4f215 to your computer and use it in GitHub Desktop.
Save pavlov200912/c290a3d773776378a39fa8fab3e4f215 to your computer and use it in GitHub Desktop.
Конспект Проектирование Высоконагруженных систем 2021

package main

// Билеты по Проектированию Высоконагруженных систем

/*

1. Высоконагруженная система

Высокая нагрузка это что? Простой метод посчитать RPS - request per second 10 RPS много или мало? 1000 RPS много или мало? Ответ: "Это зависит"

High load наступает, когда мы не справляемся с нагрузкой. (это сильно зависит от требований пользователя и задачи)

TODO: ?? А что тут надо еще

2. Проактивные действия разработчика ВС

Как готовиться к High Load?

  • Хорошо понимать задачу
  • Дизайн (архитекутра) - система должна масштабироваться
  • Мониторинг - система должна быть наблюдаема
  • Знать свои технологии / инструменты (+ алгосы и структуры данных)

Как не готовиться?

  • Преждевременные оптимизации (Возможно тратим время просто так, часто портят архитектуру, оптимизированный код иногда выглядит ужасно)

TODO: ??? Добавить еще

3. Пользовательские истории

Требования к системе. Короткими и ясными фразами. На естественном языке.

A user story is an informal, general explanation of a software feature written from the perspective of the end user. Its purpose is to articulate how a software feature will provide value to the customer.

  • Stories keep the focus on the user. (a collection of stories keeps the team focused on solving problems for real users.)
  • Stories enable collaboration. (With the end goal defined, the team can work together to decide how best to serve the user)
  • Stories drive creative solutions.
  • Stories create momentum. (With each passing story the development team enjoys a small challenges and a small win)

“As a [persona], I [want to], [so that].”

4. API. Виды API

API - Application Programming Interface Хотим сделать взаимодействие между различными частями. Нужен протокол/интерфейс. Нам нужна абстракция, например мы не хотим знать детали монитора, но хотим уметь с ним взаимодействовавать Кроме этого, разграничиваем части системы, создав интерфейс.

Wiki: "It defines the kinds of calls or requests that can be made, how to make them, the data formats that should be used, the conventions to follow, etc. It can also provide extension mechanisms so that users can extend existing functionality in various ways and to varying degrees"

Виды API:

  • Библиотеки, фреймворки
  • Application Binary Interface (в такой-то кусок памяти положи то то)
  • Remote (RPC, REST, JSON-API, взаимодействие удаленных узлов)
  • Таблицы, структуры данных
  • Очереди, шины

5. Remote Procedure Call. JSON-RPC, gRPC

Remote Procedure Call - вызов процедуры.

Remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction.

RPC is a request–response protocol. An RPC is initiated by the client, which sends a request message to a known remote server to execute a specified procedure with supplied parameters. The remote server sends a response to the client, and the application continues its process.

Адресуем методы. Не говорим про объекты, структуры, сущности.

Какие бывают?

  • SOAP (сообщения в формате XML, объекты которые мы передаем в рамках протокола должны соотв. схеме)
  • JSON-RPC, XML-RPC (простые схемы)
  • gRPC (google RPC)
  • очень много других

JSON-RPC:

  • использует любой транспорт
  • сообщения - JSON объекты с определенной схемой
  • использует систему типов JSON

Пример запроса

{
 "jsonrpc": "2.0", // версия протокола
 "method": "substract",
 "params": [42, 43],
 "id": 1 // id запроса, будет копирован в ответ
}

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

{
 "jsonrpc": "2.0",
 "result": 19, // вместо result может быть error с кодом и сообщением ошибки
 "id": 1
}

Используется в очень примитивных ситуациях, плохая поддержка в Go

gRPC

  • Протокол
  • Фреймворк для разных языков и платформ (есть набор инструментов)
  • Бинарная сериализация
  • Транспорт: HTTP/2
  • Поддерживает двунаправленные потоки (можно не диалог, а монолог, просто стримить сообщения)
  1. Определяем протокол (*.proto file)
  2. Генерируем код клиента и сервера
  3. Реализуем сервер и клиент

Пример proto файла:

syntax = "proto3";

option go_package = "github.com/mp-hl-2021/grpc-example/ api";

package echo;

service Echo {
  rpc Do(EchoRequest) returns (EchoReply) {}
}

message EchoRequest {
  string line = 1;
  int32 num = 2;
}

message EchoReply {
  string echo_line = 1;
}

Генерируем код

 protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative api/echo.proto

Нам сгенерируют интерфейсы и методы заглушки, нужно их реализовать

6. REST API. REST API как набор ограничений. Недостатки.

Representational State Transfer

Архитектурный стиль, в котором клиент state machine, а сервер движок текстовых квестов (вы в такой-то комнате, у вас дверь на север, на юг, куда кликните? и т.д)

REST как набор ограничений:

  • Separation of Client and Server (имплементации сервера и клиента могут не знать о друг друге, пока каждая сторона понимает формат общения, они могут изменяться независимо)
  • Statelessness (Сервер не должен знать ничего о состоянии клиента и наоборот, таким образом клиент и сервер могут воспринимать любые получаемые сообщения, даже без истории взаимодействия до)
  • Cacheable (Данные в запросах и ответах могут быть помечены как кешируемые, стороны могут использовать те же данные при дальнейших запросах, не обновляя их)
  • Uniform interface (
    • Identification of resources; (for example with URLs)
    • manipulation of resources through representations; (имея ресурс representation of a resource клиенту хватает информации чтобы модифицировать или удалить его)
    • Self-descriptive messages (Каждое сообщение содержит достаточно информации, чтобы его обработать)
    • Hypermerdia as the engine of application state)
  • Layered system (Разделяем приложение на слои, ограничиваем каждую компоненту в взаимодействии: разрешаем только с компонентами ее слоя)

TODO: Недостатки REST? Он вообще ничего не сказал

7. HTTP и HTTPS: модель, методы и их семантика.

HTTP - HyperText Transfer Protocol (уровень общения application)

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

HTTP Model - все представляется в виде ресурсов, к каждому ресурсу можно получить доступ по URL (Uniform Resource Location)

Пример https://example.com:80/examples/1#test

Примерная грамматика URL: scheme://[userinfo@]host[:port]/path[?query][#fragment]

HTTP: Запрос

  • Метод (глагол)
  • Положение ресурса
  • Заголовки
  • Тело

Методы:

  • GET (получить предстваление ресурса)
  • HEAD - как GET но не возвращает тело (например метаданные взять из заголовка, когда ресурс обновился)
  • POST - передает серверу "нечто" в подчинение какому-то ресурсу (ресурсу по URL)
  • PUT - сохранить "нечто" с доступом по указанному URI
  • DELETE - удалить ресурс
  • etc

HTTP: ответ

  • Статус (некий код)
  • Заголовок
  • Тело

Статус коды:

Informational responses (100–199) Successful responses (200–299) Redirects (300–399) Client errors (400–499) Server errors (500–599)

HTTPS: HTTP over TLS (secured TCP), Алиса и Боб используют асимметричное шифрование (Открытый, приватный ключ у Алисы, но еще нужно подписывать сообщения у сторонних сервисов, которым все доверяют) Мы как владельцы домена приходим к Certification Authority, один раз валидируем свою личность, получаем подпись и кладем ее в конфиг сервера, теперь нам можно доверять (ну типа)

8. Аутентификация и авторизация.

Аутентификация - процесс, при котором система удостоверяется, что нечто является подлинным

  • ID соответствует пользователю
  • Сайт настоящий, а не поддельный

Авторизация - подтверждения права получить доступ к ресурсу. (и если есть доступ, то какой)

9. Аутентификация в Web. Basic HTTP Authentication. Form-based authentication. Cookies. JSON Web Token

Basic HTTP Authentication

(через HTTP заголовок) Клиент:

Authorization: Basic <credentials>
credentials := BASE64(id:password)
Authorization: Basic AkkBkerCdlaFASff6Adssf66a6f6jg891921bBbasdgvbadsC

Сервер (если не авторизован)

401 Unauthorized
WWW-Authenticate: Basic realm="User Visible Realm" // отвечает на что авторизация

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

Form-Based Authentication

  • Формочка, в нее записываем логин пароль
  • После этого например это в javascript записывается в json, отправляется POST запросом на сервер
  • Сервер валидирует данные этой формы, возвращает токен Надо как-то хранить токен, чтобы каждый раз не пришлось вводить логин-пароль Два варианта, про которые сказал Никита:
  • Cookies
  • Bearer Authentication

HTTP Cookie:

Механизм хранения состояния на стороне клиента

  • Аутентификация
  • Корзина товаров (так стараются не делать)
  • Персональные предпочтнения (темная тема)
  • Слежка за пользователями (third party cookies, я так понял это хотят забанить)

Чтобы выставить Cookies сервер возвращает заголовки

Set-Cookie: theme=light
Set-Cookie: sessionToken=6dasdgafasdsa; Expires=Wed, 09 Jun 2021 10:18:14 GMT

В следующий раз мы (client) уже в заголовке посылаем

Cookie: theme=light; sessionToken=6dasdgafasdsa

Виды HTTP Cookie:

  • Session (удаляются после закрытия браузера)
  • Persistent (удаляются с течением времени, хранятся внутри браузера)
  • Secure (работают только по HTTPS)
  • Same-site (Отправлять куки только в рамках домена, защищает от Cross Site Scripting) TODO: XSS
  • Third-party (Куки, установленные с других доменов)
  • HTTP-Only (Недоступно из клиентского JS)
  • ...

В Go для работы с куки есть http.Cookie

Cookie опасны! (XSS)

  • Требуйте HTTPS (secure cookies)
  • Помните, что на клиенте тоже могут подменить куки (сервер не должен всегда доверять содержимому куки, если мы хотим обезопасить себя, нужно хранить состояние клиента, в реальности корзину мы храним на сервере, session_id храним на сервере, и т.д в итоге от REST много где нам придется отказаться =((( )
  • Устанавливайте Domain

JSON Web Token

JWT, джот - кусок данных в формате JSON, использует подпись и/или шифрование Сервер вместе с джотом генерирует подпись, по которой может проверить, что JSON был сгенерирован им Обычно содержит информацию о правах доступа.

Обычно кодируют публичным и приватным ключом, приватный оставляют только у сервера. В Go это все делает либой rsa и с помощью openssl.

JWT не средство шифрования! Токен подписан, но любой сторонний сервис может узнать содержимое

10. Авторизация. Основная схема OAuth2.0.

Пример: Создать аккаунт с помощью Apple/Facebook/Google/Microsoft

  • Resource Owner (конечный пользователь, ресурс - мой акк в гугле)
  • Resource Server (хранит ресурсы, гугл)
  • Client (сторонее приложение, сокращатель ссылок)
  • Authorization Server - выдает клиенту токены, которые разрешают от имени RO получить доступ к ресурсам на RS

гугл = Resource Server

Client запрашивает разрешение у RO на авторизацию через гугл

RO выдает разрешение Client

Client показывает разрешение Authorization Server

Authorization Server выдает Client Access Token

Client показывает гугл Access Token

гугл выдает protected resource Client-у

Обновление токена:

Обычно вместе с Access Token выдается Refresh Token, Access Token работает какое-то время, используя Refresh Token Client может обновить Access Token

11. Работа с паролями.

  • Не храните пароли в открытом виде
  • Проверяйте сложность, валидируйте
  • Не изобретайте свои алгоритмы !!!
  • Защищайтесь от перебора
  • Подумайте о возможных уязвимостях (модель угроз)

Как хранить пароли?

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

12. Тестирование и модульное тестирование. TDD и Extreme Programming.

Модульное тестирование - способ тестирования отдельных "единиц" кода (unit testing)

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

Проблемы модульного тестирования:

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

В Go это делается с помощью модуля testing

TODO: Добавить больше инфы про тесты, тут много чего можно сказать

TDD:

  • Перед написание кода пишем требования (тесты) к коду
  • Помогает справится с проблемой пустой страницы (тесты писать просто)
  • Частично отвечает на вопрос (а надо ли тестировать тесты? когда изначально тест падает мы понимаем, он содержательный)
  • Помогает посмотреть на код с точки зрения пользователя кода
  • Улучшает покрытие тестами

[1] Пишем тесты [2] Проверить падает ли тест (если не упал, возвращаемся к пункту [1]) [3] Написать столько кода, чтобы пройти этот один тест [4] Проверить на других тестах, если упали пофиксить [5] Переходим на пункт [1]

Extreme Programming:

  • Хорошие практики доведены до абсурда
  • Нужны тесты, пишем TDD
  • Нужно code review, делаем его постоянно (два разработчика у ноутбука)
  • Нужно уметь поставлять продукт пользователю, давайте сразу поставим пустой продукт и с каждым изменением кода будем обновлять
  • Идея: сокращать feedback loop, не ждать долго ответа от заказчика, постоянно создаем тестируем прототипы и выдаем

13. Domain-Driven Design. Основная сложность разработки ПО и как её преодолевать.

Стиль построения моделей

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

В какой-то момент разработки ПО мы должны себя спросить

  • Правильную ли проблему решаем?
  • Понимает ли нас специалист?
  • А мы специалиста?
  • Как справиться со сложностью? (число модулей быстро растет, код запутывается)

Язык, на котором мы говорим о предметной области, не совпадает с языком, на котором мы обсуждаем софт, который ее реализует

Ubiquitous language - язык, сфокусированный на модели предметной области и использующийся всеми в команде, с целью связать всю деятельность команды с софтом.

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

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

Есть DDD Heuristics.

  • Предметной области нет дела до протокола и БД. (логике приложения плевать на линукс и виндовс, grcp, json-rcp, ядро с логикой не должно об этом знать)
  • Публичным интерфейсом объекта должно быть поведение, а не данные (если моделируем объекты в виде структур данных, мы жестко связываем участки кода, все завязано на поля, лучше предоставлять поведение, а не структуры данных)
  • В памяти держим только валидное состояние
  • В коде бизнес-логика отражается буквально (имена должны отражать предметную область)
  • Используйте ООП

Object-oriented Programming

Давайте вместо инструкций компьютера будем использовать другие способы абстракции. Будем для решения задачи использовать много маленьких компьюетров у которых есть 2-3 сложных метода, такие компьютеры назовем объеком. Они должны быть изолированы от других, но объекты могут общаться между собой сообщениями (вызов метода это сообщение к объекту).

14. Слоистые архитектуры.

Общий вид

  • Пользовательский интерфейс (http api)
  • Уровень приложения (use cases)
  • Предметная область (комната в чате - объект предметной области)
  • Инфраструктура (драйвер БД)

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

Следующий слой (выше) - приложение. Та среда, в которую помещаем объекты предметной области, чтобы они взаимодействовали.

Следующий слой (выше) - интерфейс. Отвечает за связь приложения с внешним миром.

От всех эти трех слоев зависит инфраструктура (линукс и т.д).

Все зависимости идут в одну сторону - в предметную область.

Примеры:

  • Предметная область (Entity, Value object, Service, Repository)
  • Инфраструктура (Драйверы, Фреймворки, Библиотеки)
  • Интерфейс (реализации репозиториев, http handlers)

15. Связность и сцепленность. Принципы SOLID.

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

Сцепленность - степень зависимости между модулями. (в го модулю == пакеты)

Связность (противопостовляется сцепленности) - степень зависимости между элементами внутри модуля. Насколько элеметны модуля хорошо вместе решают общую цель. (пакет с утилитами имеет очень низкую связность)

Добиваемся низкой сцепленности и высокой связности!

SOLID (woow!)

  • Single Responsibility principle
  • Open-Closed principle
  • Liskov substitution principle
  • Interface Segregation principle
  • Dependency inversion principle

Помогают понять как из базовых конструкций собрать большую программу.

Single Responsibility Principle

Закон Конвея - Организации проектируют системы, которые копируют структуру коммуникаций в этой организации.

Модуль должен иметь одну причину для изменений. (Модуль должен отвечать за одного актора)

Пример:

Класс сотрудник у которого три метода:

  • расчет зарплаты
  • отчет по часам работы
  • сохранить

Кто пользуется классом?

  • Финансовый директор (интересуется расчетом зарплаты)
  • HR (интересуется отработанными часами)
  • Технический директор (интересует бэкапы, использует save)

Функции calculatePay и reportHours скорее всего будут использовать функцию вычисления часов.

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

Создали объект, изменение в котором затрагивает сразу двух клиентов.

Как починить? Создаем три класса PayCalculator, HourReporter, EmployeeSaver. Разделили ответственность. Создаем классы по принципу: это поведение для этого клиента (актора), это поведение для этого.

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

Open-Closed Principle

Програмный артефакт должен быть открыт для расширения, но закрыт для модификаций.

(а чо делать если модель плохо написали? а ничего, принцип лоховский)

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

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

Пример:

В приложении надо сформировать финансовый отчет в виде веб-страницы. Отчет занимает несоклько экранов, отрицательные числа подсвечиваем красным.

Приходит стейкхолдер и говорит : Хочу тот же отчет, но для печати на ЧБ. (теперь красные символы надо сделать жирными)

Мы могли сделать так: (dataflow)

  • Financial Data -> Financial Web Reported -> Web reporter

И чего теперь делать? переписывать вообще всё?

Надо было делать так: -Financial Data -> Financial Analyzer -> Financial Report Data -Financial Report Data -> Web Reporter -Financial Report Data -> Print Reporter

Боб Мартин предлагает супер сложную картинку, которую словами не описать.

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

Liskov substitution principle

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 substituted for o2 then S is a subtype of T.

Два типа взаимозаменяемы, если вызывающая сторона не видит разницы.

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

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

Стараемся не использовать type assertions, т.е не выяснять конкретный тип объекта, лучше писать общий код.

Interface Segregation principle

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

(очень похоже на первый принцип)

Если есть три пользователя (актора) user1, user2, user3 которые из класса OPS дергают op1(), op2(), op3() соответственно, то это нарушение принципа. Следует сделать промежуточные интерфейсы U1 OPS, U2 OPS, U3 OPS и связать их с нашими пользователями.

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

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

Dependency inversion principle

  • Модули верхних уровней не должны зависеть от модулей нижних уровней.
  • Оба типа модулей должны зависеть от абстракций.
  • Абстракции не должны зависеть от деталей
  • Детали должны зависеть от абстракций

(Если меняем что-то в БД, логика менятся не должна, но если поменяли логику, возомжно придется поменять БД)

(Почему инверсии зависимостей? Как текут данные в программе? Сначала мы открываем TCP соединение, читаем байты, разбираем заголовок, понимаем метод, который нам нужен и переходим к логике. Т.е мы начинаем с самых деталей и переходим к абстракциям. Это прямая поседовательность, а зависимости наши должны быть в другом порядке. Dependencies = inverted DataFlow)

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

ТеПеРь ВЫ зНАеТе Про СоЛиД

16. Процесс, поток, виртуальная память. Контейнер, образ, Docker, Docker-compose.

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

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

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

Контейнер (это процесс) - средство изоляции процессов или групп процессов; средство контроля доступа групп процессов к ресурсам; стандартная единица поставки приложения.

Docker - система для управления и распространения контейнеров.

Docker Engine:

  • Docker Daemon (управляет контейнерами)
  • Docker API (чтобы стучаться внутрь докера и удаленно им управлять)
  • Docker CLI (интерфейс к апи)

Как создать контейнер? Docker Image - шаблон с инструкциями. Информация, позволяющая привести состояние внутри контейнера к нужному состоянию. Можно создавать свои образы на базе других.

Образ - на самом деле слепок состояний файловой системы. (такие образы персистенты, обновляя докер мы создаем diff и применияем его)

docker run -it debian bash // запустить контейнер с именем образа debian и запустить внутри процесс bash

Пример докер-образа для нашего приложения

FROM golang:1.16.2-alpine3.13 as builder // базовый образ
RUN mkdir /build
ADD . /build/ // все файлы из текущей рабочей директории помещаем в папку build/
WORKDIR /build
RUN go build -a -o chat-server cmd/chat-server/main.go // теперь в директории лежит бинарник

FROM alpine:3.13 // хотим образ без лишних файлов, только бинарник
COPY --from=builder /build/chat-sever .

ENTRYPOINT [ "./chat-server"] // точка входа в образ

Ещё надо примонтировать внутрь докера файлы с ключами, а также прокинуть порты.

Как общаться между контейнерами, если они изолированы?

  • Никак
  • Bridge (VLAN, виртуальная сеть)
  • Host (избавляемся от изоляции сети контейнера)
  • Overlay (несколько докер хостов, создаем оверлай сеть (??))
  • Plugins (можно еще различные плагины подключать)

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

docker network ls // все сети в докерах

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

Создаем мост:

docker network create --driver bridge chat-net

Потом по ключу --network указываем имя моста

Данные внутри контейнера сохраняются на новый слой. После смерти контейнера данные пропадают в небытие. Способы сохранять данные

  • Тома (докер выделяет папочки на хосте и там хранит свою инфу)
  • Монтирование (связываем папку на хосте и в докере)

Docker-compose: иснтрумент запуска многоконтейнерных приложений

(ну там похожим синтаксисом описываем несколько образов и типо они вместе запускаются)

17. Транзакции. ACID.

Книга с кабанчиком очень крутая, Никита советует прочитать про распределенные системы. (Designing Data-Intensive Applications, Martin Kleppmann)

  • НУЖНО ЛИ РАССКАЗЫВАТЬ ПРО ACID ТРАНЗАКЦИИ? СПРАШИВАЕТ НИКИТА
  • НЕТ, У НАС ЭТО БЫЛО НА КУРСЕ ПО БД. ОТВЕЧАЕТ ОЛЯ
  • НИКИТА ПРОПУСКАЕТ ВЕСЬ МАТЕРИАЛ ЭТОГО БИЛЕТА
  • СПАСИБО БОЛЬШОЕ ОЛЯ

Я нашел свой конспект по БД и там было слеюующее:

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

begin;
-- все что тут написано происходит внутри одной транзакции
commit;

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

ACID свойства транзакций:

  • Атомарность (либо транзакция полностью выполнена, либо не выполнена вовсе)
  • Согласованность (по мере выполнения транзакций данные переходят из одного корректного состояния в другое)
  • Isolation (конкурирующие за доступ транзакции выполняются последовательно и изолировано)
  • Долговечность (если транзакция завершена успешно, то те изменения данных, которые были ею произведены не могут быть потеряны ни при каких обстоятельствах)

Аномалии одновременного выполнения:

  • Потерянное обновление
  • Грязное чтение
  • Неповторяющееся чтение
  • Фантомное чтение

Потерянное обновление:

  • Операция разбивается на чтение и запись
  • Если мы тут читаем 1000, записываем 1100
  • update T set amount = amount + 100 where id = 1;
  • Одновременно в другом месте другой пользователь делает то же самое,
  • Он читает 1000, записывает 1100
  • Итого в поле лежит 1100
  • А где ещё 100 рублей? А все, нет 100 рублей.

Грязное чтение:

  • Чтение состояния базы в процессе выполнения другой транзакции
  • Данные явно могут изменится внутри транзакции, она еще не кончилась
  • Мы читаем грязные данные, неподтвержденные

Неповторяющееся чтение:

  • В рамках одной транзакции сделав один запрос два раза получаем разные данные
  • Из-за того, что их поменяла какая-то другая транзакция между моментами исполнения запроса

Фантомное чтение:

  • В рамках одной транзакции вызываем 2 раза запрос, например с агрегирующей функцией по данным
  • Между двумя выполнениями вклинилась транзакция, которая добавляет какую-то запись в наши данные
  • Опять два раза получили разные значения по одному казалось бы идемпотентному запросу
  • Даже repeatable read тут не помогает

Уровни изоляции:

  • Read uncommited: разрешается читать до коммита, но это грязные данные, они еще не подтверждены (например счетчик просмотров в youtube можно считать неточно, зато без блокировок)
  • Read commited: изменяемые внутри транзакции данные можно читать только после коммита (блокировка на меняемые данные, строки)
  • Repeatable read: блокировка на читаемые данные! (как-то сильно, зато запрещает невоспроизводимое чтение)
  • Serializable: запрещает добавление записей которые попадут в данные, с которыми взаимодействует другая транзакция

По умолчанию в СУБД используется read commited

Основной механизм выполнения ожновременных транзакций раньше - механизм блокировки и синхронизации (MS SQL Server) В postgres когда начинается какая-та транзакция делается snapshot данных и транзакция работает со своей версией данных, она не может их испортить в процессе выполнения Поэтому грязного чтения там быть не может

18. Типы распределенных систем. Мотивация к созданию распределенных систем.

26. Зачем нужен язык Go

  • Стремительно набирает популярность в серверной разработке
  • Хорошо иметь еще один инструмент в запасе (кроме петона)
  • Го простой язык, легко перейти с Java/C++/C
  • Документация Go, число деталей, намного меньше, чем у тех же C++/Java
  • Go стремится к простоте и единообразию (хочется всего один способ сделать вещь правильно)
  • Компилируется в нативный код (лучше чем JavaScript)
  • Автоматическое управление памятью (лучше чем C/C++)
  • Удобная поддержка конкуретного программирования (на уровне синтаксиса, в отличие от C++/Java) (в питоне есть async/await, но во-первых, у него нет своего планировщика, во-вторых, заражает весь остальной код, короче неудобно)
  • Много инфраструктурного кода написано на Go (docker, kubernetes, etc)

The Why of Go?

go is not good:

  • too simple / lack of sugar
  • no generics
  • bad dependency management
  • stuck in 70/80s
  • error handling
  • no unused imports
  • too opinionated
  • too verbose
  • no ternary operator
  • no macros or templates

Go's 21st Century Characteristics

  • Concurrency (C++ at Google was awful with concurrency)
  • Distributed Systems
  • Garbage Collection
  • Memory Locality
  • Readability

27. Синтаксис Go. Система пакетов. Базовые типы.

Пакеты в Go - набор файликов, которые делят область видимости типов и переменных

package main - точка входа */

@pftg
Copy link

pftg commented Oct 12, 2022

nice job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment