Кирилл Толкачёв, Максим Гореликов — Spring Boot или новые методы решения новых проблем

Кирилл Толкачёв, Максим Гореликов — Spring Boot или новые методы решения новых проблем

“Магия — это наука, которую мы не успели осознать” А.Ч. Кларк

Чем отличается наука от магии — кажется, что логичностью и пониманием. Давайте попробуем еще раз посмотреть на механизмы Spring Boot, понять как это работает и что оно может нам дать.

Никакого хардкора, обычная логика и истории решения проблем, которые возникали на наших проектах при переходе на микросервисы. Как с этими проблемами справлялся Spring Boot, какие существуют альтернативные подходы и инструменты? Меняется ли Spring Boot, чтобы не отстать в вечно изменяющейся отрасли?
Все выводы субъективные, но можем обсудить☺

В докладе освещено следующее:
- самостоятелен ли Spring Boot и что это значит для разработчиков;
- как не захлебнуться при поддержке 100+ компонентов вашей системы;
- чем “умная” библиотека может быть лучше обычной и в каких случаях она может быть полезна;
- зачем вообще в рамках типовой компании, использующей Spring Boot, могут понадобиться собственные стартеры;
- что такое перетекающая сложность и как ей управлять с помощью Spring Boot, Gradle и других инструментов;
- Spring Boot, сколько можно тормозить?

Доклад рассчитан на практикующих Spring (а лучше Spring Boot) инженеров, которые уже сталкивались с различными трудностями поддержки увесистой модульной/микросервисной инфраструктуры и хотят это обсудить.

3fc5b5eb32bd3b48d7810fd67b37f9a1?s=128

Moscow JUG

June 28, 2019
Tweet

