Spec-Zone.ru › Ruby 2.3

модуль DRb

Обзор

dRuby — это распределённая система объектов для Ruby. Она написана на чистом Ruby и использует собственный протокол. Дополнительные сервисы, помимо тех, что предоставляются исполняемой средой Ruby, такие как сокеты TCP, не требуются. Она не полагается на или не взаимодействует с другими распределёнными системами объектов, такими как CORBA, RMI или .NET.

dRuby позволяет вызывать методы в одном процессе Ruby для объекта Ruby, расположенного в другом процессе Ruby, даже на другом компьютере. Ссылки на объекты могут передаваться между процессами. Аргументы и возвращаемые значения методов передаются в сериализованном формате. Всё это происходит прозрачно для вызывающего удалённый метод и для объекта, на котором он вызывается.

Объект в удалённом процессе локально представлен экземпляром DRb::DRbObject. Он выступает как своего рода прокси для удалённого объекта. Методы, вызываемые на этом экземпляре DRbObject, передаются его удалённому объекту. Это организуется динамически во время выполнения. Для удалённых объектов нет статически объявленных интерфейсов, таких как IDL в CORBA.

Вызовы dRuby, сделанные в процесс, обрабатываются экземпляром DRb::DRbServer в этом процессе. Он восстанавливает вызов метода, вызывает его для указанного локального объекта и возвращает значение удалённому вызывающему. Любой объект может принимать вызовы через dRuby. Нет необходимости реализовывать специальный интерфейс или добавлять специальную функциональность. Кроме того, в общем случае объект не нужно явно регистрировать в DRbServer для получения вызовов dRuby.

Процесс, желающий выполнить вызовы dRuby на другой процесс, должен каким-то образом получить начальную ссылку на объект в удалённом процессе, отличную от возвращаемого значения удалённого вызова метода, так как изначально нет ссылки на удалённый объект, на котором можно вызвать метод. Это делается путём подключения к серверу через URI. Каждый DRbServer связывается с URI, таким как 'druby://example.com:8787'. DRbServer может иметь привязанный к нему объект, который действует как главный объект сервера. DRbObject можно явно создать из URI сервера. Удалённый объект этого DRbObject будет главным объектом сервера. Этот главный объект может затем возвращать ссылки на другие объекты Ruby в процессе DRbServer.

Вызовы методов через dRuby в значительной степени ведут себя так же, как обычные вызовы методов Ruby, выполняемые внутри процесса. Поддерживаются вызовы методов с блоками, а также возбуждение исключений. Помимо стандартных ошибок метода, вызов dRuby может также вызвать одно из специфичных для dRuby исключений, все из которых являются подклассами DRb::DRbError.

Любой тип объекта может передаваться в качестве аргумента вызова dRuby или возвращаться в качестве его возвращаемого значения. По умолчанию такие объекты сериализуются на локальном конце, затем десериализуются на удалённом конце. Удалённый конец, таким образом, получает копию локального объекта, а не распределённую ссылку на него; методы, вызываемые на этой копии, выполняются полностью в удалённом процессе, а не передаются локальному оригиналу. Это имеет семантику, подобную передаче по значению.

Однако, если объект не может быть сериализован, вместо него передаётся или возвращается ссылка dRuby на него. Это появится на удалённом конце как экземпляр DRbObject. Все вызовы методов на этот удалённый прокси перенаправляются на локальный объект, как описано в обсуждении DRbObject. Это имеет семантику, похожую на обычную передачу по ссылке в Ruby.

Самый простой способ указать, что мы хотим, чтобы иначе сериализуемый объект передавался или возвращался как ссылка DRb::DRbUndumped, а не сериализованный и отправленный как копия, заключается в включении модуля миксина DRb::DRbUndumped.

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

Примеры использования

Дополнительные примеры dRuby см. в каталоге samples в полном дистрибутиве dRuby.

dRuby в режиме клиент/сервер

Это иллюстрирует настройку простой системы drb клиент-сервер. Запустите код сервера и клиента в разных терминалах, сначала запустив код сервера.

Код сервера

require 'drb/drb'

