IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  SOA и Web-сервисы  >

Практическое использование Rails: Кэширование в Rails

Различные механизмы кэширования для создания приложений Rails

developerWorks
Опции документа

Опции документа, требующие включения JavaScript, не отображаются

Обсудить


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

Брюс Тэйт, президент, RapidRed

07.11.2007

Ruby on Rails все в большей степени становится базовым средством разработки современных масштабируемых приложений большого и среднего формата. Так как Ruby представляет собой интерпретируемый язык программирования, то для того, чтобы заставить Rails работать так, как нужно именно вам, придется использовать различные механизмы кэширования. В данной статье приведен обзор подобных механизмов, существующих в настоящий момент, в том числе и тех, которые используются на сайте ChangingThePresent.org.

О серии

В среде разработчиков Rails пользуется своеобразной репутацией. Вокруг него множество слухов и домыслов: некоторые говорят о его выдающейся производительности, другие – об обратном; кто-то отзывается о нем, как о сверхпродуктивном средстве, а кто-то - как о хорошо разрекламированной, а потому и хорошо продаваемой игрушке. Подобно большинству новейших технологий, Rails известен как непроверенный продукт с ограниченной масштабируемостью. В отличие от C и Java™, Ruby является интерпретируемым языком, унаследовавшим все функциональные недостатки подобных продуктов.

Не секрет, что многие из крупнейших сайтов Интернета используют интерпретируемые языки. В них используются те же самые технологии, что и в Ruby - кластеризованные, нераспределяемые архитектуры с поддержкой кэширования. Для достижения максимально возможной производительности большинству сайтов необходим эффективный механизм кэширования. На это ориентируются и разработчики Rails.

В серии Практическое использование Rails известный во всем мире автор и оратор Брюс Тэйт покажет реальную картину разработки Rails изнутри. Будучи главным техническим директором в WellGood, LLC, он отвечает за проектирование, разработку и поддержку ChangingThePresent.org - портала благотворительных пожертвований, где можно пожертвовать деньги на оплату часа работы в области исследований рака, на защиту акра тропического леса или проспонсировать операцию по устранению катаракты и вернуть зрение слепому. К настоящему времени сотни тысяч пользователей получили бесплатную помощь на ChangingThePresent, поэтому популярность и масштабы сайта постоянно растут.

Существуют десятки статей, описывающих процесс создания простых приложений Rails. Данная серия выведет вас за рамки основ построения простого блога и расскажет о вопросах, которые приходится решать каждому сайту, созданному на базе Rails. Вы узнаете, как оптимизировать Rails и сделать работу сайта более стабильной, как при помощи плагинов можно обойти основные ограничения Rails и что нужно сделать, чтобы сайты на основе Rails работали в реальной жизни.

Несколько сценариев

Для начала, давайте проанализируем несколько страниц ChangingThePresent.org и рассмотрим те элементы сайта, где возможно применение кэширования. Затем будет показан наш выбор для каждого из этих элементов и код или механизм, используемый для создания этих страниц. В частности, будет подробно рассказано, что именно делать с:

  • Полностью статичными страницами
  • Полностью динамическими страницами, которые редко изменяются
  • Фрагментами динамических страниц
  • Данными прикладных программ

Сначала обратим внимание на статичные страницы, присутствующие практически на каждом сайте. В качестве примера рассмотрим рисунок 1, на котором указаны наши положения и условия. На данную страницу можно перейти, если щелкнуть мышкой на register и затем - на user agreement. В ChangingThePresent с нее был убран весь динамический контент для того, чтобы позволить Apache поместить ее в кэш. Из-за правил, заданных в нашей конфигурации Apache, этот контент никогда не обрабатывается серверами Rails. Подобное Rails-кэширование не будет рассматриваться вовсе.


Рисунок 1. Соглашение об использовании
Figure 1

Теперь давайте рассмотрим полностью динамические страницы. Теоретически, в ChangingThePresent могли бы присутствовать страницы, которые создаются динамическими, но при этом редко изменяются. Так как практически все наши страницы отображают информацию о наличии регистрации пользователя, то данный тип кэширования нас не интересует.

