Децентрализация — Наше всё

Несколько лет назад в мире начался настоящим бум Децентрализованных, P2P решений. Возможно, катализатором этого процесса стали Крипто-валюты (blockchain, bitcoin). Возможно — желание людей получить воистину приватный интернет без слежки и всего остального. Как бы там не было, теперь не возможно делать вид, что P2P-технологии нам не нужны. Это наше будущее.

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

Для изучения Децентрализованных решений, было решено взять простую задачку — файлообменник (File sharing). В конечном итоге, хотелось получить программу, которая позволяла бы двум произвольным пользователям обменяться некоторыми файлами. Гланвое требование — serverless (не, не так, как в Амазоне, а так, чтобы не было центрального звена, которое бы потребовалось для выполнения обмена). В общем, что-то типа BitTorrent.

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

В первую очередь, нужно отменить две проблемы, которые, по момему мнению, являются главными в P2P-мире. Речь идёт о NAT (Network address translation), а также о необходимости использования Консенсусов для сходимости системы. Про NAT я поговорю далее, а про алгоритмы консенсусов уже написано довольно много всего, можно почитать например это — https://habr.com/company/bitfury/blog/327468/, https://en.wikipedia.org/wiki/Paxos_(computer_science) и https://en.wikipedia.org/wiki/Raft_(computer_science). Важно! Алгоритмы Paxos и Raft используютс в классических распределенных системах (например, в NoSQL-решениях).

Про NAT… Это жопа, честно. Если мы разрабатываем классические приложения, они запускаются в известной нам среде. Это сервера с белыми IP-адресами (чаще всего, даже IPv4). В случае, когда наша программа работает на клиентской стороне, у нас вообще нет никаких гарантий. Всё, что мы знаем — это наличие возможности сделать исходящий сетевой запрос. У клиента может быть один NAT. А ещё бывает несколько слоёв NAT — когда вам продаёт интернет мелкий провайдер, который является простым посредником некоторого большого провайдера, и этот мелкий провайдер сам находится за NAT. Несколько NAT наиболее часто встречаются на мобильных телефонов (а это грустно, ведь P2P и IOT (Internet of things) стоят бок о бок друг с другом).

Сущесвтует несколько концепций, как обойти NAT — NAT traversal. В общем-то, я бы выделил из всего этого следующее:

  • UPnP и NAT-PMP. Эта группа алгоритмов закладывается на то, что ваши Роутеры поддерживают эти стандарты. UPnP и NAT-PMP позволют динамически сконфигурировать отображение внутреннего порта на внешний, и закрепить этот порт за вашим приложением. Крайне удобная штука, но… Очевидно, что должен быть какой-то fallback — нужно что-то делать, если роутер не такой умный.
  • UDP hole punching и TCP hole punching. Подход основан на одной простой идее. NAT предполагает, что клиент может сделать некоторый исходящий запрос. Понятно дело, что сервер иногда должен как=то ответить. Поэтому реализации NAT позволяют серверу сделать ответ клиенту в течение нескольких секунд после изначального взаимодействия. Поэтому эта группа методов и называется «проделывание дыр», так как мы буквально делаем дыру, через которую происходит общение. Минусы в NAT Hole punching тоже есть. Во-первых, мы должны как-то узнать IP-адреса обоих устройств. Во-вторых, это крайне нестабильная вещь (особенно, TCP).
  • Третий подход, на самом деле — это миллион разных алгоритмов и спецификаций, собранных для предоставления простого API для P2P взаимодействия клиентов. Всё это дело называется ICE — Interactive Connectivity Establishment. ICE внутри себя может использовать первые 2 способа для выполнения соединения. Кроме того, в группу ICE входят два стандарта (и их реализации, конечно же) — STUN и TURN. STUN — супер простой сервис, который позволяет клиенту узнать свой внешний IP адрес. TURN — сервис, который позволяет организовать fallback, если не удалось установить P2P-соединение. То есть, через TURN сервисы мы погоним трафик, если что-то пошло не так. Важно, что есть много бесплатных STUN-серверов (например, от гугла), но нет бесплатных TURN-серверов (трафик гнать крайне дорого).

«Как-то мутно всё это дело звучит. Я просто хотел написать P2P-программу на своём любимом JavaScript, а не спеки читать.» — мысли большинства из нас. Увы, Децентрализованная разработка — это сложно. Тут надо понимать, и как сеть работает, и какие вообще клиенты бывают. Однако не стоит расстраиваться. Есть вполне себе высокоуровневые решения, скрывающие от нас всю боль децентарализованной разработки.

В первую очередь, нужно вспомнить об WebRTC — https://www.html5rocks.com/en/tutorials/webrtc/infrastructure/. Изначально WebRTC создавался, как решение для браузерного мультимедия — телефонных разговоров, видео чатов и всего такого. Однако позже WebRTC стали просто использовать для реализации P2P-программ. Это связано с тем, что WebRTC — это реализация ICE (о котором шла речь выше). То есть, тут есть всё, что вам уже знакомо — TURN и STUN сервера, а также UDP hole punching. Важно, что в WebRTC есть нативное API для P2P-коммуникации — установления соединения, а также передачи данных — RTCDataChannel.

Что делать, если хочется WebRTC, а вам приходится писать не на богоподобном JavaScript, а на языке врагов — Go, C++, Java, или Rust. Ну, варианта два. Либо реализовать WebRTC самому, либо взять готовую реализацию. Например, для Golang — https://github.com/pions/webrtc.

Кстат, вот программа для обмена файлов на WebRTC — https://www.sharedrop.io/.

Возможно, вам не понравится WebRTC из-за большого фокуса на мультимедия. В этом случае, можно взять одну из готовых Relay-реализаций. Есть множество P2P-проектов, цель которых — сделать свободный интернет без централизации. Таких решений много, есть уже и весьма стабильные реализации.

Я могу рекомендовать посмотреть на https://github.com/syncthing/syncthing — проект, который строит Бизнесово систему для синхронизации файлов на всех ваших компьютерах. Это что-то типа Resilio Sync и BitTorrent.

Однако у Syncthing есть и идеологическая цель — свободность интернета. Поэтому эти ребята кроме реализации программы для обмена файлами, попутно сделали набор протоколов, каждый из которых помогает организовать P2P-систему.

Как работает Syncthing? Система состоит из серверов нескольких типов. Во-первых, это Discovery Server-а. Они нужны, для того чтобы клиенты могли регистрироваться в системе, и видеть друг друга на сетевом уровне. Во-вторых, это Relay-сервера (хнык-хнык).

Relay-сервера — централизованная часть системы — нужны, как fallback — когда не удаётся установить P2P-соединение, трафик начинает идти через эти ноды. Relay-нодой тут может быть любой человек — достаточно поднять на своей машине этот сервис (трафик шифруется, так что это не так страшно, но, факт наличия обмена между нодами не скрывается).

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

Категории: Программирование