четверг, 31 июля 2014 г.

1 parse парсирование
2 план
3 выполнение
4 фетч фетчинг данных


not may full scan table on disk
-- table access full
-- load in buffer cache only in this area oracle may scan a table

перенести данные из кеша буферов в область сортировки
область сортировки нужна чтобы сортировать результат

при нехватке  памяти выделяется области в тебл спейсе и флушится на диск
'show sga ' in sqlplus

библиотечный кеш


select * from v$sql
-- просмотр библиотечного кеша
вычистка мусора из бибилиотечного кеша и одинаковых запросов в разных регистрах
LRU при заполнении кеша удаляются самое старое запросы
95 % процентов попадания в кеш
то есть означает что парсинг только 2 запросов из 10 то есть остальные 8 известны и берутся из кеша
размер shared pool

 процент кеш промах \ кеш попаданий
избежать можно использовав опцию cursor sharing
1 ) exact -- тут все как есть так и парситься
2 ) similar -- данный режим будет заменять лайки на параметры в приложении where и в инсерте values будет менятся на параметры  -- тратит время на сборку параметров  
-- на базах разработчиков не ставить


Как установить Oracle Database 11g Release 2 RAC на Linux используя VirtualBox – первый шаг к кластеризации

Часть 1. Как установить Oracle Database 11g Release 2 RAC на Linux используя VirtualBox – первый шаг к кластеризации из песочницы tutorial

RAC на ноуте — мне показалось это интересной темой для экспериментов.
Может не одного меня это заинтересует.
Присылайте поправки и исправления в личку.

Ниже представлен живой пример, того, как получить полноценное Oracle Enterprise решение (Oracle Real Application Clusters) на обычном компьютере.
Это Часть 1 перевода, на мой взгляд очень полезной, статьи в которой очень подробно рассматривается этот вопрос (на мой взгляд, даже чересчур подробно).

Официальный источник: lab128.com
Спасибо автору за проделанную работу.
Oracle кластер на ноуте, это действительно круто!

Часть 2 (установка необходимых пакетов, настройка сети, общие папки)

Введение


Если вы хотите пройти через все шаги установки Oracle RAC и ваш ноутбук или настольный компьютер имеет 8 ГБ или больше оперативной памяти, то это вполне осуществимо с помощью Oracle VirtualBox. Следуйте инструкциям в этой статье и вы сможете получить работающую систему RAC, в которой вы может разместить небольшую тестовую базу данных. Созданная система не является и не должно рассматриваться как готовая система. Это руководство, просто позволит вам привыкнуть к установке и использованию RAC и тестированию различных процедур администрирования. В статье также объясняется, как сохранить образ и восстановить RAC из образа в считанные минуты. Даже если у вас «упадет» тестовой система, то её будет легко восстановить.

В этой статье использует 64-разрядные версии Oracle Linux, версия 6.3 и Oracle 11g Release 2, версии 11.2.0.3. С помощью VirtualBox можно запускать несколько виртуальных машин (VM) на одном сервере, который позволят запускать несколько RAC узлов на одной машине. Кроме того вы сможете использовать общие виртуальных диски. Готовая система включает в себя два гостевых операционных систем, два комплекта Oracle Grid инфраструктуры (Clusterware + ASM) и два экземпляра базы данных на одном сервере. Объем дискового пространства требуется около 32 ГБ, если вы хотите сохранить образы готовой RAC, то вам буде необходимо еще 12 ГБ дискового пространства.

Эта статья была написана, после того, как я был первоначально вдохновлен статей ««Oracle Database 11g Release 2 RAC On Linux Using VirtualBox» которая была написана Тим Холл и, которуб он опубликовал в своем блоге. Затем была почти полностью пересмотрена и переработана, теперь эти стати, как мне кажется, имеют очень мало общего.

Системные требования


  • 8 Гб оперативной памяти;
  • 32 Гб свободного пространства на жестком диске;

Все, описанное в этой статье былы протестировано на 64-битной Windows 7. Хотя не должно быть никаких проблем с использованием VirtualBox для других ОС и аппаратных решениях. Пожалуйста, дайте мне знать, если у вас успехи или проблемы в других ОС;

Необходимое ПО


Скачайте следующее ПО:
  1. Oracle Linux
  2. VirtualBox (должен быть версии 4.1.14 или старше)
  3. Oracle 11g Release 2 (11.2) Software (64 bit)


Установка виртуальной машины (VM)


В этом примере мы используем VirtualBox установленый на 64-битной Windows 7.
Теперь мы должны определить два виртуальных RAC узла. Мы можем сэкономить время, определяя одну виртуальную машину, и когда все будет установлено мы ее клонируем
Запустите VirtualBox и нажмите кнопку „Create new“ на панели инструментов. Нажмите на кнопку «Next» на первой странице мастера виртуальной машины.
Введите имя „Rac1“, ОС „Linux“ и версия „Oracle (64 бит)“, а затем нажмите кнопку „Next/Далее“:
image

Если у вас есть 16 Гб оперативной памяти, то выделите памяти до 3072 Мб, в противном случае исползуйте 2048 Мб, как на скриншоте ниже, затем нажмите кнопку „Next“:
image

Примите выбор по умолчанию, чтобы создать новый виртуальный жесткий диск, нажав на кнопку „Далее“:
image

Примите по умолчанию тип VDI и нажмите кнопку „Далее“ в Мастер создания виртуальных дисков:
image

Примите значение по умолчанию „Dynamically allocated“ вариант, нажав на кнопку „Далее“:
image

Примите по умолчанию и установите размер „16GB“ и нажмите кнопку „Далее“:
image

Нажмите кнопку „Create“ на экране «Создать новый виртуальный диск»:
image

Нажмите кнопку „Create“ на экране «Создать новую виртуальную машину»:
image

»Rac1" VM появится в левой панели.
Нажмите на кнопку «Network» ссылку с правой стороны (подсвечена синим):
image

Убедитесь, что «Adapter 1» включен, и attached to «Bridged Adapter»
image