Transcript

  1. Spring Boot

  2. Spring Boot новые методы решения для новых проблем

  3. @tolkv @lavcraft @gorelikoff @gorelikov

  4. Spring Boot Как??? Зачем?! City Как тут выжить?

  5. Цели Мы пошли в микросервисы, какие проблемы ожидать и как

    их решать?
  6. Цели Мы пошли в микросервисы, какие проблемы ожидать и как

    их решать? Чем разработчику могут помочь Spring Boot и другие инструменты, и какие могут быть проблемы?
  7. Disclaimer

  8. Disclaimer Слушайте нас, но используйте свои мозги

  9. Disclaimer Слушайте нас, но используйте свои мозги Писать код не

    будем – есть доки Только опыт, логика и выводы
  10. Live. Die. REPEAT Внимание могут быть повторы , но это

    нормально
  11. План 1. Spring boot 2. Spring Boot 3. Что-то еще

    4. Перерыв 5. Spring Boot 6. Выводы
  12. А на чем вообще пишут сервисы?

  13. И как выбирают?

  14. И как выбирают? Корпоративный стандарт 1. Только Java 2. Только

    Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> От компании
  15. И как выбирают? Корпоративный стандарт 1. Только Java 2. Только

    Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> Какой стандарт? 1. Go 2. Rust 3. Unicorn 4. Смузи От компании От разработчика
  16. Ну, а если серьезно? • Обладает нужной функциональностью • Вписывается

    в нефункциональные требования • Поддержка и количество тем на stackoverflow • Знания и интересы команды
  17. Серьезно? • Обладает нужной функциональностью • Вписывается в нефункциональные требования

    • Поддержка и количество тем на stackoverflow • Знания и интересы команды
  18. Идея

  19. Выбирать по функциональности и поддержке Компетенции команды изменчивы

  20. Spring уже не магия

  21. Spring Boot все еще магия

  22. Что такое Spring Boot? Эта jar-ник c Tomcat внутри Java

    interview 2014
  23. Java interview 2017 Что такое Spring Boot? Эта хрень помогает

    проще собрать микросервис
  24. @Getter @Setter @Aspect @ToString @EnableWs @Endpoint @EnableWebMvc @EnableCaching @Configuration @RestController

    @XmlRootElement @EnableWebSocket @RedisHash("cat") @EnableScheduling @EnableWebSecurity @NoArgsConstructor @ContextConfiguration @SpringBootApplication @Accessors(chain = true) @EnableAspectJAutoProxy @EnableAutoConfiguration @EnableRedisRepositories @EnableWebSocketMessageBroker // generate getters // generate setters // we are an aspect // generate toString() // SOAP is so enterprisy, we definitely need it // Seriously, just read above // we want MVC // and we want to cache stuff // this class can configure itself // we want some REST // this component is marshallable // we want web socket, it's so new-generation // this class is an entity saved in redis // we want scheduled tasks // and some built-in security // generate no args constructor // we want context configuration for unit testing // this is a Sprint Boot application // getters/setters are chained (ala jQuery) // we want AspectJ auto proxy // and auto configuration // since it is an entity we want to enable spring data repositories for redis // we want a broker for web socket messages
  25. Но новичкам-то нравится

  26. Пока не приходит Spring Boot инквизиция

  27. Spring Boot слишком Самостоятелен © «КакойТо» team-lead

  28. Откуда 436 spring beans?

  29. Откуда 436 spring beans?

  30. Кто прав?

  31. Нельзя любить или не любить инструмент В нем нужно разбираться

  32. Кто про что, а мы про... SPRING BOOT

  33. Давайте разберёмся в “магии”

  34. Кто был на “Spring is coming”?

  35. Смотрим код Spring Boot’a

  36. @SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = {

    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { …
  37. @EnableAutoConfiguration @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration

    { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
  38. AutoConfigurationImportSelect or protected List<String> getCandidateConfigurations(...) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(

    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); ... return configurations; }
  39. spring-boot-autoconfigure.jar /spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.\ … 129

    lines
  40. @SpringBootApplication

  41. @SpringBootApplication @EnableAutoConfiguration

  42. @SpringBootApplication @EnableAutoConfiguration ImportSelector

  43. @SpringBootApplication @EnableAutoConfiguration ImportSelector SpringFactoriesLoader

  44. @SpringBootApplication @EnableAutoConfiguration ImportSelector SpringFactoriesLoader Starter#1 Starter#2 Starter#3

  45. Ваше приложение

  46. @ConditionalOnClass @ConditionalOnBean @ConditionalOnExpression @ConditionalOnProperty @ConditionalOnResource Загрузка при условии

  47. spring-boot-autoconfigure.jar /spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ... org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ ... 129 lines

  48. @ConditionalOnClass({ RabbitTemplate.class, Channel.class }) Загрузка при условии

  49. Тимлид доволен Молодца! Ничего лишнего

  50. Прошло полгода

  51. Из чего состоит сервис?

  52. Из чего состоит сервис? • API

  53. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP
  54. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config
  55. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs
  56. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs • Data • Stream
  57. Смотрим код сервиса

  58. Так это выглядит изнутри...

  59. Так это выглядит изнутри...

  60. Так это выглядит изнутри...

  61. Так это выглядит изнутри...

  62. Так это выглядит изнутри...

  63. Так это выглядит изнутри...

  64. Так это выглядит изнутри...

  65. Этим сервисам стоило бы похудеть

  66. Я не толстый! у меня кость широкая

  67. Так это выглядит изнутри...

  68. Что в этих классах

  69. Service discovery

  70. Межсервисное общение

  71. Межсервисное общение

  72. Межсервисное общение

  73. Межсервисное общение

  74. Межсервисное общение 1.0 1.0 1.0

  75. Межсервисное общение 2.0 1.0 1.0

  76. Простое общение с @FeignClient("cards-api") public interface P2PCardService { @RequestMapping(value =

    "/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); }
  77. Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override

    public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance))
  78. Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override

    public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance))
  79. Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override

    public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance))
  80. 80 Это что?!

  81. Много странного кода public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ...

    @Override public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance))
  82. В нашем сервисе есть что-то инородное

  83. Надо переносить в библиотеки

  84. Стоп! Задумаемся

  85. Стоп! Задумаемся Как конфигурировать?

  86. Конфигурация фильтра eureka: client: registryFetchIntervalSeconds: 5 serviceUrl: defaultZone: ${EUREKA_SERV:http://127.0.0.1:8763/eureka/} instance:

    virtualHostName : ${spring.application.name} metadataMap: instanceId: ${spring.application.name}:${random.value} versions: ["1.0", "2.0"] spring: discovery: filter: versions: someServiceId: 1.0
  87. Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:

    filter: versions: someServiceId: 1.0
  88. Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:

    filter: versions: someServiceId: 1.0 2.0 1.0
  89. Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:

    filter: versions: someServiceId: 1.0 1.0 2.0 1.0 2.0
  90. Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:

    filter: versions: someServiceId: 1.0
  91. Хочется видеть что-то такое spring: discovery: filter: versions: someServiceId: 1.0

    api: versions: ["1.0", "2.0"]
  92. Надо просто переложить spring: discovery: api: versions: ["1.0", "2.0"] eureka:

    client: metadataMap: versions: ["1.0", "2.0"]
  93. spring: discovery: filter: versions: someServiceId: 1.0 api: versions: ["1.0", "2.0"]

    Хочется видеть что-то такое И проверять!!!
  94. ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error

    java.lang.IllegalStateException: Version of supported API should be specified, e.g.: spring: discovery: api: versions: ["1.0", "2.0"] Run Application
  95. EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void

    postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { } translateClientVersionProperty(env); translateZuulRoutes(env); } … }
  96. @Configuration public class DiscoveryVersionFilterConfiguration { @Bean public void propertyTranslator() {

    } return new PropertyTranslatorPostProcessor(); } … } Создаём EPP
  97. Чёт жужжит, но ничего не происходит

  98. Не работает ваша магия

  99. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent
  100. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent
  101. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent
  102. запуск SpringApplicationInitializer Локальные бины приложения созданы ApplicationEnvironment PreparedEvent запуск EnvironmentPostPorcessors

    Пользовательские бины создаются слишком поздно
  103. Надо выносить в автоконфигурацию

  104. Автоконфигурация же внутри Spring?

  105. Как устроен стартер

  106. Autoconfigure

  107. Условия @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration

    { @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... }
  108. Конфигурация @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration

    { @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... }
  109. Starter

  110. spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ru.alfalab.discovery.version.DiscoveryAPIVersionFilterAutoConfiguration org.springframework.boot.env.EnvironmentPostProcessor=\ ru.alfalab.discovery.version.property.PropertyTranslatorPostProcessor

  111. Вся магия 1. @EnableAutoConfiguration 2. @ConditionalOn… 3. spring.factories

  112. Вся магия 1. @EnableAutoConfiguration 2. @ConditionalOn… 3. spring.factories Все не

    так сложно, как кажется
  113. Псс, код посмотреть хочешь?

  114. Подробнее – смотри доклад Boot Yourself, Spring is Coming https://habr.com/ru/company/jugru/blog/424503/

    https://habr.com/ru/company/jugru/blog/425333/
  115. Дело не в spring-cloud → Умное логирование → Web-фильтры с

    доп. логикой → Любые другие расширения базовых механизмов
  116. Доработка и расширение механизмов Spring должны быть в стартерах используй

    инструменты в соответствии с уже заложенными принципами
  117. Все еще много лишнего?

  118. Все равно толстый!

  119. Первый шаг к похуданию

  120. Что у них общего?

  121. Они общаются по SOAP XML

  122. SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"

    ... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message> <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service">
  123. SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"

    ... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service"> Java Stub wsdl2java tool
  124. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

  125. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

    WS Stub in Spring Context Configure bean
  126. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

    WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans>
  127. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

    WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans> Call Bean Inject ws bean
  128. SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub

    wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean
  129. SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub

    wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean
  130. CXF Stub в библиотеки

  131. CXF Stub в библиотеки dependencies { compile 'ru.alfabank.ws:currency-stub:1.0.0' }

  132. CXF Stub в библиотеки ws-stub transfers-api cards-api ...-api dependencies {

    compile 'ru.alfabank.ws:currency-stub:1.0.0' }
  133. <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"

    address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой
  134. <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"

    address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой
  135. WS – ты кто такой transactions-api – ws.xml

  136. WS – ты кто такой transactions-api – ws.xml targeting-api –

    ws.xml
  137. WS – ты кто такой transactions-api – ws.xml targeting-api –

    ws.xml transfers-api – ws.xml ...
  138. 1. Добавить зависимость (WSDL/WS-STUB) 2. Настроить бины в ws.xml 3.

    Прописать адреса и настройки для добавленных сервисов ${lb.address.base} Как сделать новый RPC вызов
  139. Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.

    Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается?
  140. Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.

    Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? Captain Copy-Paste
  141. А что нужно разработчикам?

  142. Сервис!

  143. Run Application ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup

    failed Error creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType
  144. ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error

    creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Run Application
  145. ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error

    creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Process finished with exit code 1 Run Application
  146. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

  147. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

    Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType
  148. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

    Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory WSCardsTransactions12PortType Configure Context
  149. Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory

    WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints
  150. Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory

    WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints Или падает с ошибкой если не настроен endpoint FAIL-FAST Error creating bean with name 'ru.alfabank...WSSomeServicet13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank....WSSomeServicet13PortType
  151. EPP нет, можно в обычную библиотеку?

  152. Как все это подключать?

  153. @Slf4j @Configuration @EnableConfigurationProperties @ConditionalOnProperty(name = "spring.cxf.client.enabled", matchIfMissing = true) public

    class CxfClientConfiguration { @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment environment) { return new CxfBeanDefinitionPostProcessor(environment); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } @Slf4j @Configuration @ConditionalOnClass({SpringBus.class, JaxWsClientFactoryBean.class, ConfigurationPropertiesBindingPostProcessor.class}) @EnableConfigurationProperties({CxfClientsProperties.class, WSConfiguration.class}) public static class CxfClientFactoryAutoConfiguration { @Bean(name = CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME) @ConditionalOnMissingBean(name = {CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME}) CxfWsStubBeanFactory proxyWsBeanFactory( CxfClientsProperties cxfClientsProperties, Bus bus, CxfInterceptorConfigurer interceptorConfigurer ) { return new CxfWsStubBeanFactory( cxfClientsProperties, bus, interceptorConfigurer ); } Конфигурация библиотек @Bean @ConditionalOnMissingBean(CxfBusConfigurer.class) public CxfBusConfigurer cxfBusConfigurer(CxfClientsProperties cxfClientsProperties) { return new DefaultCxfBusConfigurer(cxfClientsProperties); } @Bean(destroyMethod = "shutdown") public Bus cxf(CxfBusConfigurer cxfBusConfigurer) { SpringBus bus = new SpringBus(); cxfBusConfigurer.configure(bus); return bus; } @Bean @ConditionalOnMissingBean(CxfInterceptorConfigurer.class) public CxfInterceptorConfigurer cxfInterceptorConfigurer( CxfInterceptorAnnotationProcessor cxfInterceptorAnnotationProcessor, BeanFactory beanFactory ) { return new CxfInterceptorConfigurer( beanFactory, cxfInterceptorAnnotationProcessor.getGlobalInterceptors(), cxfInterceptorAnnotationProcessor.getSpecificInterceptors() ); } @Bean @ConditionalOnMissingBean(CxfInterceptorAnnotationProcessor.class) public static CxfInterceptorAnnotationProcessor cxfInterceptorBFPP() { return new CxfInterceptorAnnotationProcessor(); } static final String CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME = "CxfWsClientProxyFactory"; } }
  154. @Slf4j @Configuration @EnableConfigurationProperties @ConditionalOnProperty(name = "spring.cxf.client.enabled", matchIfMissing = true) public

    class CxfClientConfiguration { @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment environment) { return new CxfBeanDefinitionPostProcessor(environment); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } @Slf4j @Configuration @ConditionalOnClass({SpringBus.class, JaxWsClientFactoryBean.class, ConfigurationPropertiesBindingPostProcessor.class}) @EnableConfigurationProperties({CxfClientsProperties.class, WSConfiguration.class}) public static class CxfClientFactoryAutoConfiguration { @Bean(name = CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME) @ConditionalOnMissingBean(name = {CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME}) CxfWsStubBeanFactory proxyWsBeanFactory( CxfClientsProperties cxfClientsProperties, Bus bus, CxfInterceptorConfigurer interceptorConfigurer ) { return new CxfWsStubBeanFactory( cxfClientsProperties, bus, interceptorConfigurer ); } Конфигурация библиотек @Bean @ConditionalOnMissingBean(CxfBusConfigurer.class) public CxfBusConfigurer cxfBusConfigurer(CxfClientsProperties cxfClientsProperties) { return new DefaultCxfBusConfigurer(cxfClientsProperties); } @Bean(destroyMethod = "shutdown") public Bus cxf(CxfBusConfigurer cxfBusConfigurer) { SpringBus bus = new SpringBus(); cxfBusConfigurer.configure(bus); return bus; } @Bean @ConditionalOnMissingBean(CxfInterceptorConfigurer.class) public CxfInterceptorConfigurer cxfInterceptorConfigurer( CxfInterceptorAnnotationProcessor cxfInterceptorAnnotationProcessor, BeanFactory beanFactory ) { return new CxfInterceptorConfigurer( beanFactory, cxfInterceptorAnnotationProcessor.getGlobalInterceptors(), cxfInterceptorAnnotationProcessor.getSpecificInterceptors() ); } @Bean @ConditionalOnMissingBean(CxfInterceptorAnnotationProcessor.class) public static CxfInterceptorAnnotationProcessor cxfInterceptorBFPP() { return new CxfInterceptorAnnotationProcessor(); } static final String CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME = "CxfWsClientProxyFactory"; } }
  155. Конфигурация библиотек @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment env) { return new

    CxfBeanDefinitionPostProcessor(env); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); }
  156. Как все это подключать? • Чтение документации

  157. Как все это подключать? • Чтение документации • Debug

  158. Как все это подключать? • Чтение документации • Debug •

    Найти того, кто это писал...
  159. Есть способ быстрее?!

  160. Есть способ быстрее?!

  161. Конфигурация библиотек Или, не дай бог : @ComponentScan( { "ru.alfa.cxf",

    "ru.alfa.discovery", "ru...", "ru..." ... } )
  162. Проблемы 1. Необходимость знания кишков библиотек Капитан сложность Долой инверсию

    контроля!
  163. Проблемы Толстый сервис 1. Необходимость знания кишков библиотек 2. Лишние

    куски кода в самом сервисе 3. Случайное подхваченные бины
  164. Не в SOAP дело • Thrift/gRPC • Netty • Любая

    сложная или устаревшая внешняя зависимость
  165. Сложность конфигурации библиотеки сопоставима со сложностью логики библиотеки Даешь инверсию

    контроля не только для компонентов Spring!
  166. Умным сервисам - умные библиотеки

  167. А какие ещё стартеры бывают? • Tracing • Logging •

    Health indicators • Monitoring • Etc
  168. А какие ещё стартеры бывают? • Tracing • Logging •

    Health indicators • Monitoring • Etc • Какие-то ваши стартеры
  169. Какие-то ваши стартеры

  170. Какие-то ваши стартеры

  171. Какие-то ваши стартеры

  172. Какие ещё стартеры???

  173. Стандартного набора хватит всем! Зачем писать свое? Опытный тех. лид

  174. Стандартного набора хватит всем! Зачем писать свое? И как тестировать?

    Опытный тех. лид
  175. Э не не не. Нам нужны свои

  176. Э не не не. Нам нужны свои 1. Не для

    всего есть стартеры 2. Мы можем их тестировать
  177. Э не не не. Нам нужны свои

  178. Не все есть – ок А тестировать как

  179. • ApplicationContextRunner • @SpringBootTest • Всё остальное тестируется как обычный

    spring код Spring Boot Test
  180. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N
  181. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests
  182. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests
  183. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests
  184. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests
  185. Да чё там тестировать то Unit Тесты пиши и всё,

    да?
  186. Первое использование

  187. Failed to load ApplicationContext Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition

    with name 'ru.some.pack.client.SomeClient' defined in null: Could not resolve placeholder some.url' in value "http://${some.url}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Первое использование
  188. starter/autoconfigure — tests Unit — per class

  189. starter/autoconfigure — tests Unit — per class Component — per

    functionality/per spec
  190. Starter component tests Сформировать часть контекста нам помогут: 1. @SpringBootTest

    2. @TestConfiguration 3. @ContextConfiguration 4. ApplicationContextRunner
  191. @SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { …

    }
  192. @SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { @Requires({

    !os.isMacOs() }) def 'should apply timeout to ASYNC requests'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == SocketTimeoutException } ...
  193. @SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { ...

    @Requires({ os.isMacOs() }) def 'should apply timeout to ASYNC requests for OSX'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == ConnectException }
  194. @ContextConfiguration @ContextConfiguration(classes = ValidConfiguration) class InterceptorConfigurerTest extends Specification { @Autowired

    CxfInterceptorConfigurer interceptorConfigurer def "should add interceptors to JaxWsProxyFactoryBean"() { when: interceptorConfigurer.configure(...) then: ... } }
  195. ApplicationContextRunner new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of( PropertyPlaceholderAutoConfiguration, CxfClientAutoConfiguration )) .withUserConfiguration(ExampleCxfConfiguration.class) .withPropertyValues(*(CXF_ALL_CLIENTS_VARARG+'spring.cxf.useAnyBea nAsDefaultSslContext=true'))

  196. Псс, код посмотреть хочешь?

  197. Подробнее – смотри доклад Spring Boot Test https://www.youtube.com/watch?v=7mZqJShu_3c

  198. Мы готовы писать свой стартер! Новичок #1 Новичок #2 Новичок

    #3 Новичок #4 Странный новичок
  199. Прошло еще полгода

  200. Вася, обнови стартер Но везде сразу! Опытный тех. лид

  201. Есть три пути Суровое настоящее CI/CD путь

  202. Есть три пути Альтернативное Будущее Хакерский путь Суровое настоящее CI/CD

    путь
  203. Есть три пути Далёкое Будущее Инфраструктурный путь Альтернативное Будущее Хакерский

    путь Суровое настоящее CI/CD путь
  204. Инфраструктурный путь Далёкое будущее

  205. Далекое будущее... или не очень

  206. Далекое будущее... или не очень K U B E R

    N E T E S
  207. Далекое будущее... или не очень K U B E R

    N E T E S S I D E C A R
  208. Kubernetes Sidecar POD Application container Sidecar container

  209. Kubernetes Sidecar POD Application container Sidecar container Могут быть:

  210. Kubernetes Sidecar POD Application container Sidecar container Могут быть: •

    общая файловая система • общая сеть
  211. Kubernetes Sidecar POD APP container Logs Sidecar Proxy Sidecar Something

    Sidecar
  212. Не везде работает • Tracing покрывается не до конца -

    коннекты к БД и MQ не обслуживаются
  213. Не везде работает • Tracing покрывается не до конца -

    коннекты к БД и MQ не обслуживаются • Логи покрываются не до конца - приложение в любом случае должно выдавать структурированный лог
  214. Хакерский путь Альтернативное будущее

  215. Просто контейнеры

  216. Просто контейнеры

  217. Просто контейнеры Любой оркестратор: • Kubernetes • Mesos • Swarm

    • Bash + puppet • Что угодно запускающее контейнер на хосте
  218. Cloudfoundry buildpacks

  219. Cloudfoundry buildpacks Buildpack Scripts Filesystem droplet additional libs/starter/files

  220. Cloudfoundry buildpacks Container APP Buildpack Scripts Filesystem droplet

  221. Cloudfoundry buildpacks Container APP with classpath additional jobs

  222. Любые контейнеры Container Base Image

  223. Любые контейнеры Container Base Image additional libs/starter/files

  224. Любые контейнеры Container Base Image additional libs/starter/files java -cp"app.jar:lib/*"

  225. CI/CD путь обновления Суровое настоящее

  226. А что обновляем то?

  227. Подопытный CI/CD N сервисов

  228. Подопытный CI/CD N сервисов M стартеров

  229. Подопытный CI/CD N сервисов M стартеров

  230. Подопытный CI/CD N сервисов M стартеров N сервисов с M

    стартерами
  231. Подопытный CI/CD build rebuild rebuild rebuild rebuild rebuild rebuild

  232. rebuild rebuild rebuild rebuild rebuild rebuild Подопытный CI/CD

  233. rebuild rebuild rebuild rebuild rebuild rebuild Подопытный CI/CD Deploy ALL

  234. rebuild rebuild rebuild rebuild rebuild rebuild Подопытный CI/CD Deploy ALL

    production
  235. А какие стартеры будем вставлять

  236. Стартеры тоже бывают разные Starter#1 Starter#2 Starter#3 spring-boot-autoconfigure

  237. В чем разница? Starter#1 Starter#2 Starter#3

  238. В чем разница? Starter#1 Starter#2 Starter#3 spring-boot-autoconfigure

  239. Подход к снаряду №1 Наивный

  240. Добавляем стартеры transactions-api/build.gradle: dependencies { compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile

    'mongodb-starter:0.2.0' … }
  241. Добавляем стартеры transactions-api/build.gradle: dependencies { compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile

    'mongodb-starter:0.2.0' … } // → 'hazelcast:3.6.9' // → 'redis:1.1.1' // → 'mongodb:3.6.0' Транзитивные зависимости
  242. Добавляем стартеры Проблему видишь?

  243. Добавляем стартеры Hazelcast Cluster v2.x.x Hazelcast Client v3.x.x +--- hazelcast-starter:1.0.0

    \--- hazelcast:3.6.6
  244. Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'

    compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … }
  245. Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'

    compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0' compile 'fat-starter:100.50.0' }
  246. Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'

    compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0' compile 'fat-starter:100.50.0' }
  247. Толстеем

  248. Толстеем, Худеем

  249. Толстеем, Худеем, Толстеем

  250. Gradle transactions-api: dependencies { compile … compile … } offers-api:

    dependencies { compile … compile … } ...
  251. Gradle transactions-api: dependencies { compile … compile … } offers-api:

    dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ...
  252. Gradle transactions-api: dependencies { compile … compile … } offers-api:

    dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ... Captain Copy-Paste
  253. Все немного разные

  254. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии

  255. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'

  256. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'

    'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0'
  257. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'

    'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' Ну такое..
  258. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'

    'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' Ну такое.. Буду складывать jar`ы в VCS
  259. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  260. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  261. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  262. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  263. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  264. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  265. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0'
  266. Сложно обновлять зависимости Все немного разные

  267. Выход №1 – latest version dependencies { implementation 'some-starter:0.1.0' //

    → 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... }
  268. Астанавитесь

  269. Выход №1 – latest version + locking dependencyLocking { lockAllConfigurations()

    } dependencies { implementation 'some-starter:0.1.0' // → 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... }
  270. Выход №1 – latest version + locking $ ./gradlew build

    --write-locks
  271. $ ./gradlew build --write-locks $ ./gradlew build --update-locks ru.company:starter Выход

    №1 – latest version + locking
  272. Выход №1 – latest version + locking $ ./gradlew build

    --write-locks $ ./gradlew build --update-locks ru.company:starter $ git status modified: gradle/dependency-locks/annotationProcessor.lockfile modified: gradle/dependency-locks/buildscript-classpath.lockfile modified: gradle/dependency-locks/compileClasspath.lockfile modified: gradle/dependency-locks/runtimeClasspath.lockfile modified: gradle/dependency-locks/testAnnotationProcessor.lockfile modified: gradle/dependency-locks/testCompileClasspath.lockfile modified: gradle/dependency-locks/testRuntimeClasspath.lockfile
  273. Выход №1 – latest version + locking diff ./gradle/dependency-locks/buildscript-classpath.lockfile: -com.netflix.nebula:nebula-publishing-plugin:11.0.0

    -com.netflix.nebula:nebula-release-plugin:10.1.1 +com.netflix.nebula:nebula-publishing-plugin:12.0.0 +com.netflix.nebula:nebula-release-plugin:10.1.2
  274. rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest

    version + locking update-lock
  275. rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest

    version + locking update-lock
  276. rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest

    version + locking update-lock
  277. Пример Инф раструктура Хей, у нас новые новая система метрик!

  278. Хей, у нас новые новая система метрик! Пример Инф раструктура

    Но я не умею экспортировать метрики в таком формате
  279. Хей, у нас новые новая система метрик! Пример Но я

    не умею экспортировать метрики в таком формате Инф раструктура Ща мы тебя пересоберём
  280. Ща мы тебя пересобирём Пример Инф раструктура

  281. Ща мы тебя пересобирём Пример Инф раструктура ну ок...

  282. Ща мы тебя пересобирём Пример Инф раструктура Теперь я умею

    экспортироавть Метрики в нужном формате
  283. Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в

    нужном формате Пример Инф раструктура Ща мы тебя задеплоим!
  284. Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в

    нужном формате Пример Инф раструктура Ща мы тебя задеплоим!
  285. Выход №2 – Release Train dependencies { implementation 'my-company-uber-jar:0.1.0.RELEASE_TRAIN_0' ...

    }
  286. Выход №2 – Release Train

  287. rebuild rebuild rebuild rebuild rebuild rebuild Подопытный CI/CD update?

  288. Управляем сборкой Выход №3 – комплексный билд

  289. Управляем сборкой, полностью Выход №3 – комплексный билд

  290. build.gradle: apply plugin: "version.plugin" apply plugin: "maven.plugin" apply plugin: "check.plugin"

    apply plugin: "findbugs.plugin" apply plugin: "verify.plugin" apply plugin: "publish.plugin" apply plugin: "awesome.plugin" 290 Декларативный подход
  291. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } } } Сила в композиции 291
  292. Где достать зависимости? Application

  293. Gradle знает Gradle plugin Application

  294. Gradle знает где их искать Gradle plugin Application

  295. Немножко магии Gradle plugin Application

  296. Инверсия контроля для зависимостей ug Application Redis starter HZ starter

    Mongo starter
  297. Почему не хватает BOM?

  298. Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj

    { version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines
  299. Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj

    { version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines ugin
  300. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 300
  301. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) project.tasks.withType(SomeTaskType) { //configure } } } 301 Сила в композиции
  302. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 302

  303. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 303 Автоапдейт минорных

    версий Путь героев – но это уже другая история
  304. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 304 Автоапдейт минорных

    версий Путь героев – но это уже другая история И dependency locking тоже работает!
  305. Dependency Lock для плагинов buildscript { configurations.classpath { resolutionStrategy.activateDependencyLocking() }

    }
  306. На больших проектах нужна инверсия контроля для сборки и зависимостей

    Идея Gradle plugin
  307. И нет никаких проблем?

  308. Как это работает starter Gradle plugin Любое изменение – повод

    пересобрать всё и передеплоить dependency
  309. Как это работает build rebuild rebuild rebuild rebuild rebuild rebuild

    Обновление приводит к сборке новой версии сервисов dependency
  310. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL production И последующему деплою
  311. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL production И последующему деплою
  312. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL test,dev,prod И последующему деплою и тестированию
  313. Систему свою не сегментируй Если начал обновление – закончи его

    до конца
  314. Актуализация версий Ручное + понятно + стабильно - геморойно -

    сложно масштабировать - нет контроля запуска - ... CI/CD + единый процесс для всех + стабильно - много работы - цементирует процессы - СI/CD — 24/7 - ... Runtime + Всё из CI/CD + без пересборки + быстрый запуск + полный контроль - очень много работы - опасно - сложно тестировать - ...
  315. Вася, обнови уже этот стартер Долго обновляется Понапихают всякого Опытный

    тех. лид
  316. Сколько это займет?

  317. Сколько это займет? 100 сервисов по 3 инстанса

  318. Сколько это займет? 100 сервисов по 3 инстанса Каждый рестартует

    за ??? сек
  319. Сколько это займет? 100 X 3 X 60 = ???

  320. Прошло 5 часов

  321. Оптимизация Можно же параллели...

  322. Update strategy • Скачать и поднять новый инстанс • Дождаться

    пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс
  323. Update strategy • Скачать и поднять новый инстанс • Дождаться

    пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс
  324. Похудел и накачался, но тормоз

  325. На что тратится время при старте?

  326. На что тратится время при старте? • Старт jvm •

    Загрузка классов • ???
  327. Что делает Spring? @ComponentScan @Service @Component

  328. Что нового в Spring 5? Context Indexer

  329. Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' }

  330. Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components

    o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ...
  331. Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components

    o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Классы проаннотированные аннотацией Spring`а
  332. Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components

    o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Чем проаннотировано
  333. И результат... Чёт как-то не сильно ускорилось..

  334. Не там копаем indexer Я старался, но у тебя бинов

    не так много
  335. Радикальный путь К черту ваш Spring, у меня новый друг

    TEAM LEAD MICRONAUT Я сделан по новым принципам
  336. Новый подход MICRONAUT • AST-трансформации для Groovy • Annotation Processor

    для Java/Kotlin
  337. Новый подход MICRONAUT Micronaut делает IoC/DI в во время компиляции

    Я быстрее, потому что делаю все заранее
  338. Новый подход на Spring Spring Fu Надо просто немного изменить

    подход
  339. Новый подход на Spring Spring Fu val app = application(WebApplicationType.SERVLET)

    { logging { level = LogLevel.DEBUG } beans { bean<SampleService>() } webMvc { router { val service = ref<SampleService>() GET("/") { ok().body(service.generateMessage()) } } converters { string() jackson { indentOutput = true
  340. название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 6

    950 +- 0.04 spring boot 11 1500 +- 0.1 spark 3 350 +- 0.01 spring Fu 5 1041 +- 0.08 Сравним время старта
  341. Не похоже на реальную жизнь Зачем мне голый сервис, где

    БД/MQ/Cloud??? TEAM LEAD
  342. название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 19

    2400 spring boot 15 3400 Если с mongo
  343. название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 15

    3000 spring boot 24 8000 Если с DB/MQ/Cloud
  344. Неужели только на DI? Чего он там копается?? TEAM LEAD

  345. https://github.com/dsyer/spring-boot-allocations

  346. http://presos.dsyer.com/decks/how-fast-is-spring.html

  347. http://presos.dsyer.com/decks/how-fast-is-spring.html

  348. GraalVM - в погоне за скоростью - Собираем долго -

    Запускаем быстро
  349. Как вам такое время старта? o.s.b.Spring - Started application in

    120 ms
  350. Spring Functional TEAM LEAD Micronaut GraalVM

  351. TEAM LEAD Быстро, но как с этим жить в реальности?

  352. TEAM LEAD Собрать не так просто GraalVM

  353. Здравый смысл Куда спешим? Какая цель?

  354. Хочу serverless… наверное

  355. Хочу IoT… наверное

  356. Еще варианты?

  357. TEAM LEAD Экосистема от Grails и Spring Micronaut

  358. TEAM LEAD Incubating и старые стартеры сделаны по старому Spring

    Fu
  359. TEAM LEAD Может потюним Spring? Spring Boot?

  360. Где оптимизировать • Не забывать обновляться

  361. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями
  362. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях
  363. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer
  364. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+]
  365. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+] • Отказаться от actuator
  366. Эволюция

  367. Дзен?

  368. Spring Boot новые методы решения для новых проблем

  369. Новые решения для новых проблем Которые сами же себе и

    создаём
  370. Решения на одном уровне порождают проблемы на другом Нельзя закрыть

    проблемы всего стека на одном уровне
  371. Качественные решения требуют отличной экспертизы Лучше знаешь инструменты - порождаешь

    меньше проблем
  372. Человек — швейцарский нож Сообщество профессионалов

  373. Это и есть DevOps бизнес-код, библиотеки, сборка, обновление, логи, мониторинг,

    разбор проблем, ….
  374. Эволюция

  375. Пути в DevOps у всех разные

  376. Вопросы? @tolkv @lavcraft @gorelikoff @gorelikov