Статьи

NewSQL в облачном масштабе — веб-сервисы Spring, Hibernate и Amazon с NuoDB

Примечание Кураторское: Это последняя статья в серии из трех статей , исследующих NuoDB. Вы можете узнать больше о NuoDB, а также получить бесплатную пробную версию, перейдя на сайт NuoDB .

Вступление

В предыдущих статьях мы показали архитектуру NuoDB NewSQL, ее ключевые компоненты и способы ее легкого масштабирования на уровнях транзакций и хранения. Мы также продемонстрировали JDBC и Hibernate с NuoDB. В этом заключительном посте из серии из 3 статей мы продемонстрируем Spring и Hibernate с NuoDB в облачном масштабе с использованием возможностей AWS (AWS EC2 и CloudFormation).

Весна и зимняя спячка с NuoDB

Spring PetClinic  — это пример приложения, которое распространяется вместе с Spring Framework. Он предназначен для того, чтобы показать, как можно использовать среды приложений Spring для создания простых, но мощных приложений, ориентированных на базы данных. В этом году он был  реорганизован  с учетом новой архитектуры, и исходный код можно загрузить с  Github . Spring PetClinic изначально поддерживает базы данных HSQL и MySQL, и в этом посте мы портируем его для использования с NuoDB.

Чтобы получить код, нам нужно запустить:

$ git clone https://github.com/SpringSource/spring-petclinic.git

Приложение может быть построено и запущено с помощью команды maven, но сначала нам нужно реализовать изменения, связанные с NuoDB, которые связаны с настройкой базы данных, обновлением конфигураций Hibernate и Spring.   Другими словами, ни один источник приложения не нуждается в модификации.

Сценарии для слоя db можно найти в этом каталоге:
~ / spring / spring-petclinic / src / main / resources / db . Там мы создали новый
каталог NuoDB и создали два сценария SQL: initDB.sql и populateDB.sql.

$ cd ~/spring/spring-petclinic/src/main/resources/db

$ mkdir nuodb

$ vi initDB.sql  # edit initDB.sql

DROP TABLE vet_specialties IF EXISTS;

DROP TABLE vets IF EXISTS;

DROP TABLE specialties IF EXISTS;

DROP TABLE visits IF EXISTS;

DROP TABLE pets IF EXISTS;

DROP TABLE types IF EXISTS;

DROP TABLE owners IF EXISTS;

 

CREATE TABLE vets (

  id         INTEGER primary key  generated always as identity,

  first_name VARCHAR(30),

  last_name  VARCHAR(30)

);

CREATE INDEX vets_last_name ON vets (last_name);

 

CREATE TABLE specialties (

  id   INTEGER primary key  generated always as identity,

  name VARCHAR(80)

);

CREATE INDEX specialties_name ON specialties (name);

 

CREATE TABLE vet_specialties (

  vet_id       INTEGER NOT NULL,

  specialty_id INTEGER NOT NULL

);

ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_vets FOREIGN KEY (vet_id) REFERENCES vets (id);

ALTER TABLE vet_specialties ADD CONSTRAINT fk_vet_specialties_specialties FOREIGN KEY (specialty_id) REFERENCES specialties (id);

 

CREATE TABLE types (

  id   INTEGER primary key  generated always as identity,

  name VARCHAR(80)

);

CREATE INDEX types_name ON types (name);

 

CREATE TABLE owners (

  id         INTEGER primary key  generated always as identity,

  first_name VARCHAR(30),

  last_name  VARCHAR(30),

  address    VARCHAR(255),

  city       VARCHAR(80),

  telephone  VARCHAR(20)

);

CREATE INDEX owners_last_name ON owners (last_name);

 

CREATE TABLE pets (

  id         INTEGER primary key  generated always as identity,

  name       VARCHAR(30),

  birth_date DATE,

  type_id    INTEGER NOT NULL,

  owner_id   INTEGER NOT NULL

);

ALTER TABLE pets ADD CONSTRAINT fk_pets_owners FOREIGN KEY (owner_id) REFERENCES owners (id);

ALTER TABLE pets ADD CONSTRAINT fk_pets_types FOREIGN KEY (type_id) REFERENCES types (id);

CREATE INDEX pets_name ON pets (name);

 

CREATE TABLE visits (

  id          INTEGER primary key  generated always as identity,

  pet_id      INTEGER NOT NULL,

  visit_date  DATE,

  description VARCHAR(255)

);

ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id);

CREATE INDEX visits_pet_id ON visits (pet_id);