Далее речь пойдет о процессе кэширования фрагмента страницы. Раньше наша домашняя страница, изображенная на рисунке 2, была полностью статичной. А теперь на ней присутствует несколько динамических элементов. Каждый день на странице отображается ряд подарков, которые могут быть выбраны как случайным образом, так и нашими администраторами. Обратите внимание на подарки в разделе "A Few of our Special Gifts for Mother's Day" ("Особые подарки ко Дню матери"). Также обратите внимание на ссылку в правой части окна с названием "login" (регистрация). Наличие данной ссылки зависит от того, вошел ли пользователь в систему или нет. Таким образом, мы не можем кэшировать страницу целиком. Страница изменяется только один раз в день.


Рисунок 2. Домашняя страница

И, наконец, рассмотрим приложение. Если вы использовали Интернет позже, чем 15 лет назад, то должны согласиться, что наиболее интересными являются динамические сайты. В современных приложениях есть уровни, и можно повысить их эффективность, если кэшировать пространство между ними. ChangingThePresent выполняет определенное кэширование на уровне базы данных. Далее мы подробно рассмотрим каждый из этих видов кэширования и обсудим то, что реализовано в ChangingThePresent.

Кэширование статического контента

Mongrel - это Web-сервер, написанный Зедом Шоу (Zed Shaw) и состоящий из 2500 строк на языках Ruby и C. Это маленький сервер, занимающий небольшой объём памяти, настроенный специально для Web-приложений Ruby, например, Rails, Nitro, Iowa и так далее. Mongrel работает на UNIX® и Linux™, а также на Win32. Чаще всего Mongrel работает за другим Web-сервером (например, Apache или Litespeed), выполняющим роль прокси, однако это вовсе не обязательно, так как Mongrel сам по себе является HTTP-сервером, и его можно использовать с любыми удобными инструментальными средствами HTTP.

О кэшировании статических данных можно сказать не так много. ChangingThePresent - благотворительный портал и поэтому должен взывать к чувствам пользователей. А это означает использование изображений и видеороликов. Однако Web-сервер Mongrel не очень хорошо обрабатывает статические данные, поэтому для обработки графической информации используется Apache.

Переходим к графическому ускорителю Panther Express, который помещает в кэш наиболее часто используемые изображения и тем самым делает их ближе к нашим пользователям. В процессе мы получаем поддомен images.changingThePresent.org. Panther Express обрабатывает все изображения непосредственно в их локальных кэшах и затем отправляет нам запросы. Так как службе Panther не известен момент изменения изображений, то окончание срока их актуальности указывается в HTTP-заголовках, например:


HTTP-заголовок с указанием окончания срока актуальности кэша
                
HTTP/1.1 200 OK
Cache-Control: max-age=86400, must-revalidate
Expires: Tues, 17 Apr 2007 11:43:51 GMT
Last-Modified: Mon, 16 Apr 2007 11:43:51 GMT

Обратите внимание на то, что это не HTML-заголовки. Они создаются независимо от контента Web-страницы. Web-сервер будет создавать такие HTTP-заголовки автоматически. Так как конфигурация Web-сервера не представляет никакого интереса для серии статей о Rails, перейдем к контенту кэша, которым управляет среда Rails (для получения более подробной информации по конфигурации Web-сервера смотрите раздел Ресурсы).

Кэширование страниц

Если есть динамические страницы, которые изменяются лишь изредка, то для работы с ними можно использовать страничное кэширование. Наглядным примером приложений такого вида являются блоги и электронные доски объявлений. Подкачка страницы в кэш позволяет Rails создать динамическую HTML-страницу и хранить ее в общей директории. Таким образом, сервер приложений может работать с ней так же, как и с любой другой статичной страницей.

Так как Rails никогда не фиксирует рисунок при кэшировании страницы, кэширование страниц является самым быстрым кэшированием в Rails. На самом базовом уровне реализовать это в Rails очень легко. Кэширование как страниц, так и их фрагментов выполняется на уровне контроллера. Rails всего лишь необходимо указать:

  • Какие страницы необходимо кэшировать?
  • Каким образом будет отслеживаться окончание срока актуальности страницы при изменениях контента страницы?

Кэширование страниц включается с помощью директивы caches_page в классе контроллера. Например, чтобы поместить в кэш страницы privacy_policy и user_agreement в about_us_controller, необходимо набрать следующий код:


