Spec-Zone.ru › Ruby 3

модуль DRb

Обзор

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

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

Объект в удалённом процессе локально представляется экземпляром 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 можно установить несколько протоколов одновременно; выбор между ними определяется схемой dRuby URI. Протокол по умолчанию выбирается схемой 'druby:' и реализован в DRb::DRbTCPSocket. Он использует обычные сокеты TCP/IP для связи. Альтернативный протокол, использующий сокеты доменной системы Unix, реализован в DRb::DRbUNIXSocket в файле drb/unix.rb и выбирается схемой 'drbunix:'. Пример реализации по протоколу HTTP можно найти в примерах, прилагаемых к основному распространению dRuby.

Компонент отображения ID-объект сопоставляет ID dRuby объекта с объектом, на который он ссылается, и наоборот. Реализация для использования может быть указана в рамках конфигурации DRb::DRbServer. По умолчанию реализация предоставляется DRb::DRbIdConv. Она использует ID объекта в ObjectSpace как его ID dRuby. Это означает, что ссылка dRuby на этот объект остается значимой только на протяжении всего времени жизни процесса объекта и на протяжении всего времени жизни объекта в этом процессе. Модифицированная реализация предоставляется DRb::TimerIdConv в файле drb/timeridconv.rb. Эта реализация сохраняет локальную ссылку на все объекты, экспортированные через dRuby, на настраиваемый период времени (по умолчанию 10 минут), чтобы предотвратить их сборку мусора за это время. Другая примерная реализация предоставляется в 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 1826
def config
  current_server.config
rescue
  DRbServer.make_config
end

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

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

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

Извлекает сервер с заданным uri.

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

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

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

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

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1816
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 1882
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 1874
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 1906
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 1915
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 1762
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 1795
def stop_service
  @primary_server.stop_service if @primary_server
  @primary_server = nil
end

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

Операция выполняется над основным сервером. Если основной сервер в данный момент не запущен, это операция без действия.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

here?(uri) Показать исходный код
# File lib/drb/drb.rb, line 1816
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 1882
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 1874
def install_id_conv(idconv)
  DRbServer.default_id_conv(idconv)
end

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

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

См. DRbServer#default_id_conv.

regist_server(server) Показать исходный код
# File lib/drb/drb.rb, line 1906
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 1915
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 1762
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 1795
def stop_service
  @primary_server.stop_service if @primary_server
  @primary_server = nil
end

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

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

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

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

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

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

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

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

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

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

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

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

Spec-Zone.ru

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