Если бы дизайнером D-Bus был я...
Jul. 29th, 2008 11:37 am![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Общесистемная/общесессионная шина сообщений, по которой приложения могут общаться между собой - штука полезная.
К сожалению, существующий кандидат на роль такой шины - D-Bus обладает рядом недостатков, затрудняющих понимание и использование её авторами приложений, и делающих практически невозможным использование этой технологии пользователями, обустраивающими свою рабочую среду с помощью наколенных скриптов.
Я бы сделал немножко по-другому
Как принято в Unix, все есть файл. Поэтому шина сообщений с точки зрения программы представляет собой двунаправленный файловый дескриптор (например, сокет) куда посылаются сообщения и откуда они принимаются.
Шина имеет топологию "звезда" - есть некий процесс - менеджер шины, с которым соединяются все процессы, желающие по этой шине общаться, и который решает кому какие сообщения форвардить.
Каждый из процессов, пользующихся шиной идентифицируется уникальным именем. Имя имеет иерархическую структуру (somethig.something.else).
Имена бывают двух видов - уникальные, генерируемые менеджером шины в момент соединения клиента (uuid.ХХХХХХХ где XXXXХХ некая алфовитно-цифровая строка) и well-known, к которым можно обращаться, чтобы получить некий известный сервис.
Вообще говоря, если менеджер шины знает, что процесса, предоставляющего данный сервис в данный момент в системе/сессии нет, он имеет право этот процесс запустить, если знает как.
Сообщения представляют собой текстовые строки, завершающиеся символом newline. Синтаксис может быть любым - лисповские S-expression, Tcl-списки, аналог командной строки shell. Мне представляется наиболее разумным использование синтаксиса shell.
Во-первых, этот синтаксис наиболее понятен всем пользователям. Во-вторых, shell таки самый высокоуровневый из распространенных скриптовых языков, Парсить сообщение в таком синтаксисе в шелл-скрипте можно посредством
Естественно, никакой типизации. Все есть строка. А как эту строку интерпретировать - вопрос договоренности между клиентом и сервисом (опубликовнаного интерфейса). Причем на уровне семантики, а не синтаксиса.
Формат сообщения следующий
Если в качестве получателя указана звездочка, то это широковещательное сообщение, которое рассылается всем клиентам (если только они не попросили менеджер шины фильтровать широковещательные сообщения и данное сообщение под фильтр не попало.
Если клиент отправлеят менеджеру шины сообщение, не содержащее адреса отправителя, то менеджер, прежде чем отправить сообщение получателю подставляет туда уникальное имя клиента, присвоенное ему при соединении.
При коннекте к менеджеру клиент получает от него сообщение
т.е. клиенту сообщается какое уникальное имя ему присвоили.
Если клиент хочет зарегистрировать well-known имя, он посылает сообщение:
на которое может получить либо
либо
После успешной регистрации имени, все имена в иереархическом дереве имен, начинающиеся с этого имени, считаются принадлежащими данному клиенту.
Сообщения с конкретным получателем могут быть вызовами методов, или ответами на вызов. У ответа на вызов получателем, как правило, является uuid.XXXX (исключение см выше).
Любой клиент, зарегистрировавший имя должен поддерживать метод Help, который вызывается следующими способами
Возвращает текстовое описание того, что этот клиент делает (apropos)
Возвращает список вложенных объектов в данном поддереве имен, которые поддерживаются данным клиентом.
Возвращает список методов, которые можно вызывать
Возвращает список параметров указанного метода
Возвращает информацию о том, что данный метод возвращает
Список ошибок, которые может вернуть данный метод.
Сообщение, являющееся ответом на вызов метода выглядит как
или
Методы объекта manager (менеджера шины)
Hello (вызывается неявно при соединении)
RegisterName (см выше)
UnregisterName - отмена регистрации имени (при закрытии соединеия отменяются все имена, зарегистрированные данным клиентом)
Filter glob-patterns - установка фильтра на широковещательные сообщения. Шаблон предваренный восклицательным знаком означает "не посылать сообщения удовлетворяющей данному шаблону". Просто шаблон - "посылать только сообщения, удовлетворяющие данному шаблону".
Client имя - возвращает известную менеджеру информацию об указанном клиенте. По крайней мере пользователя-владельца соответствующего процесса.
Available имя - возвращает статус доступности указанного имени. Может быть либо active (процесс, зарегистрировавший такое имя, выполняется) startable - менеджер знает, как такой процесс запустить и unknown.
Monitor on/off- заправшивает/отменяет пересылку данному клиенту ВСЕХ сообщений, бегающих по шине, независимо от отправителя получателя.
Debug on/off - разрешает данному клиенту посылать сообщения от имени любого отправителя. Этот метод завершается успешно только если юзер-владелец клиента совпадает с юзером-владельцем процесса-менеджера.
Некоторые соглашения о пространстве имен:
hw.* такие имена должны использовать в качестве имен отправителей сообщения, которые сообщают о событиях с оборудованем.
net.* сервисы, работающие с сетевыми соединеиями и сообщения о событиях с сетью.
Запуск сервисов менеджером - для каждой шины (системной/сессионной) имеется некоторая директория, в которую помещаются скрипты, запускающие сервисы, запускаемые on-demand. Имя скрипта должно совпадать с именем, которое зарегистрирует запущенный сервис.
Процессы-гейты, обеспечивающие гейтование шины в другой транспорт (например, сообщения протокола X11) вписываются в эту систему совершенно прозрачным образом. Единственное что для каждого клиента, приконнектившегося к гейту, гейт должен вызвать
manager.Hello явным образом, чтобы получить для него uuid.
Назовем данную конструкцию мы E-Bus. Во-первых, это "неприлично" звучит, а я люблю аббревиатуры на грани фола. Во-вторых, это сокращение от easy bus, в третьих - E - следующая буква алфавита после D,
К сожалению, существующий кандидат на роль такой шины - D-Bus обладает рядом недостатков, затрудняющих понимание и использование её авторами приложений, и делающих практически невозможным использование этой технологии пользователями, обустраивающими свою рабочую среду с помощью наколенных скриптов.
Я бы сделал немножко по-другому
Как принято в Unix, все есть файл. Поэтому шина сообщений с точки зрения программы представляет собой двунаправленный файловый дескриптор (например, сокет) куда посылаются сообщения и откуда они принимаются.
Шина имеет топологию "звезда" - есть некий процесс - менеджер шины, с которым соединяются все процессы, желающие по этой шине общаться, и который решает кому какие сообщения форвардить.
Каждый из процессов, пользующихся шиной идентифицируется уникальным именем. Имя имеет иерархическую структуру (somethig.something.else).
Имена бывают двух видов - уникальные, генерируемые менеджером шины в момент соединения клиента (uuid.ХХХХХХХ где XXXXХХ некая алфовитно-цифровая строка) и well-known, к которым можно обращаться, чтобы получить некий известный сервис.
Вообще говоря, если менеджер шины знает, что процесса, предоставляющего данный сервис в данный момент в системе/сессии нет, он имеет право этот процесс запустить, если знает как.
Сообщения представляют собой текстовые строки, завершающиеся символом newline. Синтаксис может быть любым - лисповские S-expression, Tcl-списки, аналог командной строки shell. Мне представляется наиболее разумным использование синтаксиса shell.
Во-первых, этот синтаксис наиболее понятен всем пользователям. Во-вторых, shell таки самый высокоуровневый из распространенных скриптовых языков, Парсить сообщение в таком синтаксисе в шелл-скрипте можно посредством
read message set -- $message
Естественно, никакой типизации. Все есть строка. А как эту строку интерпретировать - вопрос договоренности между клиентом и сервисом (опубликовнаного интерфейса). Причем на уровне семантики, а не синтаксиса.
Формат сообщения следующий
отправитель:получатель параметры
Если в качестве получателя указана звездочка, то это широковещательное сообщение, которое рассылается всем клиентам (если только они не попросили менеджер шины фильтровать широковещательные сообщения и данное сообщение под фильтр не попало.
Если клиент отправлеят менеджеру шины сообщение, не содержащее адреса отправителя, то менеджер, прежде чем отправить сообщение получателю подставляет туда уникальное имя клиента, присвоенное ему при соединении.
При коннекте к менеджеру клиент получает от него сообщение
manager.Hello:uuid.XXXXXXX
т.е. клиенту сообщается какое уникальное имя ему присвоили.
Если клиент хочет зарегистрировать well-known имя, он посылает сообщение:
manager.RegisterName имя
на которое может получить либо
manager.RegisterName:имя ok
либо
manager.RegisterName:имя error "имя уже занято"
После успешной регистрации имени, все имена в иереархическом дереве имен, начинающиеся с этого имени, считаются принадлежащими данному клиенту.
Сообщения с конкретным получателем могут быть вызовами методов, или ответами на вызов. У ответа на вызов получателем, как правило, является uuid.XXXX (исключение см выше).
Любой клиент, зарегистрировавший имя должен поддерживать метод Help, который вызывается следующими способами
some.client.Help
Возвращает текстовое описание того, что этот клиент делает (apropos)
some.client.Help objects
Возвращает список вложенных объектов в данном поддереве имен, которые поддерживаются данным клиентом.
some.client.Help methods some.client.subobject.Help methods
Возвращает список методов, которые можно вызывать
some.client.Help parameters Имя
Возвращает список параметров указанного метода
some.client.Help retval Имя
Возвращает информацию о том, что данный метод возвращает
some.client.Help errors Имя
Список ошибок, которые может вернуть данный метод.
Сообщение, являющееся ответом на вызов метода выглядит как
some.client.Method:uuid.caller ok возвращаемые значения
или
some.client.Method:uuid.caller error сообщение об ошибке [машинно-читаемые данные об ошибке]
Методы объекта manager (менеджера шины)
Hello (вызывается неявно при соединении)
RegisterName (см выше)
UnregisterName - отмена регистрации имени (при закрытии соединеия отменяются все имена, зарегистрированные данным клиентом)
Filter glob-patterns - установка фильтра на широковещательные сообщения. Шаблон предваренный восклицательным знаком означает "не посылать сообщения удовлетворяющей данному шаблону". Просто шаблон - "посылать только сообщения, удовлетворяющие данному шаблону".
Client имя - возвращает известную менеджеру информацию об указанном клиенте. По крайней мере пользователя-владельца соответствующего процесса.
Available имя - возвращает статус доступности указанного имени. Может быть либо active (процесс, зарегистрировавший такое имя, выполняется) startable - менеджер знает, как такой процесс запустить и unknown.
Monitor on/off- заправшивает/отменяет пересылку данному клиенту ВСЕХ сообщений, бегающих по шине, независимо от отправителя получателя.
Debug on/off - разрешает данному клиенту посылать сообщения от имени любого отправителя. Этот метод завершается успешно только если юзер-владелец клиента совпадает с юзером-владельцем процесса-менеджера.
Некоторые соглашения о пространстве имен:
hw.* такие имена должны использовать в качестве имен отправителей сообщения, которые сообщают о событиях с оборудованем.
net.* сервисы, работающие с сетевыми соединеиями и сообщения о событиях с сетью.
Запуск сервисов менеджером - для каждой шины (системной/сессионной) имеется некоторая директория, в которую помещаются скрипты, запускающие сервисы, запускаемые on-demand. Имя скрипта должно совпадать с именем, которое зарегистрирует запущенный сервис.
Процессы-гейты, обеспечивающие гейтование шины в другой транспорт (например, сообщения протокола X11) вписываются в эту систему совершенно прозрачным образом. Единственное что для каждого клиента, приконнектившегося к гейту, гейт должен вызвать
manager.Hello явным образом, чтобы получить для него uuid.
Назовем данную конструкцию мы E-Bus. Во-первых, это "неприлично" звучит, а я люблю аббревиатуры на грани фола. Во-вторых, это сокращение от easy bus, в третьих - E - следующая буква алфавита после D,