Листинг 2. Включение кэширования страниц
                
class AboutController < ApplicationController
  caches_page :privacy_policy, :user_agreement 
end

Указать окончание срока актуальности страниц можно с помощью директивы expire_page. Для того чтобы задать в качестве окончания срока актуальности вышеупомянутых страниц инициацию Rails-действия new_pages можно использовать следующий код:


Листинг 3. Окончание срока актуальности страниц
                
class AboutController < ApplicationController
  caches_page :privacy_policy, :user_agreement 
  
  def new_pages
    expire_page :action => :privacy_policy
    expire_page :action => :user_agreement
  end
  
end

Также необходимо помнить и о таких второстепенных вопросах, как URL-адреса. URL-адреса не могут зависеть от URL-параметров. Например, вместо gifts/water?page=1 необходимо использовать gifts/water/1. Такие URL-адреса можно использовать в routes.rb. Например, зачастую в наших страницах используется параметр вкладки, который указывает, какая вкладка выбрана в данный момент. Для того чтобы сделать вкладку частью URL-адреса используется следующее правило маршрутизации:


Листинг 4. Правила маршрутизации для вкладок
                
map.connect 'member/:id/:tab', :controller => 'profiles', :action => 'show'

То же самое необходимо делать и для списков с параметрами страниц и всех остальных страниц, которые зависят от параметров URL. Также необходимо помнить и о защите.

Поскольку среда Rails не используется в том случае, если страница находится в кэше, то сервер не может управлять защитой вместо вас. Web-сервер предоставит любую страницу из кэша, независимо от того, есть ли у пользователя права на ее просмотр или нет. Следовательно, кэширование страниц не следует использовать в том случае, если вопрос о доступе к просмотру страницы является принципиальным.

Вот, пожалуй, и все, что можно сказать о кэшировании простых статических страниц. Поскольку основное содержание операции элементарно, то его легко реализовать.

Трудности возникают при попытке кэшировать более сложный контент. При кэшировании страниц с большим динамическим наполнением логика окончания срока актуальности будет становиться все более и более запутанной. Для работы со сложными политиками завершения срока актуальности вам придется писать и настраивать специальных чистильщиков (custom sweepers). Данные классы удаляют выбранные элементы из кэша при запуске конкретного контроллера.

Большинство специальных чистильщиков следят за каким-либо объектом модели и на основе изменений активизируют логику для прекращения срока актуальности одной или нескольких кэшированных страниц. В листинге 5 показан стандартный чистильщик кэша. В чистильщике разработчик может определить событие активной записи, например, after_save. Когда произойдет данное событие, чистильщик будет запущен и сможет аннулировать указанные страницы в кэше. В данном примере показано объявление данных недействительными на основе метода expire_page. Вместо этого большинство серьезных приложений напрямую используют первоклассные Ruby-утилиты файловой системы для явного удаления кэшированных страниц.


Листинг 5. Стандартный алгоритм наблюдения
                
class CauseController < ApplicationController
   cache_sweeper :cause_sweeper
...

class CauseSweeper < ActionController::Caching::Sweeper
  observe Cause
    
  def after_save(record)
    expire_page(:controller => 'causes', :action => 'show', 
               :id => record.id)
    cause.nonprofits.each do |nonprofit|
     expire_page(:controller => 'nonprofits', :action => 'show', 
                  :id => nonprofit.id)
     end
   end
end

Вероятно, вы уже начали осознавать всю сложность процесса кэширования страниц. Можно идеально реализовать кэширование на уровне отдельной страницы, однако присущие этому сложности приведут к возникновению проблем при тестировании приложения и возникновению ошибок в системе. Кроме того, если разным пользователям предоставляются разные страницы, или необходимо кэшировать страницы входа в систему, то придется заглянуть за пределы кэширования страниц. В ChangingThePresent нам приходится сталкиваться с обоими вариантами, так как ссылки на основном макете меняются в зависимости от того, вошел пользователь в систему или нет. Для большинства страниц бессмысленно даже думать о кэшировании на страничном уровне. В разделе Ресурсы даны ссылки на несколько превосходных статей по кэшированию на страничном уровне. Далее мы перейдем к кэшированию действий, которое является еще одной формой кэширования целой страницы.