# The URI for the server to connect to
URI="druby://localhost:8787"

class TimeServer

  def get_current_time
    return Time.now
  end

end

# The object that handles requests on the server
FRONT_OBJECT=TimeServer.new

$SAFE = 1   # disable eval() and friends

DRb.start_service(URI, FRONT_OBJECT)
# Wait for the drb server thread to finish before exiting.
DRb.thread.join

Код клиента

require 'drb/drb'

# The URI to connect to
SERVER_URI="druby://localhost:8787"

# Start a local DRbServer to handle callbacks.
#
# Not necessary for this small example, but will be required
# as soon as we pass a non-marshallable object as an argument
# to a dRuby call.
#
# Note: this must be called at least once per process to take any effect.
# This is particularly important if your application forks.
DRb.start_service

timeserver = DRbObject.new_with_uri(SERVER_URI)
puts timeserver.get_current_time

Удалённые объекты в dRuby

Этот пример иллюстрирует возвращение ссылки на объект из вызова dRuby. Экземпляры Logger находятся в процессе сервера. Ссылки на них возвращаются в процесс клиента, где на них можно вызывать методы. Эти методы выполняются в процессе сервера.

Код сервера

require 'drb/drb'

URI="druby://localhost:8787"

class Logger

    # Make dRuby send Logger instances as dRuby references,
    # not copies.
    include DRb::DRbUndumped

    def initialize(n, fname)
        @name = n
        @filename = fname
    end

    def log(message)
        File.open(@filename, "a") do |f|
            f.puts("#{Time.now}: #{@name}: #{message}")
        end
    end

end

# We have a central object for creating and retrieving loggers.
# This retains a local reference to all loggers created.  This
# is so an existing logger can be looked up by name, but also
# to prevent loggers from being garbage collected.  A dRuby
# reference to an object is not sufficient to prevent it being
# garbage collected!
class LoggerFactory

    def initialize(bdir)
        @basedir = bdir
        @loggers = {}
    end

    def get_logger(name)
        if !@loggers.has_key? name
            # make the filename safe, then declare it to be so
            fname = name.gsub(/[.\/\\:]/, "_").untaint
            @loggers[name] = Logger.new(name, @basedir + "/" + fname)
        end
        return @loggers[name]
    end

end

FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")

$SAFE = 1   # disable eval() and friends

DRb.start_service(URI, FRONT_OBJECT)
DRb.thread.join

Код клиента

require 'drb/drb'

SERVER_URI="druby://localhost:8787"

DRb.start_service

log_service=DRbObject.new_with_uri(SERVER_URI)

["loga", "logb", "logc"].each do |logname|

    logger=log_service.get_logger(logname)

    logger.log("Hello, world!")
    logger.log("Goodbye, world!")
    logger.log("=== EOT ===")

end

Безопасность

Как и в случае со всеми сетевыми службами, безопасность следует учитывать при использовании dRuby. Разрешая внешний доступ к объекту Ruby, вы не только позволяете внешним клиентам вызывать определённые вами методы для этого объекта, но по умолчанию и выполнять произвольный Ruby-код на вашем сервере. Рассмотрите следующее:

# !!! UNSAFE CODE !!!
ro = DRbObject::new_with_uri("druby://your.server.com:8989")
class << ro
  undef :instance_eval  # force call to be passed to remote object
end
ro.instance_eval("`rm -rf *`")

Опасности, связанные с instance_eval и подобными методами, таковы, что DRbServer обычно следует запускать с $SAFE, установленным хотя бы на уровень 1. Это отключит eval() и аналогичные вызовы для строк, передаваемых по сети. Приведённый выше пример кода использования следует этой практике.

DRbServer можно настроить с списком управления доступом, чтобы выборочно разрешать или запрещать доступ из указанных IP-адресов. Основной дистрибутив druby предоставляет класс ACL для этой цели. В общем случае этот механизм следует использовать вместе с, а не вместо, хорошего файрволла.

Внутреннее устройство dRuby

dRuby реализован с использованием трёх основных компонентов: сериализатор/десериализатор удалённого вызова метода; транспортный протокол; и отобразитель ID-объекта. Два последних могут быть непосредственно, а первый косвенно заменены для обеспечения разного поведения и возможностей.