Затем нажмите на кнопку «Adapter 2» во вкладках. Убедитесь, что «Adapter 2» включен и прикреплен к «Внутренняя сеть/Internal Network ». Затем нажмите кнопку «ОК»:
image

При желании можно отключить звуковую карту, используя «Audio» ссылку. Это, вероятно, позволит сохранить некоторое количество дискового пространства и/или памяти и избежать возможных проблем, связанных с аудио-настройками. Также, если ваша система имеет 4 ядра процессора или больше, то будет иметь смысл выделить 2 процессоров для виртуальной машины. Вы можете сделать это в меню настроек «System».

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

Установка гостевой операционной системы



Обратите внимание, что во время установки Virtual Box будет держать указатель мыши внутри области VM. Чтобы перехватить управление мышью, нажмите правую клавишу “Ctrl” на клавиатуре.
Поместите DVD Oracle Linux 6.3 (или новее) в привод DVD и пропустить следующие два скриншота.
Если у вас нет DVD, то необходимо скачать. ISO-образ и поместить его в виртуальный DVD. Выберите «Storage» ссылка на правой панели экрана VirtualBox Manager, чтобы открыть “Storage” экраном. Затем выберите DVD диск в «Storage Tree» раздела:
image

В разделе «Attributes» нажмите на значке диска DVD и выберите файл .ISO файл. Обратите внимание, что имя файла должно быть видно в Storage Tree. Нажмите кнопку «ОК»:
image

Запустите виртуальную машину, нажав на кнопку «Start» на панели инструментов. В результате окно консоли будет содержать окно загрузки Oracle Linux. Выберите «Install or upgrade an existing system»:
image

Не проводите испытания средств массовой информации. Выберите кнопку «Skip»:
image

Продолжите установку Oracle Linux, как для обычного сервера. На следующих трех экранах необходимо выбрать язык, клавиатуру и основное устройство хранения данных.
Установите «Hostname» в “rac1.localdomain” и нажмите кнопку «Configure Network»:
image

В окне сетевых подключений выберите «System eth0» интерфейс и нажмите кнопку «Edit»:
image

Установите флажок «Connect automatically». Выберите «IPv4 Settings» на вкладке убедитесь, что Method имеет значение «Automatic (DHCP)». Выберите «IPv6 Settings», убедитесь, что Method имеет значение «Ignore». Нажмите кнопку «Apply»:
image

Закройте окно сетевых подключений и перейдите к следующему экрану установки. Выберите часовой пояс; Введите пароль пользователя root: oracle
Выберите «Use All Space» тип установки и установите флажок «Review and modify partitioning layout»:
image

Изменить размер lv_swap устройств до 1500 Мб; затем изменить размер lv_root на 14380 Мб. Нажмите «Далее»:
image

Подтвердить все предупреждения и создания разделов. Оставайтесь в экране загрузчика.
В экране установки программного обеспечения типа выберите «Database Server» и установите флажок «Customize now». Нажмите Next:
image

В настройки экрана выберете Database и снимите отметки со всех пунктов, выберите пункт Desktops и установите флажок «Desktop» и " Graphical Administration Tools", а затем нажмите кнопку Next и окончите установку. Reboot.

Когда виртуальная машина перезагрузится, то будет доступно больше экранов настройки. Не создавать учетную запись «oracle», это мы сделаем позже.
Поздравляем! Linux был установлен.

Проверьте доступ в Интернет



Нам понадобится доступ в Интернет, потому что дополнительные пакеты будут установлены из Интернета. Откройте терминал и попробуйте пропинговать любой сайт в Интернете, например:

ping ya.ru

Если пинг не проходит, устранить проблему с помощью команды «Ifconfig» запустите ее и внесите изменений в Network Connections (Linux desktop Main menu | System | Preferences | Network Connections).
Если вы внесли изменения в сетевых подключений, перезагрузите виртуальную машину или интерфейс.
Для перезапуска интерфейса используете эти две команды:

# ifdown eth0
# ifup eth0

Затем проверьте пинг снова.

Часть 2. Как установить Oracle Database 11g Release 2 RAC на Linux используя VirtualBox – поднимаем кластер tutorial

Все идет к тому, что я все таки запущу Oracle Real Application Clusters у себя на ноуте.

Я продолжаю мучатся и ставить на него абсолютно не подъемное Enterprise решение — RAC.
Так же у меня появилось желание проверить отказоустойчивость данного решения под нагрузкой.
После того, как все это заработает конечно…
После окончания установки, я планирую отключить работающий под нагрузкой Oracle node — буду использовать популярный в быту метод «power off».

А так как мой хороший друг в Oracle, как раз занимается разработкой системы восстановления и сохранения данных в случае падения, то я ожидаю, что на следующей неделе у нас с ним состоится серьезный разговор…

Часть 1 (первый шаг к кластеризации)

Часть 2 перевода, на мой взгляд, уже не чересчур подробной статьи.

Официальный источник: lab128.com
Спасибо автору за проделанную работу.

P.S.Посмотрим, запустится ли все это?

Часть 1
Oracle Clusterware установка.
Установка необходимых пакетов.


Все действия в данном разделе должны выполняться суперпользователем.

Запуск автоматической настройки, установит пакет «oracle-rdbms-server-11gR2-preinstall» Этот пакет выполнит все необходимые установки, включая ​​изменение ядра, необходимых параметров и создаст учетную запись Linux Oracle:

# yum install oracle-rdbms-server-11gR2-preinstall

Примечание.
Возможно, вы не сможете скопировать и вставить эту команду. Так что вам придется ввести её вручную. Мы собираемся исправить, путем установки Дополнения гостевой ОС. На данный момент просто введите эти команды в ручную.


Инсталлируем ASMLib:

# yum install oracleasm
# yum install oracleasm-support

Настройте ASMLib, выполнив эту команду и ответьте на вопросы как в примере ниже:

# oracleasm configure -i
Configuring the Oracle ASM library driver.

This will configure the on-boot properties of the Oracle ASM library driver. The following questions will determine whether the driver is loaded on boot and what permissions it will have. The current values will be shown in brackets ('[]'). Hitting <ENTER> without typing an answer will keep that current value. Ctrl-C will abort.

