Знакомство с Spring Cloud

Разработка, внедрение и работа с облачными приложениями может быть такой же легкой(если не легче), как с локальными приложениями. Именно таким принципом и должна руководствоваться облачная платформа, библиотека или инструмент. Spring Cloud —open-source библиотека— облегчает разработку облачных JVM приложений. С ней, приложения смогут легко соединяться с сервисами и получать информацию облака, таких как Cloud Foundry и Heroku. В дальнейшем, вы сможете расширить функционал для других облачных платформ и сервисов.

В этой статье(первой из серии) я буду знакомить вас с Spring Cloud и показывать, как работать с ней со стороны разработчика приложений. Мы будем разрабатывать простое приложение и разворачивать в Cloud Foundry и Heroku. В следующих статьях рассмотрим вопрос расширяемости.

Spring Cloud в Nutshell

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

Как получить доступ к приложениям этих сервисов? К примеру, если ваше приложение работает с реляционной базой данных, вам нужно создать объект DataSource этого сервиса. Вот где Spring Cloud помогает вам. Она берет на себя всю работу по настройке доступа и соединения и позволяет сфокусироваться на использовании этих сервисов. Она также предоставляет информацию о приложении(адрес хоста, порт, имя и др.).

Spring Cloud делает это все независимо от вида облака через Cloud Connector. На данный момент предоставляется реализация для Cloud Foundry и Heroku, однако мы можете расширить для своих типов облаков, реализовав интерфейс или используя часть существующего функционала библиотеки. Затем просто добавьте вашу библиотеку расширения в classpath вашего приложения, чтобы не пересобирать Spring Cloud.

Spring Cloud также признает, что невозможна реализация для каждого сервиса каждого облака. Поэтому, пока поддерживаются наиболее используемые сервисы «из коробки», вы можете сами расширить функциональность для своих сервисов. Просто, как обычное расширение облака, вы добавляете jar-файл расширения вашего сервиса в classpath вашего приложения.

В итоге, поддержка Spring(включая Spring Boot) приложениями в Java и XML конфигурациях осуществляется на уровне добавления свойств, т.к. Spring Cloud является сам модулем, основанным на Spring. Другие фреймворки осуществляют поддержку библиотеки подобным образом.

Рассмотрим Spring Cloud в действии.

Spring Cloud в действии

Мы начнем с простого приложения(исходный код), основанном на Spring Boot(обычное Spring MVC приложение также работало бы хорошо, но с немного большим количеством кода). Приложение состоит из бина, который взаимодействует с сервисом, и страницы, на которой отображается информация, полученная от сервиса.

@Controller
public class HomeController {
    @Autowired(required = false) DataSource dataSource;
    @Autowired(required = false) RedisConnectionFactory redisConnectionFactory;
    @Autowired(required = false) MongoDbFactory mongoDbFactory;
    @Autowired(required = false) ConnectionFactory rabbitConnectionFactory;

    @Autowired ApplicationInstanceInfo instanceInfo;

    @RequestMapping("/")
    public String home(Model model) {
        Map<Class<?>, String> services = new LinkedHashMap<Class<?>, String>();
        services.put(dataSource.getClass(), toString(dataSource));
        services.put(mongoDbFactory.getClass(), toString(mongoDbFactory));
        services.put(redisConnectionFactory.getClass(), toString(redisConnectionFactory));
        services.put(rabbitConnectionFactory.getClass(), toString(rabbitConnectionFactory));
        model.addAttribute("services", services.entrySet());

        model.addAttribute("instanceInfo", instanceInfo);

        return "home";
    }

    // ... различные toString() методы для каждого сервиса

}

HomeController имеет четыре переменные для конкретного сервиса, чтобы можно было бы связать с приложением, и ещё одну переменную ApplicationInstanceInfo. "/" добавлено как строковое представление адреса каждого сервиса вместе с его моделью классов. В обобщенном виде отображается вся эта информация.

В конфигурацию мы добавим CloudConfig, как показано ниже:

@Configuration
@ServiceScan
@Profile("cloud")
public class CloudConfig extends AbstractCloudConfig {
    @Bean
    public ApplicationInstanceInfo applicationInfo() {
        return cloud().getApplicationInstanceInfo();
    }
}

Класс наследуется от AbstractCloudConfig, именно таким является подход к написанию Java-конфигураций в Spring Cloud. Мы установили @Profile("cloud"), чтобы конфигурация загружалась только в облаке. Аннотация @ServiceScan сканирует все связанные сервисы и создает для каждого из них бин(который будет добавляться в HomeController). Вы можете задаться вопросом, в чем же разница между @ComponentScan и @ServiceScan и будете правы. В то время, как первый сканирует классы на кандидаты в бины, второй сканирует на связанные сервисы. Мы также создаем бин, содержащий информацию об экземпляре приложения.

