if price != lastPrice
lastPrice = price
notify_observers(Time.now, price)
end
sleep 1
end
end
end
ticker = Ticker.new(MockPrice.new('MSFT'))
DRb.start_service('druby://localhost:9001', ticker)
puts 'Нажмите [return] для завершения.'
gets
На платформе Windows примененный способ завершения программы вызывает сложности. Функция gets
в этом случае может блокировать главный поток. Если вы это видите, попробуйте вместо обращения к gets
поставить DRb.thread.join
(а завершайте программу нажатием Ctrl+C).
Неудивительно, что клиент (листинг 20.3) начинает с установления соединения с сервером. Он получает ссылку на объект показа котировок и устанавливает верхний и нижний пороги изменения цены. Затем клиент выводит сообщение пользователю всякий раз, как цена выходит за пределы указанного диапазона.
require 'drb'
class Warner
include DRbUndumped
def initialize(ticker, limit)
@limit = limit
ticker.add_observer(self) # Любой объект Warner
# является наблюдателем.
end
end
class WarnLow < Warner
def update(time, price) # Обратный вызов наблюдателя.
if price < @limit
print '--- #{time.to_s}: Цена ниже #@limit: #{price}
'
end
end
end
class WarnHigh < Warner
def update(time, price) # Обратный вызов наблюдателя.
if price > @limit
print '+++ #{time.to_s}: Цена выше #@limit: #{price}
'
end
end
end
DRb.start_service
ticker = DRbObject.new(nil, 'druby://localhost:9001')
WarnLow.new(ticker, 90)
WarnHigh.new(ticker, 110)
puts 'Нажмите [return] для завершения.'
gets
Модуль DRbUndumped
(см. листинге 20.3) следует включать в любой объект, который не нужно подвергать маршалингу. Самого присутствия этого модуля в числе предков объекта достаточно, чтобы drb
не пытался применять к нему маршалинг. Вот исходный текст этого модуля целиком:
module DrbUndumped
def _dump(dummy)
raise TypeError, 'can't dump'
end
end
Приложение из этого раздела достаточно содержательно, и в то же время в нем легко разобраться. Есть и другие подходы к решению подобных задач. Но способ, показанный нами, демонстрирует простоту и элегантность распределенного Ruby.
20.3. Rinda: пространство кортежей в Ruby
Термин «пространство кортежей» появился в 1985 году, а сама идея еще старше.
Пока реализация пространства кортежей кажется ничем не примечательной. Но все становится гораздо интереснее, стоит лишь осознать, что к нему могут обращаться многие клиенты и доступ должен синхронизироваться. Короче говоря, это распределенная сущность; любой клиент может читать из пространства кортежей или писать в него, то есть его можно рассматривать как большое распределенное хранилище или даже способ коммуникации.
Первой реализацией пространства кортежей был проект Linda — исследование в области параллельного программирования, выполненное в Йельском университете в 1980-х годах. Реализация на языке Ruby (конечно, на основе библиотеки drb
), естественно, называется Rinda.
Кортеж в Rinda может быть массивом или хэшем. На хэш налагается дополнительное ограничение: все ключи должны быть строками. Вот несколько примеров простых кортежей:
t1 = [:add, 5, 9]
t2 = [:name, :add_service, Adder.new, nil]
t3 = { 'type' => 'add', 'value_1' => 5, 'value_2' => 9 }
Элемент кортежа может быть произвольным объектом; это работает, потому что drb
умеет выполнять маршалинг и демаршалинг объектов Ruby. (Конечно, необходимо либо включить модуль DRbUndumped
, либо сделать определения объектов доступными серверу.)
Пространство объектов создается методом new
:
require 'rinda/tuplespace'
ts = Rinda::TupleSpace.new