Default user to own the driver interface []: oracle
Default group to own the driver interface []: oinstall
Start Oracle ASM library driver on boot (y/n) [n]: y
Scan for Oracle ASM disks on boot (y/n) [y]: 
Writing Oracle ASM library driver configuration: done
#

Установить дополнения для гостевой ОС


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

Для того чтобы установить Дополнения гостевой ОС, перезагрузите только что созданную VM, войдите как root. Затем в окне меню выберите Devices | Install Guest Additions. Пройдите через загрузку до появления DVD Autorun экрана:
image

Нажмите «OK», затем «Run», чтобы начать установку.

Примечание.
Установка может жаловаться на отсутствие «kernel-uek-develт» за который отвечает программа «yum». Выполните эту команду. Вот почему нам необходим доступ в Интернет. Также установите другой пакет: «yum install gcc». Затем снова Guest Additions, дважды щелкнув на VBOXADDITIONS DVD значок на рабочем столе и нажать «Open Autorun Prompt» кнопку.


Перезагрузите машину.
Теперь вы должны быть гораздо счастливее от работы с VirtualBox!

Часть 2
Oracle Clusterware установка.
Установка необходимых пакетов.



Создайте директорию, в которую будет установлено программное обеспечение Oracle.
mkdir -p /u01 chown -R oracle:oinstall /u01 chmod -R 775 /u01/

Добавить Oracle учетную запись в dba и vboxsf группу. Группа vboxsf был создана гостевым расширением VirtualBox и позволит Oracle пользователям иметь доступ в гостевую ОС-хост:
# usermod -G dba,vboxsf oracle

Сбросьте пароль пользователя Oracle в «oracle»:
# passwd oracle
Changing password for user oracle.
New password: 
BAD PASSWORD: it is based on a dictionary word
BAD PASSWORD: is too simple
Retype new password: 
passwd: all authentication tokens updated successfully.


Отключить Secure Linux путем редактирования "/etc/selinux/config" файла, убедитесь, что SELINUX флаг установлен следующим образом:
SELINUX=disabled

Либо настройте NTP, или убедитесь, что она она отключена. Таким образом, время Oracle Cluster Service (ctssd) может синхронизировать со временем RAC узла. В этом случае нужно отключить NTP.
# service ntpd stop
Shutting down ntpd: [FAILED]
# chkconfig ntpd off
# mv /etc/ntp.conf /etc/ntp.conf.orig
# rm /var/run/ntpd.pid

Очистите YAM репозитарий:
# yum clean all

Проверьте использование дискового пространства, сейчас используется около 2.8 GB:
# df -k
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/vg_rac1-lv_root
14493616 2865472 10891888 21% /
tmpfs 1027556 272 1027284 1% /dev/shm
/dev/sda1 495844 77056 393188 17% /boot


Настройка сети


Все действия в данном разделе должны выполняться суперпользователем.

Ниже TCP приведен пример адресов, используемых в государственных и частных сетях. Если вам нужно использовать другой адрес, внесите соответствующие коррективы и помните, что все должно остаться согласованным с всеми изменениями в остальной части статьи.
Пожалуйста, обратите внимание, что подсеть 192.168.56.0 является конфигурацией по умолчанию и используется VirtualBox в качестве только принимающей сети, соединяющей ОС-хост и виртуальные машины.
В VirtualBox также работает DHCP-сервер в этой подсети, резервируя диапазон адресов 100-254. Так что остались свободными для использования адреса ниже 100 для статических IP.
Вы можете проверить эти настройки: Main menu | File | Preferences | Network, а затем проверить свойства Host-only сети. Мы используем эту подсеть для RAC общедоступной сети.

Измените файл "/etc/hosts", добавив следующую информацию:
# Private
192.168.10.1 rac1-priv.localdomain rac1-priv
192.168.10.2 rac2-priv.localdomain rac2-priv

# Public
192.168.56.71 rac1.localdomain rac1
192.168.56.72 rac2.localdomain rac2

# Virtual
192.168.56.81 rac1-vip.localdomain rac1-vip
192.168.56.82 rac2-vip.localdomain rac2-vip

# SCAN
192.168.56.91 rac-scan.localdomain rac-scan
192.168.56.92 rac-scan.localdomain rac-scan
192.168.56.93 rac-scan.localdomain rac-scan

Примечание.
SCAN адреса не должны быть определены в файле hosts. Вместо этого должна быть определена DNS зона, около 3 адресов в той же подсети, что и общие IP-адреса. Для этой установки, мы пойдем на компромисс и используем hosts файл. Если вы используете DNS, то закомментируйте линии SCAN-адресов.


Откройте инструмент Network Connections: Linux desktop  Main menu | System  | Preferences | Network Connections. Выберите «System eth0» интерфейс, который будет использоваться для общественных сетей, и нажмите кнопку «Edit»:
image

Убедитесь, что «Connect automatically» выбрано. В «IPv6 Settings», убедитесь, что Method имеет значение «Ignore». Выберите «Settings IPv4» на вкладке, изменение Method «Manual», нажмите кнопку «Add» и заполнить Address: 192.168.56.71, Netmask: 255.255.255.0, Gateway: 0.0.0.0. Нажмите кнопку «Apply», вот и все:
image

В окне Network Connections выберите пункт «System eth1» интерфейс, он будет использован для частной сети, нажмите кнопку «Edit». Затем установите флажок «Connect automatically». В «IPv6 Settings», убедитесь, что метод имеет значение «Ignore». Выберите «Settings IPv4» на вкладке, изменение Method  «Manual», нажмите кнопку «Add» и заполнить Address: 192.168.10.1, Netmask: 255.255.255.0, Gateway: 0.0.0.0. Нажмите кнопку «Apply», вот и все:
image

Отключить брандмауэр: Linux Main menu | System | Administration | Firewall. Нажмите на иконку «Disable», а затем «Apply»
image

Загружаем файлы Oracle установки


Есть два варианта как установить Oracle:
1. Загрузка или копирование файлов в VM и распаковка их в VM;
2. Загрузка и распаковка в ОС-хост, затем сделать папки доступными для VM файловой системы;