Установка приложения в Cloud Foundry

Мы подключили следующий manifest.yml, который связывает все сервисы, которые нам нужны в данном примере(вам нужно создать сервисы, используя команду cf create-service):

---
applications:
- name: hello-spring-cloud
  memory: 512M
  instances: 1
  host: hello-spring-cloud-${random-word}
  domain: cfapps.io
  path: target/hello-spring-cloud-0.0.1-SNAPSHOT.jar
  services:
    - postgres-service
    - amqp-service
    - mongodb-service
    - redis-service
  env:
    SPRING_PROFILES_DEFAULT: cloud

Теперь нужно собрать и установить:

$ mvn package
$ cf push

Теперь, если мы откроем страницу, то увидим информацию об этих четырех сервисах:

В реальном приложении, вы вероятнее всего включите эти сервисы в служебные бины и включите более полезную информацию о соединении! Пожалуйста, посмотрите Spring Cloud и взгляните на список примеров приложений, которые делают более интересные вещи. Собственно говоря, сам spring.io использует Spring Cloud.

Установка приложения в Heroku

Вы можете установить то же приложение и на Heroku. Вам нужно только добавить пару файлов(ни один из них не относятся к Spring Cloud): system.properties позволяет Heroku использовать Java 7, Procfile — выполняет правильные команды для запуска приложения и активизирует cloud профиль. Мы установим приложение на Heroku следующим образом:

$ heroku apps:create
$ heroku addons:add mongolab
$ heroku addons:add rediscloud
$ heroku addons:add cloudamqp
$ heroku config:set SPRING_CLOUD_APP_NAME=hello-spring-cloud
$ git push heroku master

Мы создаем дополнения(похожие на сервисы Cloud Foundry) для MongoDb, Redis и AMQP. Heroku автоматически предоставляет Postgres сервис, поэтому мы его не добавляли. Рабочее окружение приложений Heroku, в отличие от Cloud Foundry, не раскрывает имени приложения, поэтому мы использовали heroku config:set для явного указания(иначе Spring Cloud установит <unknown>). Есть ещё несколько отличий в том, как Spring Cloud адаптируется между этими двумя облаками; мы рассмотрим их с следующих статьях.
Это все, что нужно сделать. Когда мы откроем страницу нашего приложения, то увидим информацию обо всех сервисах почти такую же, какую видели в Cloud Foundry.

Немного по управляем

Использование @ServiceScan делает легким захват всех сервисов и возможность их использования. Но на практике часто нужно делать большее, чем создание соединения, например установка параметров пула. В этом случае вы можете использовать Java или XML-конфигурацию. Внесем изменения в класс CloudConfig:

@Profile("cloud")
public class CloudConfig extends AbstractCloudConfig {
    @Bean
    public ConnectionFactory rabbitConnectionFactory() {
        return connectionFactory().rabbitConnectionFactory();
    }

    @Bean
    public DataSource dataSource() {
        return connectionFactory().dataSource();
    }

    @Bean
    public MongoDbFactory mongoDb() {
        return connectionFactory().mongoDbFactory();
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return connectionFactory().redisConnectionFactory();
    }

    @Bean
    public ApplicationInstanceInfo applicationInfo() {
        return cloud().getApplicationInstanceInfo();
    }
}

Если сравнить с первым вариантом, то вы увидите, что мы удалили аннотацию @ServiceScan. Вместо неё мы использовали API наследуемого AbstractCloudConfig класса, чтобы создать бин для каждого из сервисов.Теперь бины создаются так же, как и с @ServiceScan, но теперь у нас есть возможность для их настройки. Например, если мы захотим связать DataSource с конкретным сервисом и инициализировать его с конкретной конфигурацией, мы просто внесем изменения, как показано ниже:

@Bean
public DataSource dataSource() {
    PoolConfig poolConfig = new PoolConfig(20, 200);
    ConnectionConfig connectionConfig =
        new ConnectionConfig("sessionVariables=sql_mode='ANSI';characterEncoding=UTF-8");
    DataSourceConfig serviceConfig = 
        new DataSourceConfig(poolConfig, connectionConfig);
     return connectionFactory().dataSource("my-service", serviceConfig);
}

DataSource, созданная таким образом, имеет максимальный размер пула — 20, максимальное время ожидания — 200 миллисекунд, а также определенные параметры соединения.

Итог

Spring Cloud абстрагирует соединение к облачному сервису и дает возможность устанавливать одни и те же приложения на разных облачных платформах с минимальным количеством усилий. В этой статье мы слегка затронули то, что предлагает Spring Cloud. В следующей статье мы рассмотрим подробнее о Java и XML-конфигурации, а также как можно использовать основное API в не-Spring приложениях. В дальнейшем, мы будем рассматривать более подробно Spring Cloud.

comments powered by Disqus