Первоначально написано Полом Чепменом для весеннего блога
Микросервисы позволяют создавать большие системы из нескольких взаимодействующих компонентов. Это делает на уровне процессов то, что 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.