$ vi  populateDB.qsl  # edit populateDB.sql

INSERT INTO vets VALUES (NULL, 'James', 'Carter');

INSERT INTO vets VALUES (NULL, 'Helen', 'Leary');

INSERT INTO vets VALUES (NULL, 'Linda', 'Douglas');

INSERT INTO vets VALUES (NULL, 'Rafael', 'Ortega');

INSERT INTO vets VALUES (NULL, 'Henry', 'Stevens');

INSERT INTO vets VALUES (NULL, 'Sharon', 'Jenkins');

 

INSERT INTO specialties VALUES (NULL, 'radiology');

INSERT INTO specialties VALUES (NULL, 'surgery');

INSERT INTO specialties VALUES (NULL, 'dentistry');

 

INSERT INTO vet_specialties VALUES (2, 1);

INSERT INTO vet_specialties VALUES (3, 2);

INSERT INTO vet_specialties VALUES (3, 3);

INSERT INTO vet_specialties VALUES (4, 2);

INSERT INTO vet_specialties VALUES (5, 1);

 

INSERT INTO types VALUES (NULL, 'cat');

INSERT INTO types VALUES (NULL, 'dog');

INSERT INTO types VALUES (NULL, 'lizard');

INSERT INTO types VALUES (NULL, 'snake');

INSERT INTO types VALUES (NULL, 'bird');

INSERT INTO types VALUES (NULL, 'hamster');

 

