Программирование на языке Ruby - [227]
Это, конечно, не все, что можно сказать о библиотеке >drb
. Но для обзора вполне достаточно. В следующем разделе мы рассмотрим простой drb-сервер и drb-клиент, близкие к реальным программам. А затем поговорим о программах Rinda и Ring.
20.2. Пример: эмуляция биржевой ленты
В этом примере сервер публикует в сети биржевые котировки акций. К серверу может обратиться любой клиент, желающий узнать, сколько сейчас стоит его пакет.
Но мы добавили одну тонкость. Не желая следить за малейшими колебаниями цен, мы реализовали модуль >Observer
, который позволяет подписаться на информационный канал. Клиент следит за поступающими сведениями и предупреждает нас, когда изменение цены превысит заданный порог.
Сначала рассмотрим модуль >DrbObservable
. Это прямолинейная реализация паттерна >Observer
(Наблюдатель), описанного в замечательной книге Э. Гаммы, Р. Хелма, Р. Джонсона и Дж. Влиссидеса «Паттерны проектирования» (см. сноску в разделе 12.3.1). Еще этот паттерн называют «Издатель-Подписчик».
В листинге 20.1 наблюдатель определен как объект, отвечающий на вызов метода update. Сервер добавляет наблюдателей по их просьбе и посылает им уведомления, обращаясь к методу >notify_observers
.
>module DRbObservable
> def add_observer(observer)
> @observer_peers ||= []
> unless observer.respond_to? :update
> raise NameError, "наблюдатель должен отвечать на вызов 'update'"
> end
> @observer_peers.push observer
> end
> def delete_observer(observer)
> @observer_peers.delete observer if defined? @observer_peers
> end
> def notify_observers(*arg)
> return unless defined? @observer_peers
> for i in @observer_peers.dup
> begin
> i.update(*arg)
> rescue
> delete_observer(i)
> end
> end
> end
>end
Сервер (он же канал) в листинге 20.2 эмулирует биржевые котировки с помощью последовательности псевдослучайных чисел (простите мою иронию, но это очень точно соответствует характеру рынка). Символ, идентифицирующий компанию, — всего лишь косметическое украшение, никакого реального смысла в этой программе он не имеет. При каждом изменении цены посылается уведомление всем наблюдателям.
>require "drb"
>require "drb_pbserver"
># Генерировать случайные котировки.
>class MockPrice
> MIN = 75
> RANGE = 50
> def initialize(symbol)
> @price = RANGE / 2
> end
> def price
> @price += (rand() - 0.5)*RANGE
> if @price < 0
> @price = -@price
> elsif @price >= RANGE
> @price = 2*RANGE - @price
> end
> MIN + @price
> end
>end
>class Ticker # Периодически получать котировку акций.
> include DRbObservable
> def initialize(price_feed)
> @feed = price_feed
> Thread.new { run }
> end
> def run
> lastPrice = nil
> loop do
> price = @feed.price
> print "Текущая котировка: #{price}\n"
> 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}\n"
> end
> end
>end
>class WarnHigh < Warner
> def update(time, price) # Обратный вызов наблюдателя.
> if price > @limit
> print "+++ #{time.to_s}: Цена выше #@limit: #{price}\n"
> 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 году, а сама идея еще старше. Кортежем называется массив или вектор, состоящий из элементов данных (как строка в таблице базы данных).
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Сейчас во многих школах, институтах и других учебных заведениях можно встретить компьютеры старого парка, уже отслужившие свое как морально, так и физически. На таких компьютерах можно изучать разве что Dos, что далеко от реалий сегодняшнего дня. К тому же у большинства, как правило, жесткий диск уже в нерабочем состоянии. Но и выбросить жалко, а новых никто не дает. Различные спонсоры, меценаты, бывает, подарят компьютер (один) и радуются, как дети. Спасибо, конечно, большое, но проблемы, как вы понимаете, этот компьютер в общем не решает, даже наоборот, усугубляет, работать на старых уже как-то не хочется, теперь просто есть с чем сравнивать.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.