Сериализация и десериализация удалённых вызовов методов выполняется экземпляром DRb::DRbMessage. Он использует модуль Marshal для сериализации вызова метода перед отправкой его по транспортному слою, а затем восстанавливает его на другом конце. Обычно нет необходимости заменять этот компонент, и нет прямого способа сделать это. Однако возможно реализовать альтернативный метод сериализации как часть реализации транспортного уровня.

Транспортный уровень отвечает за открытие клиентских и серверных сетевых соединений и перенаправление запросов dRuby через них. Обычно он использует DRb::DRbMessage внутри для управления сериализацией и десериализацией. Транспортный уровень управляется DRb::DRbProtocol. Несколько протоколов могут быть установлены в DRbProtocol одновременно; выбор между ними определяется схемой URI dRuby. По умолчанию транспортный протокол выбирается схемой 'druby:', и реализуется DRb::DRbTCPSocket. Он использует обычные сокеты TCP/IP для связи. Альтернативный протокол, использующий сокеты доменной системы UNIX, реализован в файле drb/unix.rb классом DRb::DRbUNIXSocket и выбирается схемой 'drbunix:'. Пример реализации через HTTP можно найти в примерах, прилагаемых к основному дистрибутиву dRuby.

Компонент сопоставления ID-объекта сопоставляет идентификаторы объектов dRuby с объектами, на которые они ссылаются, и наоборот. Реализацию, которую следует использовать, можно указать в рамках конфигурации DRb::DRbServer. По умолчанию реализация предоставляется DRb::DRbIdConv. Он использует идентификатор ObjectSpace объекта в качестве его идентификатора dRuby. Это означает, что ссылка dRuby на этот объект остаётся значимой только на протяжении всего срока службы процесса объекта и срока службы объекта в этом процессе. Модифицированная реализация предоставляется DRb::TimerIdConv в файле drb/timeridconv.rb. Эта реализация сохраняет локальную ссылку на все объекты, экспортированные через dRuby, на настраиваемый период времени (по умолчанию десять минут), чтобы предотвратить их сборку мусора в течение этого времени. Ещё один пример реализации приведён в sample/name.rb в основном дистрибутиве dRuby. Это позволяет объектам указывать свой собственный идентификатор или «имя». Ссылка dRuby может быть сделана постоянной через процессы, если каждый процесс регистрирует объект с тем же именем dRuby.

frozen_string_literal: false

Атрибуты

primary_server[RW]

Основной локальный сервер dRuby.

Это сервер, созданный вызовом start_service.

primary_server[RW]

Основной локальный сервер dRuby.

Это сервер, созданный вызовом start_service.

Методы публичного класса

config() Показать исходный код
# File lib/drb/drb.rb, line 1758
def config
  current_server.config
rescue
  DRbServer.make_config
end

Получить конфигурацию текущего сервера.

Если текущий сервер отсутствует, возвращается конфигурация по умолчанию. См. current_server и DRbServer::make_config.

current_server() Показать исходный код
# File lib/drb/drb.rb, line 1715
def current_server
  drb = Thread.current['DRb']
  server = (drb && drb['server']) ? drb['server'] : @primary_server
  raise DRbServerNotFound unless server
  return server
end

Получить текущий сервер.

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

Если вышеуказанное правило не находит сервер, генерируется ошибка DRbServerNotFound.

fetch_server(uri) Показать исходный код
# File lib/drb/drb.rb, line 1855
def fetch_server(uri)
  @server[uri]
end

Получает сервер с заданным uri.

См. также #regist_server и remove_server.

front() Показать исходный код
# File lib/drb/drb.rb, line 1769
def front
  current_server.front
end

Получить объект front текущего сервера.

Это вызывает ошибку DRbServerNotFound, если текущий сервер отсутствует. См. current_server.

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1748
def here?(uri)
  current_server.here?(uri) rescue false
  # (current_server.uri rescue nil) == uri
end

Является ли uri URI текущего локального сервера?