INSERT INTO owners VALUES (NULL, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');

INSERT INTO owners VALUES (NULL, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');

INSERT INTO owners VALUES (NULL, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');

INSERT INTO owners VALUES (NULL, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');

INSERT INTO owners VALUES (NULL, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');

INSERT INTO owners VALUES (NULL, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');

INSERT INTO owners VALUES (NULL, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');

INSERT INTO owners VALUES (NULL, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');

INSERT INTO owners VALUES (NULL, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');

INSERT INTO owners VALUES (NULL, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');

 

INSERT INTO pets VALUES (NULL, 'Leo', '2010-09-07', 1, 1);

INSERT INTO pets VALUES (NULL, 'Basil', '2012-08-06', 6, 2);

INSERT INTO pets VALUES (NULL, 'Rosy', '2011-04-17', 2, 3);

INSERT INTO pets VALUES (NULL, 'Jewel', '2010-03-07', 2, 3);

INSERT INTO pets VALUES (NULL, 'Iggy', '2010-11-30', 3, 4);

INSERT INTO pets VALUES (NULL, 'George', '2010-01-20', 4, 5);

INSERT INTO pets VALUES (NULL, 'Samantha', '2012-09-04', 1, 6);

INSERT INTO pets VALUES (NULL, 'Max', '2012-09-04', 1, 6);

INSERT INTO pets VALUES (NULL, 'Lucky', '2011-08-06', 5, 7);

INSERT INTO pets VALUES (NULL, 'Mulligan', '2007-02-24', 2, 8);

INSERT INTO pets VALUES (NULL, 'Freddy', '2010-03-09', 5, 9);

INSERT INTO pets VALUES (NULL, 'Lucky', '2010-06-24', 2, 10);

INSERT INTO pets VALUES (NULL, 'Sly', '2012-06-08', 1, 10);

 

INSERT INTO visits VALUES (NULL, 7, '2013-01-01', 'rabies shot');

INSERT INTO visits VALUES (NULL, 8, '2013-01-02', 'rabies shot');

INSERT INTO visits VALUES (NULL, 8, '2013-01-03', 'neutered');

INSERT INTO visits VALUES (NULL, 7, '2013-01-04', 'spayed');

Затем нам пришлось изменить файл business-config.xml в
каталоге ~ / spring / spring-petclinic / src / main / resources / spring, чтобы он ссылался на правильный NuoDB hibernate.dialect с помощью свойства hibernate.dialect:

# business-config.xml

   <!-- JPA EntityManagerFactory -->

        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"

              p:dataSource-ref="dataSource">

            <property name="jpaVendorAdapter">

                <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

            </property>

            <property name="jpaPropertyMap">

              <map>

                <entry key="hibernate.show_sql" value="${jpa.showSql}" />

                <entry key="hibernate.dialect" value="${hibernate.dialect}" />

                <entry key="hibernate.temp.use_jdbc_metadata_defaults" value="false" />

              </map>

            </property>

            <!-- gDickens: BOTH Persistence Unit and Packages to Scan are NOT compatible, persistenceUnit will win -->

            <property name="persistenceUnitName" value="petclinic"/>

            <property name="packagesToScan" value="org.springframework.samples.petclinic"/>

        </bean>

Чтобы поддерживать Hibernate 4, мы также устанавливаем hibernate.temp.use_jdbc_metadata_defaults в false.

Свойства базы данных должны быть настроены в файле data-access.properties — этот файл содержит соответствующие параметры JDBC (используемые в data-source.xml), а также диалект Hibernate. В приведенном ниже примере есть ссылка на локальный
экземпляр
NuoDB, работающий на виртуальной машине Ubuntu, и экземпляр AWS EC2 (закомментированный), который будет настроен позже в этой статье:

jdbc.driverClassName=com.nuodb.jdbc.Driver

jdbc.url=jdbc:com.nuodb://192.168.80.128/spring?schema=user

#jdbc.url=jdbc:com.nuodb://ec2-46-51-162-14.eu-west-1.compute.amazonaws.com/spring?schema=user

jdbc.username=spring

jdbc.password=spring

 

# Properties that control the population of schema and data for a new data source

jdbc.initLocation=classpath:db/nuodb/initDB.sql

jdbc.dataLocation=classpath:db/nuodb/populateDB.sql

 

# Property that determines which Hibernate dialect to use

# (only applied with "applicationContext-hibernate.xml")

hibernate.dialect=com.nuodb.hibernate.NuoDBDialect

 

# Property that determines which database to use with an AbstractJpaVendorAdapter

jpa.showSql=true

Файл datasource-config.xml определяет компонент источника данных с использованием JDBC:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"

          p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"

          p:username="${jdbc.username}" p:password="${jdbc.password}"/>

Оригинальный pom.xml от Github ссылается на Hibernate 4, а NuoDB может поддерживать Hibernate 4, поэтому мой pom.xml выглядел так:

# pom.xml              

          <properties>

                <!-- Hibernate / JPA -->

                <hibernate.version> 4.1.0.Final</hibernate.version>

                <!-- Bean validation -->

                <hibernate-validator.version> 4.2.0.Final</hibernate-validator.version>

 

                <nuodb.version>1.1</nuodb.version>

           </properties>

           <dependencies>

                <!-- For NuoDB -->

                <dependency>

                        <groupId>com.nuodb</groupId>

                        <artifactId>nuodb-hibernate</artifactId>

                        <version>${nuodb.version}</version>

                </dependency>

                <dependency>

                        <groupId>com.nuodb</groupId>

                        <artifactId>nuodb-jdbc</artifactId>

                        <version>${nuodb.version}</version>

                </dependency>

           </dependencies>

Наконец, нам нужно изменить файл web.xml в  каталоге ~ / spring / spring-petclinic / src / main / webapp / WEB-INF, чтобы использовать jpa — стандартная конфигурация использует jdbc.

   <context-param>

        <param-name>spring.profiles.active</param-name>

        <param-value>jpa</param-value>

    </context-param>

Теперь мы можем запустить команду maven, чтобы скомпилировать код, инициализировать и заполнить таблицы NuoDB, а затем прослушать порт 9966 для запросов.

$ mvn tomcat7:run

Также мы можем подключить Spring, используя  http: // localhost: 9966 / petclinic / .

Spring PetClinic с использованием NuoDB на AWS

До сих пор мы использовали локальный экземпляр NuoDB. Теперь пришло время перенести слой базы данных в облако AWS. Чтобы запустить NuoDB на экземплярах AWS EC2, одним из самых простых вариантов является использование AWS CloudFormation. На github доступны шаблоны CloudFormation  .

Чтобы скачать их, мы можем запустить

$ git clone https://github.com/nuodb/cloudformation.git

Затем нам нужно открыть консоль управления AWS; перейдите в CloudFormation и выберите «Создать стек» и загрузите шаблон NuoDB. (например, NuoDB-1.1. шаблон в нашем случае). Затем мы можем определить количество агентов (в дополнение к брокеру), имя домена, имя пользователя и пароль администратора домена и тип экземпляра EC2.

Нажмите на все изображения для увеличения

Как только мы нажмем продолжить, будут созданы экземпляры EC2. Сначала статус будет CREATE_IN_PROGRESS, затем CREATE_COMPLETE.

В результате мы получим 3 экземпляра EC2 (так как мы выбрали 2 агентов); один предназначен для брокера NuoDB, а два других — для агентов NuoDB. У нас также будет 3 тома EBS, по одному для каждого экземпляра EC2.

ВНИМАНИЕ .

Скрипт NuoDB CloudFormation использует группу автоматического масштабирования EC2, которая гарантирует, что число выбранных вами агентов всегда будет работать. В результате, если вы просто остановите экземпляры EC2 в конце теста, AWS перезапустит их. Чтобы все правильно остановить и очистить тестовую среду, откройте консоль управления AWS, перейдите в CloudFormation, выберите стек NuoDB и нажмите «Удалить стек». Это завершит все экземпляры, которые были запущены шаблоном CloudFormation. Подробнее об автоматическом масштабировании AWS и о том, как использовать инструмент командной строки, можно узнать  на веб-сайте AWS .

Теперь мы можем подключиться к хосту EC2, на котором работает брокер NuoDB:

После входа в систему мы можем запустить базу данных:

На следующих шагах мы можем определить имя базы данных (весна), оставив невыбранной опцию «разрешить недолговечную базу данных» и каталоги архива и журнала для менеджера хранилища, работающего на одном из хостов ( / home / ec2-user / nuodb) / data и / home / ec2-user / nuodb / journal соответственно). Обратите внимание, что каталог / home / ec2-user должен иметь соответствующие права, чтобы разрешить создание каталога данных и журнала (например, -rwxrwxrwx в нашем тесте).

После этого мы можем определить механизм транзакций, работающий на другом хосте EC2:

Теперь у нас есть 3 хоста EC2: один для брокера NuoDB для обслуживания клиентских подключений, один для менеджера хранилища с файловой системой и один для механизма транзакций.

Теперь, если мы изменим строку подключения jdbc в Spring PetClinic (помните, она определена в data-access.properties), мы можем подключить наше приложение к
NuoDB с помощью серверов AWS. Причина, по которой соединение возможно, заключается в том, что группы безопасности AWS позволяют подключать любые серверы.

$ vi data-access.properties

#jdbc.url=jdbc:com.nuodb://192.168.80.128/spring?schema=user

jdbc.url=jdbc:com.nuodb://ec2-46-51-162-14.eu-west-1.compute.amazonaws.com/spring?schema=user

Затем мы можем повторно запустить команду Spring PetClinic — на этот раз с базой данных в облаке AWS:

$ mvn tomcat7:run

Когда сервер Tomcat запущен и работает, мы можем перейти по  адресу http://ec2-46-51-162-14.eu-west-1.compute.amazonaws.com:9966/petclinic/  и мы можем искать можно добавлять новых владельцев, мы можем их искать и т. д.


Масштабирование и устойчивость

Масштабирование — это метод добавления вычислительных ресурсов путем добавления дополнительных компьютеров в систему, а не увеличения вычислительных ресурсов на компьютерах в системе.   Устойчивость — это способность обеспечивать и поддерживать приемлемый уровень обслуживания в случае неисправностей.

Если мы хотим масштабировать нашу базу данных, а также сделать ее эластичной, мы можем просто вернуться к консоли NuoDB и добавить новый процесс с помощью меню «Добавить процесс». Например, мы можем добавить механизм транзакций на сервер EC2, на котором изначально работал менеджер хранилища, и мы также можем добавить менеджер хранилища на сервер EC2, на котором ранее работал сервер транзакций. Таким образом, у нас есть два сервера EC2, на которых запущен один экземпляр механизма транзакций и менеджер хранилища.

Масштабирование базы данных — это простой процесс для приложения Spring PetClinic; мы просто запускаем другой сервер EC2 с агентом и добавляем механизм транзакций или механизм хранения. На Youtube загружен вебинар, в   котором рассказывается, как масштабировать NuoDB на AWS всего несколькими щелчками мыши.

Как видно из 
отчета о производительности NuoDB, NuoDB масштабируется почти линейно. На диаграмме ниже показано, как можно увеличить количество транзакций в секунду (TPS), добавив новый узел:

Вывод

Как мы видели в этой серии,
NuoDB   объединяет стандартные свойства SQL и ACID с эластичной масштабируемостью, что делает его идеально подходящим для использования в качестве надежной облачной системы управления данными. Его уникальный архитектурный подход обеспечивает высокую производительность операций чтения и записи и геораспределения операций 24/7 со встроенной отказоустойчивостью. Более того, приложения, использующие известные платформы, такие как Spring и Hibernate, могут быть легко перенесены или разработаны с помощью NuoDB в качестве базы данных NewSQL, отвечающей требованиям облачного масштаба.