Очевидно, что второй вариант намного лучше, потому что он не использует виртуальный диск гостевой VM и приведет к меньшему финальному образу диска. Также установочные файлы могут быть легко использован в другой установке. В этом разделе мы собираемся установить VirtualBox Shared Folders.

Предполагается, что вы уже скачали файлы Oracle установки, и для разархивирования их в «grid» и «database» папки. В нашем примере эти папки находятся в «C:\TEMP\oracle_sw» папке.
C:\TEMP\oracle_sw>dir -l
total 0
drwx------+ 1 Domain Users 0 Aug 5 18:10 database
drwx------+ 1 Domain Users 0 Aug 5 03:08 grid

Завершите работу виртуальной машины. В диспетчере VirtualBox нажмите на кнопку «Shared Folders» ссылку в правой панели. Добавьте общую папку, нажав значок «plus». Затем выберите путь к папке программного обеспечения Oracle, и установите оба флажка «Read-only» и «Auto-mount»:
image

Нажмите «OK», чтобы сохранить настройки. Сейчас Shared Folders должна выглядеть следующим образом:
image

Перезагрузите виртуальную машину и войдите в систему как пользователь Oracle . Перейдите в каталог "/media/sf_oracle_sw" — это папка в которой VirtualBox хранит информацию об общих папках ОС хоста. Обратите внимание, что VirtualBox добавляет ​​приставка «sf_» к именам папок. «ls» Список содержит папки:
$ cd /media/sf_oracle_sw
$ ls
database grid
$

восстановление контрол файлов и работа с ними

делать несколько копий контрол файлов


четверг, 24 июля 2014 г.

быстрый способ загрузки хранилища данных

от одной таблицы откусить секцию и прилепить к другой - быстрое
файловая группа одна если

перенести старые заказы
таблица с одинаковой структурой и пустой
либо секционированной так же

таблица фактов
время загрузки новых данных
грузим данные во временную таблицу
а потом подключаем \ меняем партицию

проблема проста: четвертая часть времени всей загрузки - подянтие FK (это у одного из клиентов)

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





[24.07.2014 11:37:01] Рабащенко Иван: поднять в NOVALIDATE - не канает
[24.07.2014 11:37:09] Рабащенко Иван: поднимаем так:
[24.07.2014 11:37:38] Рабащенко Иван: [21 июля 2014 г. 14:23] Рабащенко Иван:

<<< ALTER TABLE ... PARALLEL ...
ALTER TABLE ... MODIFY CONSTRAINT ...  ENABLE NOVALIDATE
ALTER TABLE ...  MODIFY CONSTRAINT ... VALIDATE
[24.07.2014 11:37:56 | Изменены 11:37:59] Рабащенко Иван: Подымаем в одной сессии





среда, 23 июля 2014 г.

Перетягивание «Верблюда» или интеграция средствами Camel. Часть 1

Перетягивание «Верблюда» или интеграция средствами Camel. Часть 1

История одного проекта.



Вам когда-нибудь снились верблюды? Вот и мне тоже нет. Но, когда работаешь с Camel-ом уже третий год, начинают сниться не только верблюды.
В общем, буду делиться опытом, писать о верблюдах и учить вас их готовить. Это серия статей в трёх частях: первая часть будет для тех, кому интересны истории и муки творчества; вторая — больше техническая, о паттернах интеграции, их применении и третья часть — об ошибках и отладке.
Если вам нужно объединить ваши сервисы, здесь вы узнаете, чем хорош Camel. Если вы хотите научиться использовать что-нибудь новенькое, здесь мы начнём с азов. Если вам нравятся истории и оригинальные фишки, которые есть в каждой команде, то читайте дальше.



Задача интеграции


Начну с того, как появилась необходимость в сервисной шине. Мы разрабатываем крупную систему, которую ласково “за глаза” назвали монстриком. Монстрик получился большой и страшный, а в действительности это была одна из BPM систем (business process managment system). Началось всё несколько лет назад. Однажды на совещании руководитель проекта заговорил о планах на будущее:
— Коллеги, в ближайшее время мы планируем интегрироваться с большим количеством внутренних и внешних систем. Сейчас нам надо проработать системный подход для того, чтоб наши аналитики смогли начать подготовку задач.

Потом ещё какое-то время он рассказывал о тех системах, с которыми нам предстоит интегрироваться. Здесь были как внешние хорошо известные системы, например, elibrary.ru, так и внутренние, которые ещё только предстояло создать. По завершению рассказа обозначились характерные черты интеграции с разными типами систем. Для внешних систем — это большая неопределённость описания процессов и объектов предметной области (бизнес объектов), которыми предстояло обмениваться; зато было понятно направление передачи данных (загрузка или выгрузка); просматривались дополнительные требования к защите каналов передачи данных, обеспечению гарантированной доставки и предпосылки для создания инструментов исправления ошибочных данных. Для вторых — внутренних систем, ясности было больше. Четко описывалось функциональное назначение каждой из систем. Благодаря чему можно было проработать круг объектов, подлежащих передаче. Вот только для этих систем неопределённость проявлялась на уровне будем — не будем, успеем — не успеем реализовать. Не вдаваясь в подробности прикладной области, ограничусь типичными задачами, на решении которых мы сконцентрировались.
  1. Необходимо загрузить информацию в основную систему по стандартному протоколу. На момент обсуждения для внешних систем упоминались два: HTTP, ftp; для внутренних: JMS, HTTP, NFS, SMB и/или используя интерфейс RMI. С помощью какой архитектуры мы могли бы решить эту задачу?
  2. Предположим, необходимо выгрузить информацию из основной системы и передать во внешнюю систему по одному из стандартных протоколов. Какие могли быть способы решения такой задачи?
  3. Или же требуется передать один, два, n бизнес-объектов. Как мы могли подготавливать данные и какой формат использовать?
  4. Допустим, необходимо отправить электронное сообщение через почтовый сервер, который временно не доступен. Для этого пользователю потребуется либо дождаться доступности последнего, либо получить сообщение о невозможности закончить свою работу сейчас. Как мы могли построить систему, гарантирующую доставку и не блокирующую работу пользователей?
  5. Примем, что нам необходимо иметь возможность забирать информацию с внешней системы в 1 или 3 часа ночи, но наша система в это время прерывала свою работу на обслуживание. Как сделать так, чтоб во время не доступности основной системы процессы выгрузки и загрузки данных не останавливались?
  6. И последняя задача: необходимо иметь возможность оперативно изменить логин и пароль для доступа к внешней системе или протокол связи с ftp на sftp. Как мы могли сделать так, чтоб изменение настроек можно было выполнять гибко, не нарушая работы основной системы?