install_acl(acl) Показать исходный код
# File lib/drb/drb.rb, line 1814
def install_acl(acl)
  DRbServer.default_acl(acl)
end

Установить по умолчанию ACL на acl.

См. DRb::DRbServer.default_acl.

install_id_conv(idconv) Показать исходный код
# File lib/drb/drb.rb, line 1806
def install_id_conv(idconv)
  DRbServer.default_id_conv(idconv)
end

Установить объект преобразования идентификаторов по умолчанию.

Ожидается экземпляр, такой как DRb::DRbIdConv, который отвечает на to_id и to_obj и может преобразовывать объекты в и из ссылок DRb.

См. DRbServer#default_id_conv.

regist_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1838
def regist_server(server)
  @server[server.uri] = server
  mutex.synchronize do
    @primary_server = server unless @primary_server
  end
end

Регистрирует server в DRb.

Вызывается при создании нового DRb::DRbServer.

Если основного сервера нет, то server становится основным сервером.

Пример:

require 'drb'

s = DRb::DRbServer.new # automatically calls regist_server
DRb.fetch_server s.uri #=> #<DRb::DRbServer:0x...>
remove_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1847
def remove_server(server)
  @server.delete(server.uri)
end

Удаляет server из списка зарегистрированных серверов.

start_service(uri=nil, front=nil, config=nil) Показать исходный код
# File lib/drb/drb.rb, line 1694
def start_service(uri=nil, front=nil, config=nil)
  @primary_server = DRbServer.new(uri, front, config)
end

Запустить локальный сервер dRuby.

Новый сервер dRuby станет основным сервером, даже если в настоящее время другой сервер является основным.

uri — URI для привязки сервера. Если равно nil, сервер будет привязываться к случайному порту на стандартном локальном имени хоста и использовать стандартный протокол dRuby.

front — объект front сервера. Может быть nil.

config — конфигурация нового сервера. Может быть nil.

См. DRb::DRbServer.new.

stop_service() Показать исходный код
# File lib/drb/drb.rb, line 1727
def stop_service
  @primary_server.stop_service if @primary_server
  @primary_server = nil
end

Остановить локальный сервер dRuby.

Действует на основной сервер. Если в данный момент основной сервер не запущен, это ничто.

thread() Показать исходный код
# File lib/drb/drb.rb, line 1795
def thread
  @primary_server ? @primary_server.thread : nil
end

Получить поток основного сервера.

Возвращает nil, если основной сервер отсутствует. См. primary_server.

to_id(obj) Показать исходный код
# File lib/drb/drb.rb, line 1786
def to_id(obj)
  current_server.to_id(obj)
end

Получить идентификатор ссылки для объекта, используя текущий сервер.

Вызывает ошибку DRbServerNotFound, если текущий сервер отсутствует. См. current_server.

to_obj(ref) Показать исходный код
# File lib/drb/drb.rb, line 1778
def to_obj(ref)
  current_server.to_obj(ref)
end

Преобразовать ссылку в объект, используя текущий сервер.

Вызывает ошибку DRbServerNotFound, если текущий сервер отсутствует. См. current_server.

uri() Показать исходный код
# File lib/drb/drb.rb, line 1736
def uri
  drb = Thread.current['DRb']
  client = (drb && drb['client'])
  if client
    uri = client.uri
    return uri if uri
  end
  current_server.uri
end

Получить URI, определяющий локальное пространство dRuby.

Это URI текущего сервера. См. current_server.

Методы экземпляра (приватные)

config() Показать исходный код
# File lib/drb/drb.rb, line 1758
def config
  current_server.config
rescue
  DRbServer.make_config
end

Получить конфигурацию текущего сервера.

Если текущего сервера нет, возвращается конфигурация по умолчанию. См. current_server и DRbServer::make_config.

current_server() Показать исходный код
# File lib/drb/drb.rb, line 1715
def current_server
  drb = Thread.current['DRb']
  server = (drb && drb['server']) ? drb['server'] : @primary_server
  raise DRbServerNotFound unless server
  return server
end

Получить 'текущий' сервер.

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

Если вышеуказанное правило не находит сервер, возникает ошибка DRbServerNotFound.