Кэширование действий

Теперь вам известны основные достоинства и недостатки кэширования страниц: для большинства извлечений страницы Rails никогда не фиксирует изображение. Основным преимуществом является скорость, а недостатком - гибкость. Если необходимо кэшировать целые страницы на основе условий в приложении, например, проверки прав доступа, то можно использовать кэширование действий.

Процесс этот работает аналогично процессу кэширования страниц, однако порядок выполнения действий будет другим. На самом деле Rails будет активизировать контроллер перед выполнением действия. Если предоставляемая действием страница уже находится в кэше, то Rails отображает страницу из кэша, а не генерирует ее заново. Так как теперь Rails работает с изображением, то общая производительность, по сравнению с процессом кэширования страниц, несколько падает; впрочем, здесь есть и положительная сторона. Практически все схемы входа в систему Rails применяются до фильтров на контроллере. Кэширование действий позволяет использовать преимущества аутентификации и любых фильтров контроллера.

Синтаксически кэширование действий работает точно так же, как и кэширование страниц, но при этом используется другая команда. В листинге 6 показано как использовать команду caches_action.


Листинг 6. Активация кэширования действий
                
    class AboutController < ApplicationController
      caches_action :secret_page, :secret_list 
    end

Окончание срока актуальности кэша и чистильщики работают аналогичным образом. Мы не используем кэширование действий по тем же самым причинам, что и кэширование страниц. Однако кэширование фрагментов представляет собой гораздо больший интерес для нас.

Кэширование фрагментов страницы

Используя частичное кэширование, можно кэшировать часть страницы, чаще всего - контент для макета. При кэшировании фрагментов разработчик указывает фрагмент, который подлежит кэшированию, заключая его в rhtml-директивы непосредственно на Web-странице, как показано в листинге 7. На ChangingThePresent.org первая и еще несколько страниц кэшируются на основе кэширования фрагментов. Все эти страницы очень часто обращаются к базе данных и являются самыми популярными на нашем сайте.


Листинг 7. Идентификация фрагментов кэширования
                
<% cache 'gifts_index' do %>
    <h3>
      Here, you can make the world a better place with a single gift. Donation gifts 
      are also a wonderful way to honor friends and family. Just imagine what we
      can achieve together.
    </h3>
    <h2 class="lightBlue"><%= @event_title %></h2>
    <div id="homefeatureitems">
        <% for gift in @event_gifts %>
          <%= render :partial => 'gifts/listable', :locals => { :gift => gift } %>
        <% end %>
    </div>
    ...
<% end %>

В листинге 7 cache helper указывает на фрагмент, который необходимо поместить в кэш. Первый параметр - это уникальное имя, определяющее фрагмент для кэширования. Во втором параметре содержится кодовый блок — весь код между первым do и последним end —, который точно указывает, какой RHTML-фрагмент необходимо кэшировать.

У нашего сайта - одна домашняя страница, поэтому называть страницы легко. В остальных случаях будет использоваться метод, определяющий URL страницы, чтобы однозначно указать фрагмент для кэширования. Например, для того, чтобы поместить в кэш код для категорий "мир во всем мире" или "борьба с нищетой" можно использовать код, показанный в листинге 8. Данный код находит постоянный url, который называется permalink (постоянная ссылка на категорию).


Листинг 8. Определение фрагмента для кэширования по URL
                
<% cache @cause.permalink(params[:id]) do %>

Обычно при кэшировании отдельных страниц необходимо удалять их по истечению срока актуальности с помощью чистильщиков. Иногда проще и доступнее использовать простое окончание срока актуальности объектов на основе временных рамок. По умолчанию в Rails нет такого механизма, однако эта функция реализована в плагине timed_fragment_cache. С помощью этого плагина можно указать тайм-аут либо в кэшированном контенте, либо в коде контроллера, который предоставляет динамическую информацию для страницы. Например, в листинге 9 показан код, который генерирует динамические данные для страницы со списком категорий. Метод when_fragment_expired будет выполняться только по истечению срока актуальности фрагмента. В данном методе используется параметр, указывающий временной интервал тайм-аута, и кодовый блок, описывающий какой контент необходимо сгенерировать заново по истечению срока актуальности контента. Также тайм-аут можно было указать и на rhtml-странице вместе с методом кэширования, однако предпочтение было отдано методу контроллера.