Обсудив задачи, мы пришли к решению создать автономное приложение, которое будет связывать основную систему с другими. Идея использовать промежуточное звено не нова и известна в мире интеграции под терминомсервисная шина предприятия или ESB. Давайте очертим круг функциональных возможностей, которыми должно было обладать наше приложение. За основу взяты формулировки задач. Оно должно уметь:
  • взаимодействовать с другими системами по стандартным протоколам: HTTP, FTP, а также по протоколу JMSили с помощью интерфейса RMI.
  • читать, записывать и создавать файлы в локальной файловой системе, для работы с NFS
  • отправлять почтовые сообщения через SMTP сервера.
  • разбирать один из транспортных форматов XML, JSON
  • иметь гибкую настройку
  • иметь возможность описывать правила согласования форматов на одном из языков программирования Java, Groovy. Мы работали с этими языками, поэтому могли рассчитывать на отсутствие дополнительного overhead-а на изучение.
  • возможность работать с XSLT и xPath была бы дополнительным плюсом.


От рассуждений к практике


Через какое-то время, когда процесс поиска только набирал обороты, аналитики по заказу клиента написали первую постановку:
— Ребят, мы тут постановку написали, надо её сделать быстро. Заказчик очень ждал её ещё вчера, поэтому постарайтесь.

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

А пока предстояло выбрать технологии, на которых должен был строиться наш прототип. Изобретать свой велосипед и начинать с чистого листа — здорово, но слишком дорого. Проприетарные решения тоже не стали использовать потому, что результат требовался быстро, а на то, чтоб согласовать и решить финансовые вопросы, нужно время. Поэтому обратились к opensource проектам. На тот момент выбор был небольшой, поэтому оценить достоинства “верблюда” смогли с первого взгляда.

Как видно из схемы, Apache Camel — модульный, легко расширяемый каркас для интеграции приложений. Главные структурные элементы: компонентная модель, механизм маршрутизации, механизм обработки сообщений. Компонентная модель представляет собой набор фабрик, создающих конечные точки маршрутизации. Например, конечная точка может быть абстракцией, отправляющей сообщения в очередь JMS брокера. Механизм маршрутизации связывает конечные точки с абстракциями обработки сообщений. Последний, механизм обработки сообщений, позволяет манипулировать данными сообщений. Например, преобразовывать в другой формат, проводить валидацию, добавлять новое содержимое, журналировать и многое другое. Все три архитектурных компонента модульные, и благодаря этому возможности Camel постоянно расширяются. Подробно ознакомиться с архитектурой можно в википедии и на официальном сайте. Только появившись, Apache Camel обзавёлся солидной коллекцией компонентов. Нашлись компоненты, чтобы удовлетворить все наши требования: JMS, HTTP, ftp, file, SMTP, xPath, xslt, XStream, Groovy, Java. Как и автор статьи Which Integration Framework Should You Use – Spring Integration, Mule ESB or Apache Camel?, мы выбрали Camel. В этой статье проводится сравнение трёх framework-ов: Spring Integration, Mule ESB and Apache Camel. Ключевое преимущество автор описывает так:
… Apache Camel due to its awesome Java, Groovy and Scala DSLs, combined with many supported technologies.
Возможность использовать fluent Java DSL вместо “корявого” XML стала большим преимуществом и для нас. Возникает вопрос: в чём корявость? XML — отличный язык разметки, но ему уже давно предпочитают JSON или YAML. Предпочтение им отдают из-за простоты, лучшей читаемости, меньшего числа вспомогательной информации и более простых алгоритмов разбора. Языки программирования такие как Java, Groovy, Scala имеют полноценную поддержку современными IDE, а значит в отличии от XML появляется возможность отладки и рефакторинга. Сомнений в Camel-е не осталось, и он лег в основу нашей сервисной шины. Тот факт, что этот проект использовали другие компании, добавлял уверенности в правильном выборе.
Оставался главный вопрос — как интегрировать Camel в нашего монстрика.

Муки выбора: JMS против RMI.



Интеграцию сервисной шины и нашего монстрика можно было реализовать с помощью одного из множества компонентов, поддерживаемых Camel-ом. Основываясь на задачах, приведённых выше, мы сформировали требования: связь должна быть стабильной и гарантирующей доставку сообщений. Остановились на трёх вариантах: первые два были синхронные RMI и HTTP, и один асинхронный JMS. Из трёх остановились на двух самых простых вариантах подходящих для наших проектов на Java: JMS, RMI. JMS (Java message service) — это стандарт для рассылки сообщений, он регламентирует правила отправки, получения, создания и просмотра сообщений. Второй, RMI (Remote Method Invocation) — это программный интерфейс вызова удаленных процедур, он позволяет вызывать методы одной JVM в контексте другой. Стандартная процедура удалённого вызова включает упаковку Java объектов и передачу. Справедливости ради, стоит отметить, что противопоставлять JMS и RMI не корректно потому, что JMS может быть транспортом и составной частью RMI. Мы противопоставляем стандартную реализацию RMI и реализацию JMS — ActiveMQ. Раньше RMI уже использовался нами для интеграции двух приложений. Почему его выбрали тогда? Когда оба приложения на Java, нет ничего проще RMI. Для того, чтобы его использовать, достаточно описать интерфейс и зарегистрировать объект, реализующий этот интерфейс. Но нам довелось решать проблемы, возникающие с RMI, при передаче большого объёма данных между приложениями: память забивалась, и приложения “складывались”. Мы искали способы решения этой проблемы и обсуждали её с разработчиками JVM на JavaOne. Выяснилось, что сборщики “мусора” в виртуальной машине и распределённые сборщики — это разные вещи. Всё упиралось в то, что для стандартного сборщика мусора можно было выбрать его тип и настроить оптимальные параметры, а для распределённого такой возможности не было. Если говорить о других отличиях, то RMI ограничивал интеграцию приложениями, выполняющимися на JVM, а JMS — нет. Вдобавок к описанным трудностям было желание изучить что-то новое: отказаться от RMI и использовать альтернативное решение.

