Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring Boot И Не Только

Spring Boot И Не Только

В программе
* spring boot
* способы его расширения и заточки под нужды компании
* сложности возникающие при масштабировании подходов работы с микросервисами
* подходы к их решению с помощью spring boot и gradle
* немного о том как работать в команде и к чему нужно стремиться

Учитесь, развивайтесь, ходите на конференции :)

Промокоды на конференции в 2020 году:
https://jpoint.ru/ – JavaMentorJpointPC
http://heisenbug.ru/ – JavaMentorHeisenbugPC

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

March 01, 2020
Tweet

Transcript

  1. Spring Boot

  2. @tolkv @lavcraft 2

  3. Особая благодарность @gorelikoff @gorelikov 3

  4. В конце полезняшки — ??? — ??? 4

  5. Начнём? Господин Best Practice Господин «Аастанавись, Задумайся» Господин тимлид –

    он за контроль Господин «№;$?% Магия…» Господин, который просто классно шутит 5
  6. Spring Boot новые методы решения для новых проблем

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    проще собрать микросервис 27
  28. @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 28
  29. Но новичкам-то нравится 29

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

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

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

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

  34. Кто прав? 34

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

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

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

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

  39. @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 { … 39
  40. @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 {}; } 40
  41. AutoConfigurationImportSelect or protected List<String> getCandidateConfigurations(...) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(

    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); ... return configurations; } 41
  42. 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 42
  43. @SpringBootApplication 43

  44. @SpringBootApplication @EnableAutoConfiguration 44

  45. @SpringBootApplication @EnableAutoConfiguration ImportSelector 45

  46. @SpringBootApplication @EnableAutoConfiguration ImportSelector SpringFactoriesLoader 46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  72. Service discovery 72

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

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

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

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

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

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

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

    "/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); } 79
  80. Отфильтруем друзей 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
  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)) 81
  82. Отфильтруем друзей 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. 83 Это что?!

  84. Много странного кода 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)) 84
  85. В нашем сервисе есть что-то инородное 85

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

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

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

  89. Конфигурация фильтра 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 89
  90. Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:

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

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

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

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

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

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

    Хочется видеть что-то такое И проверять!!! 96
  97. 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 97
  98. EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void

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

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

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

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

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

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

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

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

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

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

  109. Autoconfigure 109

  110. Условия @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); } ... } 110
  111. Конфигурация @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); } ... } 111
  112. Starter 112

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

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

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

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

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

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

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

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

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

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

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

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

  125. 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"> 125
  126. 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 126
  127. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

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

    WS Stub in Spring Context Configure bean 128
  129. 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> 129
  130. 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 130
  131. 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 131
  132. 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 132
  133. CXF Stub в библиотеки 133

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

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

    compile 'ru.alfabank.ws:currency-stub:1.0.0' } 135
  136. <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 – ты кто такой 136
  137. <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 – ты кто такой 137
  138. WS – ты кто такой transactions-api – ws.xml 138

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

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

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

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

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

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

  145. Сервис! 145

  146. 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 146
  147. 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 147
  148. 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 148
  149. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

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

    Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType 150
  151. Автоматизируем? 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 151
  152. Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory

    WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints 152
  153. Автоматизируем? 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 153
  154. EPP нет, можно в обычную библиотеку? 154

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

  156. @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"; } } 156
  157. @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"; } } 157
  158. Конфигурация библиотек @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(); } 158
  159. Как все это подключать? • Чтение документации 159

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  190. 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}" Первое использование 190
  191. starter/autoconfigure — tests Unit — per class 191

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

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

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

    } 194
  195. @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 } ... 195
  196. @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 } 196
  197. @ContextConfiguration @ContextConfiguration(classes = ValidConfiguration) class InterceptorConfigurerTest extends Specification { @Autowired

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  223. Cloudfoundry buildpacks Container APP Buildpack Scripts Filesystem droplet 223

  224. Cloudfoundry buildpacks Container APP with classpath additional jobs 224

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    'mongodb-starter:0.2.0' … } 243
  244. Добавляем стартеры 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' Транзитивные зависимости 244
  245. Добавляем стартеры Проблему видишь? 245

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

    \--- hazelcast:3.6.6 246
  247. 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' … } 247
  248. 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' } 248
  249. 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' } 249
  250. Толстеем 250

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

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

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

    dependencies { compile … compile … } ... 253
  254. 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 … } ... 254
  255. 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 255
  256. Все немного разные 256

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

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

  259. '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' 259
  260. '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' Ну такое.. 260
  261. '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 261
  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' 262
  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' 263
  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' 264
  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' 265
  266. Все немного разные '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. Все немного разные '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' 267
  268. Все немного разные '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' 268
  269. Сложно обновлять зависимости Все немного разные 269

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

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

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

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

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

    №1 – latest version + locking 274
  275. Выход №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 275
  276. Выход №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 276
  277. rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest

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

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

    version + locking update-lock 279
  280. – Но это нужно Тестить! 280

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

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

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

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

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

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

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

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

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

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

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

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

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

  294. 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" 294 Декларативный подход
  295. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

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

  297. Gradle знает Gradle plugin Application 297

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

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

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

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

  302. Почему не хватает 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 302
  303. Почему не хватает 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 303
  304. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 304
  305. • 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 } } } 305 Сила в композиции
  306. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 306

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    323
  324. Прошло 5 часов 324

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

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

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

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

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

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

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

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

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

  334. Просто добавь зависимость 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 ... 334
  335. Просто добавь зависимость 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`а 335
  336. Просто добавь зависимость 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 ... Чем проаннотировано 336
  337. И результат... Чёт как-то не сильно ускорилось.. 337

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

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

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

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

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

    подход 342
  343. Новый подход на 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 343
  344. название →__ условие →__ -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 Сравним время старта 344
  345. Не похоже на реальную жизнь Зачем мне голый сервис, где

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

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

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

    348
  349. https://github.com/dsyer/spring-boot-allocations 349

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

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

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

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

    120 ms 353
  354. Spring Functional TEAM LEAD Micronaut GraalVM 354

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  371. Дзен?

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

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

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

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

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

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

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

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

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

  381. Немного полезностей — JavaMentorJpointPC — JavaMentorHeisenbugPC 381 2020