Блоги проекта
Разработка
Сегодня я расскажу о еще одной, хоть и не столь масштабной, но ключевой концепции проекта - концепции потоков данных. Эта концепция не нова, на данный момент она в том или ином виде применяется в различных приложениях.
Но при правильном использовании она открывает очень широкие возможности. Впрочем, обо всем по порядку.
Под потоком данных (stream) обычно понимается некоторая сущность, позволяющая последовательно читать или записывать данные. Классическими примерами таких потоков являются файл на диске, область памяти, TCP/IP соединение, устройство ввода/вывода (например, принтер) и т.д. Каждый из них имеет свой программный интерфейс, который обычно сводится к подмножеству определенного набора операций (чтение, запись, перемещение курсора и т.д.).
Очевидно, иметь отдельный интерфейс для каждого потока данных - это неудобно и неуниверсально (как жаль, что этого до сих пор не понимают в Microsoft). При таком подходе, если я пишу, например, программу, обрабатывающую HTML-страницы, мне нужно написать отдельно модуль, читающий эти страницы из файлов на диске, отдельно - из zip-архивов, отдельно - качающий из интернета и т.д. Я, конечно, человек не ленивый и потружусь написать все эти модули. Но моя программа всегда будет ограничена в функциональности именно теми модулями, которые я реализую, а для добавления дополнительной функциональности - скажем, чтения страниц по протоколу FTP - потребуется еще один модуль.
В операционных системах семейства UNIX с этим обстоит намного лучше. Во-первых, здесь реализован универсальный интерфейс чтения/записи данных в абстрактный "файловый дескриптор", безотносительно источника данных, который скрывается за этим дескриптором. А во-вторых, файловая система UNIX позволяет рассматривать устройства как обычные файлы. Но и здесь не без проблем. Во-первых, предлагаемый ОС UNIX программный интерфейс предоставляет абстракцию только для функций чтения/записи, а открывать каждый из потоков все равно приходится при помощи отдельного интерфейса. А во-вторых, количество поддерживаемых файловой системой потоков данных ограничено и расширяемо только на уровне ядра.
В ядре Диптауна реализовано более красивое, на наш взгляд, решение. Во-первых, интерфейс потока данных универсален по отношению к операции открытия: необходимые параметры передаются в виде некоторой строки (например: file:info.txt или ftp://dev.deeptown.org/info.txt). Во-вторых, ядро позволяет очень просто - через наследование классов - добавлять реализации новых потоков данных.
Такой подход позволяет на уровне приложения полностью абстрагироваться от источника данных, с которым работает данное приложение. В итоге абсолютно все приложения, использующие данный интерфейс ядра, будут одинаково хорошо работать и с файлами, и с архивами, и с удаленными файлами (доступными по протоколу HTTP, FTP и пр.). Более того: при добавлении нового источника данных, все ранее написанные приложения будут отлично работать и с новым источником.
Кроме того, этот подход позволяет на уровне ядра определить некоторые универсальные механизмы работы с потоками данных, применимые для всех потоков, такие как: передача данных из одного потока в другой, фильтры, конверторы форматов данных и некоторые другие. Таким образом, во многих случаях работа с данными вообще не требует программирования, а заключается лишь в применении вышеуказанных механизмов в правильной последовательности.
Справедливости ради отмечу, что такой подход применяется во многих прикладных решениях - например, KDE и Gnome каждая имеет свою подсистему ввода-вывода (KIO и Gnome VFS соответственно), каждая из которых предназначена для решения схожих задач (насколько мне известно - я могу ошибаться, т.к. не знаком с ними подробно). О качестве интерфейсов по не знанию судить я не берусь, но здесь есть одна проблема: эти интерфейсы определены на прикладном уровне, и использоваться могут только в тех программах, которые написаны для KDE или Gnome соответственно.
В тексте я попытался сделать упор на сравнении различных подходов к проектированию. Думаю, что пример с потоками данных - наиболее простой, и в то же время наиболее показательный. Он демонстрирует, какие преимущества дает выбранный нами подход к проектированию, применяемый повсеместно в проекте. Фактически, все ядро Диптауна представляет собой набор интерфейсов и очень простых универсальных классов, которые манипулируют этими интерфейсами в соответствии с их смыслом. И это дает фантастическую функциональность! Например, если определить десять различных потоков данных, десять баз данных и десять объектов виртуального пространства, мы получим 101010 = 1000 различных комбинаций "объект-база-поток", и все тысяча будут нормально работать!
Сегодня я постараюсь рассказать о некоторых особенностях архитектуры системы Диптауна. А именно, с точки зрения объектов виртуального пространства.
Введение
В недавнем прошлом, после долгих обсуждений появилась концепция RPC, то есть механизма удаленного вызова функций. Суть этой концепции в том, что пользователь производит взаимодействие с некоторым объектом, вызывая его методы. При этом, "физически" объект может быть расположен "где-то в сети", то есть на удаленной машине. Но с точки зрения пользователя он расположен локально. При таком взаимодействии, пользователю не нужно заботиться ни об установлении сетевых соединений, ни о продумывании форматов передаваемых пакетов, ни об их собственно заполнении на одной стороне, передаче и приеме с "расшифровкой" на другой стороне. Все эти операции конечно же выполняются, но скрыты от глаз прикладного программиста. Выполняются они на уровне сетевой подсистемы, а именно протокола DTTP.
Концепция объектов диптауна
Первоначально, идея объектов и событий была разработана в рамках инфраструктуры Диптауна. Объект, -- это некоторая сущность, представленная в виртуальном пространстве. Это может быть и аватар, и бот... и даже нечто, не имеющее визуального представления. События, -- это соответственно то, что может с объектами произойти. В более общем смысле, события это сообщения доставляемые объектам. Отличие от традиционных сообщений заключается в том, что изначально неизвестно, где "физически" находится тот или иной объект, то есть неизвестен адрес сервера назначения. Транспортировка событий реализуется целиком силами протокола DTTP с использованием опорных серверов (например UIN сервера). При этом, адресом назначения для события является не ip адрес, а UID объекта. Таким образом достигается абстрагирование собственно виртуального пространства (и объектов виртуального пространства) от инфраструктуры серверов его поддерживающих.
Объекты RPC
С появлением реализации RPC понятие объектов несколько расширилось. Теперь объектом может быть не обязательно некоторая сущность виртуального пространства. В общем случае, это просто экземпляр некоторого класса, зарегистрированный на сервере как объект.
И вот как раз это позволяет нам подойти к концепции распределенных программ. Традиционно, распределенные вычисления применялись в случаях когда мощности одной отдельно взятой машины было недостаточно для решения поставленной задачи за разумное время. Это может быть и предсказание погоды, и расчет динамики ядерного взрыва, и моделирование уличного движения... Ну и рендеринг 3Д анимации конечно. Все эти задачи являются сугубо специфичными. При проектировании распределенных систем конечная цель была заранее известна и на нее делался уклон. Поэтому, грубо говоря взять кластер для рассчета погоды и переделать его для рендеринга, было практически невозможно (только путем создания новой инфраструктуры).
С появлением кластерных операционных систем дело несколько изменилось. Например проект ClusterKnoppix предоставляет свободную реализацию чего то вроде кластерной OC, в роли основного звена которой выступает проект OpenMosix. OpenMosix это система позволяющая объединить несколько машин (нод) в один кластер, а затем запускать поверх него обычные приложения (как уверяют авторы, в общем случае даже не требуется переписывать код). Собственно кластеризация осуществляется путем переноса потоков команд (или нитей) между нодами и распределением нагрузки. Данный подход является интересным, но проблема заключается в том, что кластер не имеет понятия о том, что же он выполняет. То есть, любое приложение выполняемое в этой среде представляется для него как совокупность потоков команд и данных которыми эти потоки оперируют.
Как ни странно, но система Диптауна позволяет решать распределенные задачи более элегантным образом.
Идея распределенных программ
Любая программа представляет собой совокупность нескольких подсистем: ввода данных, хранения, обработки и вывода соответственно. Каждая из подсистем, в свою очередь, разделяется на подсистемы более низкого порядка и т. д. до тех пор пока мы не дойдем до атомарных операций. Подсистемы взаимодействуют друг с другом передавая друг другу данные и контекст управления. Написание программы, в свою очередь сводится к формализации этих самых подсистем и написанию "правил" их взаимодействия.
Причем в большинстве случаев, подсистеме не важно, где "физически" находятся ее соседи. На локальной машине или где то в сети. Все что ей нужно, это знать что она может дать некоторый запрос соседу и получить от него ответ. Прослеживается полная аналогия с объектами диптауна.
Собственно идея заключается в повсеместном использовании RPC объектов при написании программ. Не обязательно клиент-серверных. При этом, все функционально-независимые части программ офрмляются в виде RPC объектов. Регламентируется внешний интерфейс этих объектов и некоторые их свойства. После этого объект добавляется в своего рода общий репозиторий.
На деле, этот подход принципиально не отличается от знакомого нам ООП, когда в роли подсистем выступают классы. RPC Объекты точно так же могут наследоваться друг от друга, использовать друг друга и т. д. Разница заключается в способе создания объекта и в обращении к нему.
Далее пишется собственно программа, которая использует эти объекты и "ставит им задачи". И вот тут начинается самое интересное =) Дело в том, что как уже было отмечено ранее, объект это сущность, не привязанная к конкретной машине. Более того в рамках протокола предусмотрены механизмы прозрачного переноса объекта с одной машины (узла) на другой, с сохранением всех данных и внутреннего состояния объекта. При этом, другие объекты все так же будут взаимодействовать с данным объектом и им не важно, был ли этот объект перемещен или нет.
Таким образом, мы получаем программу, которая автоматически может выполняться распределенно. Программу, которая не зависит ни от используемой ОС (поскольку код диптауна является кроссплатформенным), ни от фактических имеющихся вычислительных ресурсов. В общем случае, программа может выполняться в Сети, а Сеть представляется уже не совокупностью разрозненных узлов, а единой вычислительной средой, в которой все ресурсы могут быть свободно разделены между участниками. Средой, в которой стирается грань между локальной машиной и удаленной. Можно сказать, что вся Сеть выступает в роли одного гигантского мейнфрейма, к которому подключены все пользователи находящиеся в данный момент онлайн.
Варианты применения
Допустим, у нас есть офис некоторой компании, занимающейся производством программного обеспечения, а так же 3д дизайном. В этом офисе находится большое количество рабочих станций (например 20), а так же несколько серверов, предоставляющих услуги хранения данных, доступа в Диптаун и т. д.
Данная компания находится на пике прогресса и старается использовать все новейшие достижения в области IT для повышения своей производительности. Все разработки данной компании ориентированы на использование платформы Диптаун, так как это дает им большой выигрыш в производительности и в гибкости создаваемых ими программных продуктов :)
1) Допустим, что один из разработчиков, дописав свой модуль, решает скомпилировать проект (при этом подразумевается, что среда разработки так же написана с использованием нашей платформы). После начала процесса компиляции его рабочая станция начинает генерировать большое количество объектов и активно использовать ресурсы системы. Брокерный сервер, который следит за распределением нагрузки внутри офиса "видит", что один из узлов сети (а именно та самая рабочая станция, производящая компиляцию) в течение продолжительного времени оказывается сильно загруженным. При этом, брокер начинает автоматически перераспределять часть нагрузки с данного узла на остальные, менее загруженные машины сети, тем самым вовлекая в процесс дополнительную вычислительную мощность. Фактически, при таком подходе в процессе компиляции может участвовать весь офис, то есть вместо одного процессора рабочей станции, в распоряжении нашего программиста будет кластер из 20 процессоров рабочих станций. И все это в автоматическом, прозрачном режиме!
2) Другой пример: Дизайнер из той же компании закончив работу над новым рекламным роликом решает начать его рендеринг. Как известно, 3Д рендеринг это одна из самых ресурсоемких задач которые только можно представить в настоящий момент. Но и в этом случае, на помощь его рабочей станции придет кластер. При этом, в процессе помимо самих CPU, будут участвовать еще 20 видеокарт с графическими процессорами а также ресурсы хранилища данных серверов.
3) Использование кластеризации вовсе не означает что кластер должен использоваться только в моменты пиковой нагрузки на локальный узел. Можно представить себе домашнюю рабочую станцию, состоящую из нескольких машин. Например такую, как у Стефана Дайдака (http://www.stefandidak.com/). Фактически он уже имеет на руках кластер из десятка машин. Но использует он эти ресурсы дискретно, по факту. Все машины объединены в сеть, но только для того чтобы организовать единый рабочий стол. Реально же, каждая из задач все равно выполняется отдельно на отведенной для этого машине. Если же данный товарищ был бы в курсе платформы диптаун, то он смог бы использовать ее для организации единого вычислительного пространства %) При этом, используя возможности нашей распределенной файловой системы DISS и нашей же технологии мета интерфейса MEIN можно было бы выполнять те же задачи но гораздо более продуктивно. Конечно, все это при условии что наша платформа использовалась бы сторонними разработчиками для написания своих собственных систем...
Ну и в заключение хочу отметить, что данный подход будет использоваться "по назначению" в системе серверов диптауна. А именно на серверах поддержки ландшафта. При этом, они точно так же объединяются в кластер и распределяют между собой объекты виртуального пространства.
Заключение
Я думаю, уже всем понятен потенциал, заложенный в данной технологии :) Самое главное, что все это практически уже реализовано. Единственно, необходимо продумать механизмы распределения ресурсов, ну и некоторые аспекты проектирования подобных систем. Но это уже детали...
Сегодня я хотел бы рассказать об архитектуре сети Диптауна.
С точки зрения пользователя, сеть Диптауна представляет собой большой кластер. Т.е. нет никаких серверов - есть просто большой черный ящик. Под этим подразумевается то, что когда клиент говорит - "хочу доступ к такому-то объекту" - ему не надо знать адрес сервера, на котором расположен объект. Он просто кричит в черный ящик: "дайте мне такой-то объект" - и вот тебе пожалуйста.
А внутри это все организовано следующим образом.
Мозгом сети Диптауна являются сервера обсчета ландшафта, каждый из которых занимается обсчетом определенного количества объектов. Каждый сервер ландшафта отвечает за свой небольшой участок пространства.
Почему выбрана кластеризация именно такого рода? Просто потому, что скорее всего пользователь будет в один момент времени находиться в одной точке пространства - а соответственно, чтобы минимизировать количество серверов, с которыми соединен (через сервер доступа) пользователь, лучше всего разбить объекты между ЛС-ами по пространственному признаку. Каждый ЛС сервер соединен со своими соседями по пространству. Это нужно для того, чтобы в момент выхода объекта за пределы области ответственности сервера, сервер знал бы, куда именно нужно передать этот объект. Таким образом, сеть серверов обсчета ландшафта, распределенных по пространству, уже сама по себе позволяет бесконечно расширять это пространство и наращивать объектами. И все бы хорошо, если бы не некоторые проблемы.
Проблема первая и самая главная заключается в том, что в такой системе невозможно найти объект в сети. Ну тоесть грубо говоря есть некоторый объект, у него есть какой-то пусть даже уникальный по всей сети идентификатор. В качестве объекта может быть аватар или один из концов телепорта - не важно. Требуется найти этот объект, чтобы выполнить с ним какое-то действие. В этой концепции пришлось бы искать его, отправляя сообщения по всей сети, что сделало бы нагрузку на сеть огромной.
Для решения этой проблемы введены сервера генерации уникальных идентификаторов. Уникальный идентификатор объекта в нашей сети - это 16-байтовое число. Причем в этом числе 4 байта отвечают за адрес сервера, который выдал этот идентификатор (под адресом понимается не IP-адрес сервера, а внутренний адрес). Таким образом, в момент создания объекта он получает уникальный идентификатор, в котором указан адрес сервера, на котором зарегистрирован данный объект. Далее - обо всех перемещениях в сети сервера сообщают этому серверу. И когда требуется узнать, где находится объект - достаточно спросить у сервера, адрес которого есть прямо в идентификаторе объекта. Причем этот механизм встроен прямо в сетевой движок: т.е. вся эта процедура узнавания местоположения полностью автоматизирована ядром и совершенно прозрачна для пользователя (сообщения, передаваемые объектам, называются "событиями"). Всю черновую работу по вычислению текущей дислокации объекта ядро берет на себя.
Вторая проблема - это проблема оптимальной нагрузки. Когда проектируется пространство Диптауна, вряд ли можно сразу сказать наверняка, в какое место народ будет ходить толпами, а в какое - только изредка заглядывать. И поэтому тут все автоматизировано: для решения этой проблемы создана сеть брокеров. Эта сеть построена по древовидному принципу. Т.е. есть корневой брокер, у него в подчинении - буквально несколько серверов, которые только в общих чертах представляют о нагрузке в отведенных им зонах. Они, в свою очередь, имеют более мелких подчиненных и т.д. вплоть до самых мелких брокеров, которые уже занимаются непосредственно перераспределением нагрузки между серверами поддержки ландшафта.
Конечно, этот процесс - кому что считать - завязан на политику сети (в том смысле, что какие-то компании не захотят, чтобы их сервера занимались обсчетами каких-то левых кусков пространства, и наоборот - не хотели бы отдавать свои офисы в обсчет левым серверам), поэтому процесс перераспределения нагрузки будет тонко настраиваем (т.е. брокеру можно, например, сказать - что, мол, вот эту группу серверов не трогать ни при каких условиях).
Таким образом мы видим, что сеть Диптауна является саморегулирующейся. Кроме того, благодаря брокерам возможны механизмы зеркалирования некоторых участков. Т.е. если несколько серверов обсчета ландшафта падает - соответствующая часть пространства может быть по-быстрому перераспределена между остальными серверами.
Говоря о сети Диптауна, нельзя также не сказать про авторизацию в сети и сервера доступа. Но об этом можно прочитать в предыдущем посте. Прошу прощения за непоследовательность, конечно =)
Сегодня я бы хотел сказать пару слов про защиту информации в сети Диптауна. Я постараюсь на пальцах пояснить, как работает система авторизации пользователей в сети, и если среди читателей найдутся специалисты в области безопасности - буду весьма признателен за их мнение по этому поводу.
Учет всех пользователей сети ведут т.н. сервера авторизации. Этих серверов - больше одного, но некоторое ограниченное количество - по нашим прикидкам, 10-20 штук будет вполне достаточно. Все эти сервера известны всем пользователям сети, т.е. изменение списка этих серверов не может происходить в автоматическом режиме - только через обновление клиенского ПО. Фактически, это - единственный в какой-то мере централизованный элемент сети Диптауна (в том смысле, что больше никакой централизации в сети нет).
Каждый сервер сети Диптауна имеет сертификат сервера, выданный сервером авторизации. Таким образом устанавливается подлинность сервера - т.е. пользователь всегда может быть уверен, что соединяется с тем сервером, с которым он хотел соединиться.
Сразу оговорюсь, что в сети Диптауна используются свои форматы ключей и сертификатов. Т.е. под сертификатом не следует понимать стандартный X.509 сертификат. С точки зрения безопасности, сертификат - это всего лишь техническая информация о сервере, подписанная ключом доверенного сервера - в данном случае сервера авторизации. Проверка сертификата заключается в том, чтобы убедиться, что информация в сертификате соответствует ожидаемой, а подпись - верна.
Но самое интересное - это авторизация пользователя в сети. Сеть является распределенной, и пользователь в процессе своих блужданий может посещать десятки различных серверов. Каждый из них должен удостовериться, что пользователь - тот, за кого он себя выдает. Каким образом он это делает? В этом заключается суть процесса авторизации.
Прежде всего, пользователь соединяется с сетью через сервер доступа, который служит для него, фактически, проводником по сети. Т.е. пользователь единожды при запуске клиента соединяется с сервером доступа, и все дальнейшее общение происходит с ним: сервер доступа сам устанавливает соединения с остальными требуемыми данным пользователем серверами сети.
Как только пользователь соединяется с сервером доступа, он проходит процесс авторизации. Для этого сервер доступа соединяет его с сервером авторизации. После проверки логина и пароля, сервер авторизации выдает серверу доступа пользователя сертификат сессии, в котором указывается следующая информация:
- идентификатор пользователя;
- отпечаток публичного ключа сервера доступа;
- время действия сертификата.
Возможно также указать "черный" или "белый" список серверов, для которых действителен данный сертификат.
Когда процесс авторизации пройден - пользователь входит в Диптаун, и, сам того не подозревая, заходит на множество серверов обсчета ландшафта. Каждый раз, когда это происходит, сервер доступа пользователя передает серверу обсчета ланжшафта выданный пользователю сертификат сессии, по которому и устанавливается подлинность пользователя.
Наличие в сертификате такого поля, как отпечаток публичного ключа сервера доступа, делает возможным использование данного сертификата только с данным сервером доступа - т.е. даже если кто-то и сворует этот сертификат, он не сможет им воспользоваться, поскольку не имеет приватного ключа сервера доступа пользователя.
В этой схеме предполагается, что пользователь доверяет своему серверу доступа, а серверам авторизации доверяют все. На наш взгляд, такой подход сочетает в себе удобство и достаточную степень безопасности. Для тех серверов, где стандартный уровень безопасности кажется недостаточным, предусмотрены более сложные методы авторизации.
На данный момент полностью реализован модуль ядра, отвечающий за безопасность: разработаны форматы данных ключей и сертификатов, имеется возможность их генерации и проверки, реализована авторизация пользователя по сертификату.
В часности, мне вчера удалось выдать самому себе сертификат, который прошел все проверки среднего уровня безопасности. Конечно, это было сделано в рамках тестирования, но тем не менее это можно считать первой успешной авторизацией в сети Диптауна :)
Несколько дней назад я закончил работу над новой системой сборки проекта.
Под системой сборки (build system) я понимаю набор скриптов, которые компилируют исходные файлы проекта, собирают их в библиотеки и запускаемые файлы и инсталлируют это все в соответствующие директории.
В мире opensource все пользуются системой сборки GNU autotools, которая включает в себя утилиты autoconf и automake. С одной стороны, она универсальна: позволяет делать все, что угодно и как угодно. Но с другой стороны, написание файлов Makefile.am - это довольно неприятная задача: каждый новый добавленный исходный файл должен быть вписан в этот файл; каждая новая директория с исходными файлами должна быть вписана в configure.ac и Makefile.am директорией выше и т.д. Но самое ужасное - это скорость работы. Если библиотека состоит из одного файла - для ее сборки нужно подождать несколько минут, пока отработают скрипты autotools и сгенерированный ими configure, и одну секунду - пока скомпилируется этот жалкий файл.
Еще в самом начале проекта мы сочли это неправильным. Тем более, что в нашем проекте есть стандарт кода, который четко регламентирует, где и как должны храниться исходные файлы и каким образом их собирать в библиотеки - т.е. универсальность autotools нам в большей степени не нужна.
Первая версия нашей системы сборки базировалась на все тех же autotools: она автоматически искала исходные проекты и совсем простые в написании конфигурационные файлы библиотек (которые говорят, что собирать - библиотеку или программу, и от чего она зависит), и генерировала все те же файлы для autotools. Т.е. проблема с неудобством была решена, а вот проблема со скоростью - нет.
Пару дней назад я с нуля переписал систему сборки. Теперь общее время сборки проекта уменьшилось, наверное, раза в 3.
Собственно, к чему я это пишу. Новая система сборки позволяет (ну или скоро позволит) создавать бинарные дистрибутивы для linux. Это здорово, но у бинарных дистрибутивов в linux есть одна большая проблема: переносимость между дистрибутивами Linux. Ведь в каждом дистрибутиве свои версии системных библиотек. Т.е. чтобы быть полностью уверенными в переносимости, нужно для каждого дистрибутива Linux собрать свой дистрибутив Deeptown.
Было предложено решение: установить VMWare, создать с пару десятков виртуальных машин с различными линуксами и вперед. В принципе это можно даже автоматизировать. Но это очень, очень медленное решение!
Конечно, когда мы будем большими и богатыми, мы купим 20 компов, установим на них дистрибутивы и будем сидеть и наслаждаться сборкой. Но в принципе, когда мы будем большими и богатыми, мы откроем исходные коды, и каждый сможет собрать Диптаун сам, причем с теми настройками, которые ему будут удобнее.
Нет ли у кого-нибудь пары десятков серверов с зоопарком операционных систем? :)

