Первоначально написано Полом Чепменом для весеннего блога
Микросервисы позволяют создавать большие системы из нескольких взаимодействующих компонентов. Это делает на уровне процессов то, что Spring всегда делал на уровне компонентов: слабосвязанные процессы вместо слабосвязанных компонентов.
Например, представьте интернет-магазин с отдельными микросервисами для учетных записей пользователей, обработки заказов в каталоге товаров и корзинами для покупок:
Неизбежно существует ряд движущихся частей, которые необходимо настроить и настроить для построения такой системы. Как заставить их работать вместе, не очевидно — вам нужно хорошо ознакомиться с Spring Boot, поскольку Spring Cloud активно его использует, требуется несколько проектов Netflix или других OSS и, конечно, есть некоторая «магия» конфигурации Spring!
В этой статье я попытаюсь прояснить, как все работает, создавая простейшую возможную систему шаг за шагом. Поэтому я буду реализовывать только небольшую часть большой системы — службу учетных записей пользователей.
Web-приложения будут делать запросы на Счет-Сервис microservice с использованием RESTful API. Нам также необходимо добавить службу обнаружения — чтобы другие процессы могли находить друг друга.
Код для этого приложения находится здесь: https://github.com/paulc4/microservices-demo .
Продолжение 1: Другие ресурсы
В этой статье обсуждается только минимальная система. Для получения дополнительной информации вы можете прочитать статью в блоге Джоша Лонга « Регистрация и обнаружение микросервисов с помощью Spring Cloud» и «Eureka» от Netflix, в которой рассказывается о работе полной системы микросервисов в Cloud Foundry.
Проекты Spring Cloud находятся здесь .
Follow Up 2: SpringOne 2GX 2015
Забронируйте свое место в SpringOne2GX в Вашингтоне, округ Колумбия, в ближайшее время — просто лучшая возможность узнать из первых рук обо всем, что происходит, и предоставить прямой отзыв. В приложениях Spring Cloud и Cloud Native будет целый трек.
ОК, начнем …
Служба регистрации
Когда у вас есть несколько процессов, работающих вместе, они должны найти друг друга. Если вы когда-либо использовали механизм RMI в Java, вы можете вспомнить, что он опирался на центральный реестр, чтобы процессы RMI могли находить друг друга. Микросервисы имеют такое же требование.
Разработчики в Netflix столкнулись с этой проблемой при создании своих систем и создали сервер регистрации под названием Eureka («Я нашел это» на греческом языке). К счастью для нас, они сделали свой сервер обнаружения открытым исходным кодом, и Spring включил его в Spring Cloud, что еще больше упростило запуск сервера Eureka. Вот полное приложение:
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistrationServer {
public static void main(String[] args) {
// Tell Boot to look for registration-server.yml
System.setProperty("spring.config.name", "registration-server");
SpringApplication.run(ServiceRegistrationServer.class, args);
}
}
Это действительно так просто!
Spring Cloud построен на основе Spring Boot и использует POM для родителей и начинающих. Важными частями POM являются:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Angel.SR3</version> <!-- Name of release train -->
</parent>
<dependencies>
<dependency>
<!-- Setup Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<!-- Setup Spring MVC & REST, use Embedded Tomcat -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- Spring Cloud starter -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<!-- Eureka for service registration -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
Примечание: Angel.SR3 — это текущий «выпуск релизов» — набор скоординированных релизов — см. Примечание на домашней странице Spring Cloud (прокрутите вниз до второго раздела).
По умолчанию приложения Spring Boot ищут файл application.properties
или application.yml
для конфигурации. Установив spring.config.name
свойство, мы можем указать Spring Boot искать другой файл — это полезно, если у вас есть несколько приложений Spring Boot в одном проекте — как я сделаю в ближайшее время.
Это приложение ищет registration-server.properties
или registration-server.yml
. Вот соответствующая конфигурация от registration-server.yml
:
# Configure this Discovery Server
eureka:
instance:
hostname: localhost
client: # Not a client, don't register with yourself
registerWithEureka: false
fetchRegistry: false
server:
port: 1111 # HTTP (Tomcat) port
По умолчанию Eureka работает на порту 8761, но здесь мы будем использовать порт 1111
. Также, включив регистрационный код в мой процесс, я могу быть сервером или клиентом. Конфигурация указывает, что я не являюсь клиентом, и останавливает процесс сервера, пытающийся зарегистрироваться сам.
Используя Консул
Spring Cloud также поддерживает Consul в качестве альтернативы Eureka. Вы запускаете Консул-агент (его сервер регистрации) с помощью скрипта, а затем клиенты используют его для поиска своих микросервисов. Подробности смотрите в этой статье блога или домашней странице проекта .
Попробуйте запустить сервер регистрации (см. Справку по запуску приложения). Вы можете открыть панель инструментов Eureka здесь: http: // localhost: 1111, и раздел, показывающий Приложения, будет пустым.
С этого момента мы будем ссылаться на сервер обнаружения, поскольку это может быть Eureka или Consul (см. Боковую панель).
Создание микросервиса: Account-Service
Микросервис — это самостоятельный процесс, который обрабатывает четко определенные требования.
При настройке приложений с помощью Spring мы подчеркиваем «Свободное соединение» и «Tight Cohesion». Это не новые концепции (Ларри Константину приписывают их первое определение в конце 1960-х — ссылка ), но теперь мы применяем их, а не к взаимодействующим компонентам (Spring Beans), но для взаимодействующих процессов.
В этом примере у меня есть простой микросервис управления учетными записями, который использует Spring Data для реализации JPA AccountRepository
и Spring REST для предоставления RESTful-интерфейса к информации об учетной записи. В большинстве случаев это простое приложение Spring Boot.
Что делает его особенным, так это то, что он регистрируется на сервере обнаружения при запуске. Вот класс запуска Spring Boot:
@EnableAutoConfiguration
@EnableDiscoveryClient
@Import(AccountsWebApplication.class)
public class AccountsServer {
@Autowired
AccountRepository accountRepository;
public static void main(String[] args) {
// Will configure using accounts-server.yml
System.setProperty("spring.config.name", "accounts-server");
SpringApplication.run(AccountsServer.class, args);
}
}
Аннотации делают работу:
@EnableAutoConfiguration
— определяет это как приложение Spring Boot.@EnableDiscoveryClient
— это позволяет регистрировать и обнаруживать услуги. В этом случае этот процесс регистрируется в службе discovery-server с использованием имени приложения (см. Ниже).@Import(AccountsWebApplication.class)
— этот класс конфигурации Java устанавливает все остальное (см. более подробно).
То, что делает это микросервисом, — это регистрация через discovery-сервер через, @EnableDiscoveryClient
и его конфигурация YML завершает настройку:
# Spring properties
spring:
application:
name: accounts-service
# Discovery Server Access
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
# HTTP Server
server:
port: 2222 # HTTP (Tomcat) port
Обратите внимание, что этот файл
- Устанавливает имя приложения как
accounts-service
. Эта служба регистрируется под этим именем и также может быть доступна по этому имени — см. Ниже. - Указывает настраиваемый порт для прослушивания (2222). Все мои процессы используют Tomcat, они не могут все слушать порт 8080.
- URL-адрес процесса обслуживания Eureka — из предыдущего раздела.
Запустите приложение AccountsService сейчас и дайте ему завершить инициализацию. Обновите панель мониторинга http: // localhost: 1111, и вы должны увидеть СЧЕТ-СЕРВИС, перечисленный в разделе Приложения. Иногда регистрация может занять 10-20 секунд, поэтому наберитесь терпения — проверьте вывод журнала в RegistrationService
Предупреждение. Не пытайтесь отображать вывод XML с помощью внутреннего средства просмотра веб-страниц Eclipse / STS, потому что это невозможно. Вместо этого используйте ваш любимый веб-браузер.
Для более подробной информации, перейдите сюда: http: // localhost: 1111 / eureka / apps /, и вы должны увидеть что-то вроде этого:
<applications>
<versions__delta>1</versions__delta>
<apps__hashcode>UP_1_</apps__hashcode>
<application>
<name>ACCOUNTS-SERVICE</name>
<instance>
<hostName>autgchapmp1m1.corp.emc.com</hostName>
<app>ACCOUNTS-SERVICE</app>
<ipAddr>172.16.84.1</ipAddr><status>UP</status>
<overriddenstatus>UNKNOWN</overriddenstatus>
<port enabled="true">3344</port>
<securePort enabled="false">443</securePort>
...
</instance>
</application>
</applications>
В качестве альтернативы перейдите на http: // localhost: 1111 / eureka / apps / ACCOUNTS-SERVICE и посмотрите только детали для AccountsService — если он не зарегистрирован, вы получите 404.
Доступ к микросервису: веб-сервис
Spring использует RestTemplate
класс RESTful . Это позволяет отправлять HTTP-запросы на сервер RESTful и получать данные в различных форматах, таких как JSON и XML.
Примечание. Микросервис Accounts предоставляет интерфейс RESTful через HTTP, но можно использовать любой подходящий протокол. Обмен сообщениями с использованием AMQP или JMS является очевидной альтернативой.
Какие форматы могут быть использованы, зависит от наличия классов маршалинга на пути к классам — например, JAXB всегда определяется, поскольку это стандартная часть Java. JSON поддерживается, если в пути к классам присутствуют фляги Джексона.
Микросервисный (обнаружительный) клиент может использовать a, RestTemplate
и Spring автоматически настроит его для поддержки микросервиса (подробнее об этом через минуту).
Инкапсуляция микросервисного доступа
Вот часть WebAccountService
моего клиентского приложения:
@Service
public class WebAccountsService {
@Autowired // Created automatically by Spring Cloud
@LoadBalanced
protected RestTemplate restTemplate;
protected String serviceUrl;
public WebAccountsService(String serviceUrl) {
this.serviceUrl = serviceUrl.startsWith("http") ?
serviceUrl : "http://" + serviceUrl;
}
public Account getByNumber(String accountNumber) {
Account account = restTemplate.getForObject(serviceUrl
+ "/acounts/{number}", Account.class, accountNumber);
if (account == null)
throw new AccountNotFoundException(accountNumber);
else
return account;
}
...
}
Обратите внимание, что my WebAccountService
— это просто оболочка для RestTemplate, извлекающая данные из микросервиса. Интересные части serviceUrl
и RestTemplate
.
Доступ к микросервису
serviceUrl
Обеспечивается основной программе на WebAccountController
который , в свою очередь , передает ее к WebAccountService
(как показано выше):
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan(useDefaultFilters=false) // Disable component scanner
public class WebServer {
public static void main(String[] args) {
// Will configure using web-server.yml
System.setProperty("spring.config.name", "web-server");
SpringApplication.run(WebServer.class, args);
}
@Bean
public WebAccountsController accountsController() {
// 1. Value should not be hard-coded, just to keep things simple
// in this example.
// 2. Case insensitive: could also use: http://accounts-service
return new WebAccountsController
("http://ACCOUNTS-SERVICE"); // serviceUrl
}
}
Несколько замечаний:
WebController
Типичный Spring MVC вид на основе контроллера возвращения HTML. Приложение использует Thymeleaf в качестве технологии просмотра (для генерации динамического HTML)WebServer
также является,@EnableDiscoveryClient
но в этом случае, а также регистрируя себя на сервере обнаружения (который не является необходимым, поскольку он не предлагает своих собственных услуг), он использует Eureka для поиска службы учетной записи.- Установка компонента-сканера по умолчанию, унаследованная от Spring Boot, ищет
@Component
классы, и в этом случае находит myWebAccountController
и пытается его создать. Однако я хочу создать его сам, поэтому отключаю сканер следующим образом@ComponentScan(useDefaultFilters=false)
. - Сервис URL я передаю в
WebAccountController
этом имени служба используется для регистрации себя с открытием-сервером по умолчанию — это то же самое , какspring.application.name
для процесса , которыйaccount-service
— смaccount-service.yml
выше. Использование верхнего регистра не обязательно, но это помогает подчеркнуть, что ACCOUNTS-SERVICE является логическим хостом (который будет получен с помощью обнаружения), а не фактическим хостом.
Сбалансированный RestTemplate
Spring Cloud автоматически настроил RestTemplate для использования пользовательского интерфейса, HttpRequestClient
который использует ленту Netflix для поиска микро-службы. Лента также является балансировщиком нагрузки, поэтому, если у вас есть несколько доступных экземпляров службы, она выбирает один для вас. (Ни Eureka, ни Consul самостоятельно не выполняют балансировку нагрузки, поэтому вместо этого мы используем Ribbon).
Если вы посмотрите в RibbonClientHttpRequestFactory, вы увидите этот код:
String serviceId = originalUri.getHost();
ServiceInstance instance =
loadBalancer.choose(serviceId); // loadBalancer uses Ribbon
... if instance non-null (service exists) ...
URI uri = loadBalancer.reconstructURI(instance, originalUri);
Он loadBalancer
берет логическое имя службы (зарегистрированное на сервере обнаружения ) и преобразует его в фактическое имя хоста выбранного микросервиса.
RestTemplate
Экземпляр потокобезопасен и может быть использован для доступа к любому числу услуг в различных частях приложения (например, я мог бы иметь CustomerService
оберточный тот же RestTemplate
экземпляр с доступом к microservice данных клиентов).
конфигурация
Ниже соответствующая конфигурация от web-server.yml
. Он используется для:
- Установите имя приложения
- Определите URL для доступа к серверу обнаружения
- Установите порт Tomcat на 3333
# Spring Properties
spring:
application:
name: web-service
# Discovery Server Access
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
# HTTP Server
server:
port: 3333 # HTTP (Tomcat) port
Как запустить демо
Небольшая демонстрация этой системы находится по адресу http://github.com/paulc4/microservices-demo . Клонируйте его и загрузите в свою любимую IDE или используйте maven напрямую. Предложения по запуску демо-версии включены в README на домашней странице проекта.
Дополнительные заметки
Некоторые заметки об использовании Spring Boot этими приложениями. Если вы не знакомы с Spring Boot, это объясняет некоторую «магию»!
Просмотр шаблонизаторов
Панель инструментов Eureka (внутри RegistrationServer
) реализована с использованием шаблонов FreeMarker, но два других приложения используют Thymeleaf. Чтобы убедиться, что каждый из них использует правильный механизм просмотра, в каждом файле YML есть дополнительная конфигурация.
Это в конце, registration-server.yml
чтобы отключить Thymeleaf.
...
# Discovery Server Dashboard uses FreeMarker. Don't want Thymeleaf templates
spring:
thymeleaf:
enabled: false # Disable Thymeleaf spring:
Так как оба AccountService
и WebService
используют тимелеф, нам также нужно указать каждому на свои шаблоны. Вот часть account-server.yml
:
# Spring properties
spring:
application:
name: accounts-service # Service registers under this name
freemarker:
enabled: false # Ignore Eureka dashboard FreeMarker templates
thymeleaf:
cache: false # Allow Thymeleaf templates to be reloaded at runtime
prefix: classpath:/accounts-server/templates/
# Template location for this application only
...
web-server.yml
похож, но его шаблоны определяются
prefix: classpath:/web-server/templates/
Обратите внимание на / в конце каждого spring.thymeleaf.prefix
пути к классам — это очень важно .
Выполнение командной строки
Jar компилируется для автоматического запуска io.pivotal.microservices.services.Main
при вызове из командной строки — см. Main.java .
Параметр Spring Boot для установки start-class
можно увидеть в POM :
<properties>
<!-- Stand-alone RESTFul application for testing only -->
<start-class>io.pivotal.microservices.services.Main</start-class>
</properties>
Настройка AccountsWebApplication
@SpringBootApplication
@EntityScan("io.pivotal.microservices.accounts")
@EnableJpaRepositories("io.pivotal.microservices.accounts")
@PropertySource("classpath:db-config.properties")
public class AccountsWebApplication {
...
}
Это основной класс конфигурации для AccountService и является классическим приложением Spring Boot, использующим Spring Data. Аннотации выполняют большую часть работы:
@SpringBootApplication
— определяет это как приложение Spring Boot. Эта удобная аннотация объединяет@EnableAutoConfiguration
,@Configuration
и@ComponentScan
(что, по умолчанию, заставляет Spring искать пакет, содержащий этот класс, и его подпакеты, для компонентов — потенциальных Spring Beans:AccountController
иAccountRepository
).@EntityScan("io.pivotal.microservices.accounts")
— потому что я использую JPA, мне нужно указать, где@Entity
находятся классы. Обычно это опция, которую вы указываете в JPApersistence.xml
или при созданииLocalContainerEntityManagerFactoryBean
. Spring Boot создаст этот фабричный компонент для меня, потому чтоspring-boot-starter-data-jpa
зависимость находится на пути к классам. Таким образом, альтернативный способ указать, где найти@Entity
классы — использовать@EntityScan
. Это найдетAccount
.@EnableJpaRepositories("io.pivotal.microservices.accounts")
— искать классы, расширяющиеRepository
интерфейс маркеров Spring Data, и автоматически реализовывать их с помощью JPA — см. Spring Data JPA .@PropertySource("classpath:db-config.properties")
— свойства, чтобы настроить мойDataSource
— см. db-config.properties .
Обратите внимание, что AccountsWebApplication может быть запущен как отдельное приложение само по себе, что я нашел полезным для тестирования. Он слушает порт Tomcat по умолчанию: 8080, поэтому домашней страницей является http: // localhost: 8080 .
Настройка свойств
Как уже упоминалось выше, приложения Spring Boot ищут application.properties
или application.yml
настраивают сами. Поскольку все три сервера, используемые в этом приложении, находятся в одном проекте, они будут автоматически использовать одну и ту же конфигурацию.
Чтобы избежать этого, каждый указывает альтернативный файл, устанавливая spring.config.name
свойство.
Например, вот часть WebServer.java
.
public static void main(String[] args) {
// Tell server to look for web-server.properties or web-server.yml
System.setProperty("spring.config.name", "web-server");
SpringApplication.run(WebServer.class, args);
}
Во время выполнения приложение найдет и использует web-server.yml
в src/main/resources
.
логирование
Spring Boot по умолчанию настраивает ведение журнала уровня INFO для Spring. Так как нам нужно проверять журналы на наличие работоспособности наших микросервисов, я поднял уровень до WARN, чтобы уменьшить количество журналирования.
Для этого необходимо указать уровень ведения журнала в каждом из xxxx-server.yml
файлов конфигурации. Обычно это лучшее место для их определения, поскольку в файлах свойств нельзя указывать свойства ведения журнала (ведение журнала уже было инициализировано до обработки директив @PropertySource). Об этом есть примечание в руководстве по Spring Boot, но его легко пропустить.
Вместо того, чтобы дублировать конфигурацию ведения журнала в каждом файле YAML, я вместо этого решил поместить ее в файл конфигурации возврата, так как Spring Boot использует logback — см. Src / main / resources / logback.xml . Все три сервиса будут делиться одинаково logback.xml
.