Листинг 9. Окончание срока актуальности кэша на основе временных характеристик
                
def index
  when_fragment_expired 'causes_list', 15.minutes.from_now do 
    @causes = Cause.find_all_ordered
  end
end

Использование технологии тайм-аута позволяет существенно упростить механизм кэширования, если можно допустить наличие немного устаревшей информации. Для каждого кэшируемого элемента необходимо указать контент, который надо помещать в кэш, все действия контроллера, генерирующего динамический контент, и тайм-аут. Так же как и при кэшировании страниц, следует точно указывать срок окончания актуальности контента с помощью метода expire_fragment :controller => controller, :action => action, :id => id. Данный метод работает точно так же, как и окончание срока актуальности кэшированных действий страниц. А теперь, давайте посмотрим, как сконфигурировать серверную часть.

Memcached

До сих пор мы обсуждали модели кэширования фрагментов и страниц в Ruby on Rails. Теперь, когда настало время определить, куда же будут помещаться кэшированные данные, давайте рассмотрим API. По умолчанию, Rails поместит кэшированные страницы в файловую систему. И кэшированные страницы, и кэшированные действия будут отправлены в общедоступную директорию. Для кэшированных фрагментов можно задать место хранения. Для этого можно использовать запоминающее устройство, файловую систему (в указанной директории), базу данных или службу memcached. На ChangingThePresent.org используется memcached.

Представьте себе Memcached как огромную хэш-карту, к которой можно получить доступ через сеть. Кэширование на основе памяти характеризуется высокой скоростью, а сетевые кэши являются масштабируемыми. При поддержке плагина Rails использует memcached для кэширования фрагментов и моделей ActiveRecord. Для использования необходимо установить memcached (для получения более подробной информации смотрите раздел Ресурсы) и настроить его в environment.rb (или одном из файлов конфигурации среды, например, production.rb.)


Листинг 10. Настройка кэширования
                
config.action_controller.perform_caching = true

memcache_options = {
  :c_threshold => 10_000,
  :compression => false,
  :debug => false,
  :readonly => false,
  :urlencode => false,
  :ttl => 300,
  :namespace => 'igprod',
  :disabled => false
}

CACHE = MemCache.new memcache_options


В листинге 10 показана стандартная конфигурация. Первая строка, config.action_controller.perform_caching = true, включает кэширование. Следующая строка подготавливает опции кэширования. Обратите внимание, что большой набор опций позволяет получить больше отладочной информации и определить область имен кэш-памяти. Более подробную информацию по опциям настройки можно найти на сайте, посвященном memcached, который указан в разделе Ресурсы.

Кэширование моделей

Последней формой кэширования, которую мы используем, является кэширование на основе модели. Мы используем специализированный плагин кэширования CachedModel. Кэширование модели представляет собой ограниченную форму кэша базы данных. Кэш легко активировать на основе модели.

Чтобы модель использовала решение кэширования, нужно просто расширить класс CachedModel, а не ActiveRecord, как показано в листинге 11. CachedModel расширяет ActiveRecord::Base. ActiveRecord - это не полный объектно-реляционный уровень отображения. При реализации сложных объектов среда в значительной степени основывается на SQL, и при желании пользователь может легко опуститься до уровня SQL. Прямое использование SQL затрудняет кэширование, так как кэширующий уровень должен иметь дело с полными результирующими наборами, а не отдельными строками базы данных. Обработку полных результирующих наборов можно назвать, по меньшей мере, затруднительной. Ее практически невозможно выполнить без глубинной логики вспомогательного приложения. По этой причине CachedModel фокусирует свое внимание на кэшировании отдельных объектов модели и ускоряет выполнение только тех запросов, которые возвращают одну строку.


Листинг 11. Использование CachedModel
                
Class Cause < CachedModel

Большинство приложений Rails повторно обращаются к одним и тем же элементам, например, к объектам пользователя. При таких обстоятельствах кэширование модели может действительно ускорить решение задачи. В ChangingThePresent мы только начинаем расширять использование кэширования на основе модели.