Первый прототип Camel


Давайте вернёмся к созданию прототипа. Первая практическая задача для сервисной шины была такая: пользователь инициирует процесс выгрузки данных, система их подготавливает и отправляет в сервисную шину. Вся работа по доставке данных ложится на неё. На рисунке — пример постановки задачи в символах паттернов интеграции корпоративных приложений (EIP).

Сервисная шина объединяет приём сообщений из канала JMS, их преобразование и отправку через HTTP. Отмеченные на рисунке канал JMS и канал отправки сообщений в формате HTML предполагалось реализовать, используя JMS и Jetty компоненты Camel. Процесс преобразования данных можно было реализовать на Java и/или использовать шаблонизаторы, такие как, например, VM (Apache Velocity). Предложенная схема передачи данных реализуется на Java DSL в одну строку. Пример:
from("jms:queue:se.export")
    .setHeader(Exchange.HTTP_METHOD,constant(org.apache.camel.component.http.HttpMethods.POST))
    .process( new JmsToHttpMessageConvertor() )        
    .inOnly("jetty:{{externalsystem.uri}}");

На примере выше приведён роут на Java DSL. Роут — это описание маршрута передачи сообщения. В Camel-е описания могут быть двух типов: Java DSL и XML DSL. Характеристиками такого маршрута являются начальная и одна или несколько конечных точек, обозначенными токенами from и to соответственно. Маршрут описывает путь сообщения от начальной до конечной точки. Если конечные точки указаны последовательно, сообщение будет передано на первую, сервисная шина дождется ответа, который затем отправит на следующую точку. Могут встречаться маршруты, выбирающие нужную конечную точку (Dynamic Router) или отправляющие сообщение сразу на несколько точек (Recipient List). Параметр токенов from и to — это строка с URI. URI представляет собой тройку параметров, состоящую из названия компонента Camel, идентификатора ресурса и параметров подключения. Давайте разберём на примере:
from(“jms:queue:se.export?timeToLife=10000”)

Это описание входной точки, которая использует компонент JMS. Компонент JMS предоставляет возможность получать данные из ресурса queue:se.export. Где queue — тип канала сообщений, может быть или очередь(queue), или тема (topic). Далее идёт название канала “se.export”. Очередь с таким именем будет создана брокером сообщений. Последняя часть URI, параметры конечной точки: “timeToLife=10000”, говорит о том, что время жизни пакета составляет 10 сек.
Из примера понятно, как мы планировали организовать передачу данных, в следующей статье будет больше реального кода и примеров.
Итак, мы решили задачу передачи данных, создали прототип интеграционной шины, который состоял из Camel и был практически готов к внедрению. Оставалось решить задачу его правильной и удобной настройки.

Настройка прототипа


Мне очень импонирует эта тема, так как сложно найти практические советы и примеры реализации.
У нас структура стендов такая:
У каждого разработчика своя копия ПО, и он перед запуском её полностью настраивает. Есть два тестовых стенда для ручного тестирования, и конечно, есть рабочая система. Формулировка задачи получается следующая:
  • мелкая настройка на каждом из стендов должна была выполнятся просто редактированием минимального количества параметров в конфигурационных файлах системы
  • большая настройка (добавление новых функций) выполнялось незаметно для всех стендов

Вдобавок, если на стендах разработчиков была возможность использовать сборщик проектов (Maven), то на тестовых стендах и рабочем сервере такой возможности не было.
Сложностей в этой задаче масса: Camel связан с брокером JMS, что заставляет использовать разные каналы для разных стендов или разные брокеры сообщений. Мы пошли самым простым путём запуская встроенный в шину брокер ActiveMQ. В таком случае остаётся предусмотреть настройки подключений для разных серверов.

Давайте перейдём к примерам использования параметров в настройках Camel:
  1. Пример из файла camel-config.xml
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations">
        <list>
          <value>classpath:system.properties</value>
          <value>classpath:smtp.properties</value>
        </list>  
      </property>
    </bean>
    

    В этом примере задаются два файла с настройками для Spring-а.
  2. Пример, в котором используются настройки, заданные двумя файлами из предыдущего примера.
    <bean id="someServiceStartPolicy" class="org.apache.camel.routepolicy.quartz.CronScheduledRoutePolicy">
      <property name="routeStartTime" value="${config.someService.routeStartTime}"/>
      <property name="routeStopTime" value="${config.someService.routeStopTime}"/>
    </bean>
    

    Сами свойства задаются в строке "${config.someService.routeStartTime}"
  3. Пример, в котором передаются несколько файлов с настройками в контекст Camel-а
    <camelContext id="rootCamelRoute" xmlns="http://camel.apache.org/schema/spring">
      <propertyPlaceholder id="properties"
          location="smtp.properties, system.properties"/></propertyPlaceholder>
    </camelContext>
    
  4. Пример использования параметров в роутах Camel на Java DSL
    from("jms:queue:email.send")
        .setHeader("to", simple("${headers.email}"))
        .setHeader("from", simple("${properties:config.smtp.from}"))
        .to("{{config.smtps.server}}?username={{config.smtps.user}}&password={{config.smtps.password}}&contentType=text/html&consumer.delay=60000")
    

    Здесь используются сразу несколько способов адресации параметров. Вот они:
    — в simple диалекте строкой “${properties:config.smtp.to}”
    — в URI endpoint-а строкой “{{config.smtps.server}}”
    Названия параметров могут быть любые, строки взяты из примера выше.


