Spec-Zone.ru › Ruby 3.3

модуль DRb

Обзор

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

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

Объект в удалённом процессе локально представляется экземпляром 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.

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

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

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

Самый простой способ указать, что мы хотим, чтобы объект, который можно иначе маршализовать, передавался или возвращался как ссылка DRbObject, а не сохранялся и отправлялся как копия, заключается в добавлении модуля смешивания 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

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(/[.\/\\\:]/, "_")
            @loggers[name] = Logger.new(name, @basedir + "/" + fname)
        end
        return @loggers[name]
    end

end

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

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 следует использовать только тогда, когда клиенты надёжны.

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

Константы

VERSION

Атрибуты

primary_server[RW]

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

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

primary_server[RW]

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

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

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

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

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

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

current_server() Показать исходный код
# File lib/drb/drb.rb, line 1789
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 1934
def fetch_server(uri)
  @server[uri]
end

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

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

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

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

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

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1822
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 1888
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 1880
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 1912
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 1921
def remove_server(server)
  @server.delete(server.uri)
  mutex.synchronize do
    if @primary_server == server
      @primary_server = nil
    end
  end
end

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

start_service(uri=nil, front=nil, config=nil) Показать исходный код
# File lib/drb/drb.rb, line 1768
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.

См. DRbServer::new.

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

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

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

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

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

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

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

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

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

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

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

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

uri() Показать исходный код
# File lib/drb/drb.rb, line 1810
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 1832
def config
  current_server.config
rescue
  DRbServer.make_config
end

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

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

current_server() Показать исходный код
# File lib/drb/drb.rb, line 1789
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 1934
def fetch_server(uri)
  @server[uri]
end

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

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

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

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

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

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1822
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 1888
def install_acl(acl)
  DRbServer.default_acl(acl)
end

Set значение по умолчанию ACL на acl.

См. DRb::DRbServer.default_acl.

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

Set объект преобразования id по умолчанию.

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

См. DRbServer#default_id_conv.

regist_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1912
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 1921
def remove_server(server)
  @server.delete(server.uri)
  mutex.synchronize do
    if @primary_server == server
      @primary_server = nil
    end
  end
end

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

start_service(uri=nil, front=nil, config=nil) Показать исходный код
# File lib/drb/drb.rb, line 1768
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.

См. DRbServer::new.

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

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

Операция выполняется с основным сервером. Если в данный момент не работает основной сервер, выполняется ничтожная операция.

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

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

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

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

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

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

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

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

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

uri() Показать исходный код
# File lib/drb/drb.rb, line 1810
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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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