fetch_server(uri) Показать исходный код
# File lib/drb/drb.rb, line 1855
def fetch_server(uri)
  @server[uri]
end

Получает сервер с заданным uri.

См. также #regist_server и remove_server.

front() Показать исходный код
# File lib/drb/drb.rb, line 1769
def front
  current_server.front
end

Получить объект front текущего сервера.

Это вызывает ошибку DRbServerNotFound, если текущего сервера нет. См. current_server.

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1748
def here?(uri)
  current_server.here?(uri) rescue false
  # (current_server.uri rescue nil) == uri
end

Является ли uri URI текущего локального сервера?

install_acl(acl) Показать исходный код
# File lib/drb/drb.rb, line 1814
def install_acl(acl)
  DRbServer.default_acl(acl)
end

Установить по умолчанию ACL в acl.

См. DRb::DRbServer.default_acl.

install_id_conv(idconv) Показать исходный код
# File lib/drb/drb.rb, line 1806
def install_id_conv(idconv)
  DRbServer.default_id_conv(idconv)
end

Установить объект преобразования идентификаторов по умолчанию.

Ожидается, что это будет экземпляр, такой как DRb::DRbIdConv, который отвечает на to_id и to_obj, и может преобразовывать объекты в и из ссылок DRb.

См. DRbServer#default_id_conv.

regist_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1838
def regist_server(server)
  @server[server.uri] = server
  mutex.synchronize do
    @primary_server = server unless @primary_server
  end
end

Регистрирует server в DRb.

Вызывается при создании нового DRb::DRbServer.

Если основного сервера нет, то server становится основным сервером.

Пример:

require 'drb'

s = DRb::DRbServer.new # automatically calls regist_server
DRb.fetch_server s.uri #=> #<DRb::DRbServer:0x...>
remove_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1847
def remove_server(server)
  @server.delete(server.uri)
end

Удаляет server из списка зарегистрированных серверов.

start_service(uri=nil, front=nil, config=nil) Показать исходный код
# File lib/drb/drb.rb, line 1694
def start_service(uri=nil, front=nil, config=nil)
  @primary_server = DRbServer.new(uri, front, config)
end

Запустить локальный сервер dRuby.

Новый сервер dRuby станет основным сервером, даже если в настоящее время другим сервером является основной.

uri — это URI для привязки сервера. Если nil, сервер привяжется к произвольному порту на имени хоста по умолчанию и будет использовать протокол dRuby по умолчанию.

front — это объект front сервера. Может быть nil.

config — это конфигурация нового сервера. Может быть nil.

См. DRb::DRbServer.new.

stop_service() Показать исходный код
# File lib/drb/drb.rb, line 1727
def stop_service
  @primary_server.stop_service if @primary_server
  @primary_server = nil
end

Остановить локальный сервер dRuby.

Действует на основной сервер. Если в настоящее время не работает основной сервер, это ничто.

thread() Показать исходный код
# File lib/drb/drb.rb, line 1795
def thread
  @primary_server ? @primary_server.thread : nil
end

Получить поток основного сервера.

Возвращает nil, если основной сервер отсутствует. См. primary_server.

to_id(obj) Показать исходный код
# File lib/drb/drb.rb, line 1786
def to_id(obj)
  current_server.to_id(obj)
end

Получить идентификатор ссылки на объект, используя текущий сервер.

Вызывает ошибку DRbServerNotFound, если текущего сервера нет. См. current_server.

to_obj(ref) Показать исходный код
# File lib/drb/drb.rb, line 1778
def to_obj(ref)
  current_server.to_obj(ref)
end

Преобразовать ссылку в объект, используя текущий сервер.

Вызывает ошибку DRbServerNotFound, если текущего сервера нет. См. current_server.

uri() Показать исходный код
# File lib/drb/drb.rb, line 1736
def uri
  drb = Thread.current['DRb']
  client = (drb && drb['client'])
  if client
    uri = client.uri
    return uri if uri
  end
  current_server.uri
end

Получить URI, определяющий локальное пространство dRuby.

Это URI текущего сервера. См. current_server.

Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API