Давайте представим реальную задачу:
есть сервис, который отправляет письма через сервисную шину на сервер SMTP; такой сервис должен для рабочей системы выполнять отсылку сообщений пользователям, а для тестовой выполнять отсылку всех писем на один почтовый ящик.
Тогда эта задача трансформируется в добавление разной логики для некоторых маршрутов выполняемых на тестовой и рабочей системе.

Вот пример того как такая задача может быть решена, используя параметры в роутах Camel-а.
from("jms:queue:event.recoverypass")   
    .setHeader("to", isDebug() ? simple("${properties:config.smtp.to}") : simple("${headers.email}"))
    .setHeader("from", simple("${properties:config.smtp.from}"))
    … // some other headers
    .choice()
        .when( header("password").isNotNull() )
            .setHeader("subject", simple("${properties:config.passwordNotify}"))
            .to("velocity:vm/email/newPasswordNotify.vm")
        .otherwise()
            .setHeader("subject", simple("${properties:config.recoverypass}"))
            .to("velocity:vm/email/recoveryPassword.vm")
        .end()
    .to("{{config.smtps.server}}?username={{config.smtps.user}}&password={{config.smtps.password}}&contentType=text/html&consumer.delay=60000")

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

Итоги


Прототип шины был закончен: появились несколько роутов выполняющих передачу сообщений, появились конфигурационные файлы облегчающие настройку и развёртывание шины. Уже по результатам первых шагов в осваивании Camel-а можно было говорить о большом потенциале такого подхода. Простота и лаконичность написания маршрутов завораживает. Кажется, что одной строкой можно сделать всё. Но следует обратить внимание на то, что работа с роутами требует смены мышления. Для программистов, которые с ними дело не имели, лучше сразу обратиться к книге “Шаблоны интеграции корпоративных приложений”. Эффект от понимания шаблонов интеграции сравним с эффектом от знания шаблонов проектирования в классическом ООП.

На этом пока, пока. До встречи в следующей части. Напомню, она будет посвящена юзкейсам использования Camel-а.

вторник, 22 июля 2014 г.

Разрешение конфликтов в транзитивных зависимостях — Хороший, Плохой, Злой

Разрешение конфликтов в транзитивных зависимостях — Хороший, Плохой, Злой

Вместо предисловия


В ближайшую субботу мы с EvgenyBorisov будем выступать в Питере на JUG.ru. Будет много веселого трэша и интересной инфы (иногда не разберешь, где проходит граница), и одно из моих выступлений будет посвящено WTF-нутости модульной разработки программ. Я расскажу несколько ужастиков, один из которых будет о том, как все пытаются быстро, гибко и корректно описать зависимости в проекте, и что из этого обычно получается. Интересно? Тогда добро пожаловать в ад!


Скорее, конечно, «Хороший, Удобный и WTF-ный».


Чуть-чуть теории...


Что Такое Менеджер Зависимостей и Зачем Он Нужен?

Любой современный инструмент сборки (особенно в мире JVM) включает в себя (либо имеет легко подключающийся) менеджер зависимостей (a.k.a. dependency manager). Самые известные, это, конечно, Apache MavenApache Ivy (менеджер зависимостей для Apache Ant), и Gradle.
Одна из главных функций инструментов сборки в мире JVM это создавать classpath-ы. Используются они во время процесса сборки много где — для компиляции кода, для запуска тестов, для сборки архивов (war-ов, ear-ов, или дистирбутивов и установщиков) и даже для настройки проектов в IDE.
Для облегчения процесса нахождения в сети, скачивания, хранения и конфигурации зависимостей и существуют менеджеры зависимостей. Вы декларируете, что вам нужен, например, commons-lang, и вуаля, он у вас есть.

Что такое транзитивные зависимости?

Транзитивная зависимость — это тот артефакт, от которого зависит прямая зависимость проекта. Представьте себе следующую ситуацию:

Наш проект A зависит от двух артефактов — E и B. На этом прямые зависимости заканчиваются и начинаются транзитивные(C, D). В итоге мы получаем цепочки зависимостей, артефакты в которых могут повторяться (D, в нашем примере)

Зачем нужны транзитивные зависимости?

Очевидно, не для компиляции. Но, для всего остального, пожалуй, нужны — эти артефакты должны находиться в сборках архивов, они должны находиться в classpath для запуска тестов, и пути к ним должны быть отданы через API для интеграции с IDE.

Как может образоваться конфликт?

Если мы посмотрим на диаграмму выше, то увидим тот самый конфликт. В classpath проекта A должны находиться и артефактD версии 1 (от него зависит Е), и артефакт D версии 2 (от него зависит C)!

Почему это плохо?

JVM (и javac) определяет уникальность класса по его имени (и classloader-у, но в нашем простом примере все классы загружаются одним classloader-ом). В случае, когда в classpath встречаются два класса с одинаковым именем, загружен будет только первый. Если предположить, что в D1 и в D2 находятся классы с одинаковым именем (согласитесь, скорее всего, так и есть), то класс из jar-а, который будет прописан в сгенерированном classpath-е вторым просто не будет загружен. Какой из них будет первый? Это зависит от логики менеджера зависимостей и, в общем случае, неизвестно.
Как вы понимаете, это и есть конфликт:


Что делать?