Заключение

Несмотря на то, что Ruby является крайне производительным языком, с точки зрения производительности интерпретируемая природа языка оказывает ему медвежью услугу. Большинство крупных Rails-приложений могут смягчить негативный эффект благодаря эффективному использованию кэширования. На ChangingThePresent.org в основном используется кэширование фрагментов, а кэшированные фрагменты объявляются недействительными главным образом на основе временных рамок. Данный подход отвечает всем нашим нуждам, даже при условии, что есть страницы, контент которых изменяется в зависимости от того, вошел ли пользователь в систему или нет.

Более того, мы изучаем эффект от использования memcached-backed-классов CachedModel. В настоящее время мы только начали изучать эффективность такого кэширования с точки зрения производительности базы данных, но первые результаты оказались многообещающими. В следующей статье в качестве примера применения Rails в реальных условиях будет рассказано о некоторых приемах, которые используются для оптимизации баз данных.



Ресурсы

Научиться
  • Оригинал статьи: Real world Rails: Caching in Rails. (EN)

  • От Java к Ruby: что должен знать ваш менеджер (Pragmatic Bookshelf, 2006): Книга автора о том, где и когда имеет смысл перейти от языка программирования Java к Ruby on Rails и как это сделать. (EN)

  • Changing The Present: благотворительный сайт, на котором можно отдать в дар акр тропического леса, помочь вернуть зрение слепому или пожертвовать час времени на исследование рака. Этот сайт положен в основу данной серии статей. (EN)

  • Работа с Ruby on Rails и Узнайте все о Ruby on Rails: дополнительные материалы о Ruby и Rails, в том числе процедура установки. (EN)

  • Active Record: Active Record - устойчивая среда для Ruby on Rails. (EN)

  • Кэширование в Rails: обзор Роберта Эванса (Robert Evans), посвященный моделям кэширования в Rails и содержащий анализ процессов кэширования страниц, действий и фрагментов. (EN)

  • Руководство по кэшированию в среде Ruby on Rails: превосходное руководство по кэшированию Грега Поллака (Gregg Pollack), в котором описано кэширование страниц и представлен хороший обзор механизмов очистки кэша. (EN)


Получить продукты и технологии
  • Ruby on Rails: загрузите Web-среду Ruby on Rails с открытым исходным кодом. (EN)

  • Mongrel: высокопроизводительный сервер приложений, на котором выполняется большинство лучших Rails-сайтов. Mongrel используется и на ChangingThePresent.org. (EN)

  • Web-сервер Apache: Web-сервер, который используется большинством Rails-сайтов для обслуживания статического контента, в том числе кэшированного контента. (EN)

  • Panther Express: графический акселератор, который будет использоваться на ChangingThePresent.org для кэширования графического контента. (EN)

  • Плагин окончания срока актуальности содержимого кэша на основе времени: плагин Ричарда Ливсея (Richard Livsey) для управления окончанием срока актуальности кэшированных фрагментов. На сайте ChangingThePresent.org данный плагин используется для кэширования домашней страницы и других крупных фрагментов. (EN)

  • Memcached: Сетевая служба, обеспечивающая кэширование распределенных объектов. Memcached выступает в роли серверной составляющей для служб кэширования ChangingThePresent. (EN)

  • CachedModel: Служба кэширования memcached-backed, которая выполняет функцию резервирования для объектов ActiveRecord. CachedModel ускоряет только те запросы к базе данных, которые возвращают результаты в виде одной строки. (EN)

Обсудить


Об авторе

Брюс Тэйт (Bruce Tate) является отцом, горным байкером и байдарочником, проживающим в Austin, Texas. Он автор трех бестселлеров по языку Java, в том числе, победителя Jolt "Лучше, быстрее, легче Java". Недавно издал "За пределами Java"… Он работал 13 лет в IBM, а сейчас является основателем RapidRed consultancy, где специализируется на стратегиях и архитектурах облегченной разработки, основанных на технологии Java и Ruby on Rails.




Выскажите мнение об этой странице


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



ДаНетНе знаю
 


 


12345
 


В начало


IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.

    IBM в России Конфиденциальность Контакты