Кто виноват понятно (Java, а не те, о ком вы подумали), а вот что можно сделать?
Есть несколько стратегий разрешения конфликтов в транзитивных зависимостях (некоторые из них логичные, другие — абсурдные), но, естественно, серебряной пули нет. Давайте посмотрим на некоторые из них:
  • Latest. Стратегия «Новейший» подразумевает обратную совместимость. Если D2 полностью совместим с D1, то оставив в classpath только более новый артефакт (D2) мы получим корректную работу C (ведь он написан под D2), но и корректную работу E (ведь если D2 обратно-совместим, то он работает точно так-же как D1, под который и написан E). Эта стратегия бывает двух подвидов — новейший по версии, и новейший по дате. Чаще всего они сработают одинаково (кроме случаев, в которых нет).
    В случае нашего примера, при использовании latest в classpath окажется D2.
  • Fail (a.k.a. Strict). При этой стратегии сборка упадет в тот момент, когда менеджер зависимостей обнаружит конфликт. Естественно, самая безопасная, но и самая трудоемкая стратегия.
    В случае нашего примера, при использовании fail сборка упадет.
  • All (a.k.a. No-conflict). «И то, и другое, и можно без хлеба» значит, что и D1 и D2 из нашего примера окажутся в classpath-е (в произвольном порядке). Ад? Ад! Но в случае использования технологий изолирования classpath-а (путем загрузки разных модулей разными classloader-ами), вполне может быть не только полезен, но и необходим.
    В случае нашего примера, при использовании all в classpath окажутся и D1, и D2.
  • Nearest. Стратегия «Ближайший» это целиком и полностью великолепный WTF, про который я с удовольствием расскажу ниже. Stay tuned.
  • Custom. В этом случае менеджер зависимостей спросит у вас, что изволит барин. Это, конечно, «ручное управление», но иногда может быть весьма полезно. Вот пример псевдокода на псевдогруви:
    coflictManager = {artifact, versionA, versionB ->
        //допустим, я полагаюсь на обратную совместимость только библиотек Apache, но не остальных
        if(artifact.org.startsWith ('org.apache')){
           [versionA, versionB].max()
        } else {
            fail()
        }
    }
    

    В случае нашего примера, при использовании этой имплементации custom, если предположить что org у D1 и D2 начинается с 'org.apache', то в classpath окажется D2, в противном случае, сборка упадет.

Kто во что горазд


Теперь давайте посмотрим, кто из Дер Гроссе Тройки упомянутой выше, что умеет.

Apache Ivy

В плане менеджеров конфликтов Ivy прекрасен. Они подключаемы, оба варианта latest, а так же fail и all идут в коробке. С custom-ом тоже всё красиво. Можно полностью имплементировать свою логику, а можно воспользоваться полуфабрикатом и лишь придумать подходящий regex. По умолчанию работает latest (по версии).

Gradle

В первых версиях Gradle (до 0.6, если мне не изменяет память) использовался Ivy как менеджер зависимостей. Соответственно, всё сказанное выше было верно для Gradle тоже, но ребята из Gradleware написали свой менеджер зависимостей (в основном из за проблем с локальным хранилищем Ivy при параллельной сборкe, одного из главных преимуществ Gradle). В процессе выпуска своего менеджера такие «второстепенные» фичи как замена менеджера конфликтов были задвинуты далеко в roadmap, и довольно долгое время Gradle существовал только с latest. Не нравится latest — отключай транзитивные зависимости, и вперед, перечислять всё в ручную. Но, сегодня всё в порядке. Начиная с 1.0 есть fail, а с 1.4 и custom тоже.

Apache Maven

Ну, ради следующей картинки и был задуман весь пост.
Как вы считаете, какая из версий D попадет в classpath? D1? D2? обе? ни одной? сборка упадет?

Как вы уже, наверняка, догадались, в classpath попадет D1 (что с огромной вероятностью приведет к проблемам, потому что весь код в C, написанный под новую функциональность, которой не существует в D1, просто упадет). Это тот самый чудесный WTF, который я вам обещал. Maven работает с уникальной стратегией nearest, которая выбирает в classpath тот артефакт, который находится ближе к корню проекта (А) в дереве проектов.


Как же так? Что за ерунда?

Корень проблемы лежит в трудности исключения зависимости в Maven. Если, например, вы хотите использовать D2, а не D1, то, по хорошему, вы должны сказать Maven-у: Дорогой Maven, никогда не используй D1. Просто для примера, в Gradle мы бы написали вот так:
configurations {all*.exclude group: 'mygroup', module: 'D', version: '1'}

Проблема в том, что выразить это в Maven нельзя никак. (Опытный, и потому внимательный и вдумчивый пользователь Maven-а воскликнет здесь «А как же enforcer-plugin?!» И будет неправ). Можно сказать конкретно модулю E: «ты думал у тебя есть зависимость на D? Так вот, ее нет». Это хороший выход, конфликта больше нет, D2 в classpath, win. Но это решение совершенно не масштабируемо. Что если от D1 зависят десятки артефактов? На разных уровнях транзитивности?

Ну, и причем тут nearest?

Проблема отсутствия глобального exclude была решена в Maven-е очень «интересным» способом. Было решено, что если вы объявили в вашем проекте А зависимость с определенной версией, то только эта версия и попадет в classpath. То есть практически, это ультимативный nearest — ближе чем в A быть не может (это root), поэтому конфликт решён, не нужно искать все места откуда нужно исключать D. По дороге, правда, мы получили очень странное и трудно-предсказуемое поведение в тех случаях, когда A не объявляет D напрямую (см. наш пример), но что есть, то есть.

Достаточно интересно, что идея «то, что пользователь объявил сам — закон» используется в Gradle тоже, и это не мешает им использовать вменяемые стратегии типа latest и fail для всего остального.

Update: несколько человек в комментах напомнили, что у enforcer-plugin есть функциональность fail Это частично решает проблему. Остается: 1. дикий nearest по умолчанию. 2. варианты решения проблемы — прописывать все конфликтующие зависимости в своем проекте (сносно), либо бесконечные exclude-ы (адово).

А если одинаковая глубина?

Этот прекрасный вопрос (что делать, если бы в нашем примере B зависел от D2) не приходил ребятам из Maven-а в голову на протяжении двух с половиной лет (от релиза 2.0 в октябре 2005 и до версии 2.0.9 в апреле 2008) и какой артефакт будет в classpath было просто неопределенно. В Maven 2.0.9 было принято волевое решение — будет первый!

Как это нам помогает? Правильно, никак. Потому что мы в общем случае не знаем, какой из них будет первый, ведь транзитивные зависимости не проявляют себя пока не случается конфликт (либо пока мы не начинаем расследовать эту загадку). Спасибо, пацаны!

Вместо эпилога


WTF-нутость Maven-а, естественно, не ограничивается чудесным порождением альтернативного разума — стратегией nearest. Но на сегодня, я думаю, хватит. Холивары в комментах всячески приветствуются (если что, я притоплю за Gradle), а все питерцы приходят на JUG в субботу 31 числа в ПетроКонгресс на продолжение банкета.