$30 off During Our Annual Pro Sale. View Details »

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

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

«Магия - это наука, которую мы не успели осознать» А.Ч. Кларк
Чем отличается наука от магии - кажется, что логичностью и пониманием. Давайте попробуем еще раз посмотреть на механизмы Spring Boot, понять как это работает и что оно может нам дать.
Никакого хардкора, обычная логика и истории решения проблем, которые возникали на наших проектах при переходе на микросервисы. Как с этими проблемами справлялся Spring Boot, какие существуют альтернативные подходы и инструменты? Меняется ли Spring Boot, чтобы не отстать в вечно изменяющейся отрасли?
Все выводы субъективные, но можем обсудить :)
В докладе освещено следующее:
самостоятелен ли Spring Boot и что это значит для разработчиков;
как не захлебнуться при поддержке 100+ компонентов вашей системы
чем “умная” библиотека может быть лучше обычной и в каких случаях она может быть полезна;
зачем вообще в рамках типовой компании, использующей Spring Boot, могут понадобиться собственные стартеры;
что такое перетекающая сложность и как ей управлять с помощью Spring Boot, Gradle и других инструментов
Spring Boot, сколько можно тормозить?
Доклад рассчитан на практикующих Spring (а лучше Spring Boot) инженеров, которые уже сталкивались с различными трудностями поддержки увесистой модульной/микросервисной инфраструктуры и хотят это обсудить.

#springboot #gradle #microservices #architecture #expirience

Kirill Tolkachev

June 28, 2019
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring Boot

    View Slide

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

    View Slide

  3. @tolkv
    @lavcraft
    @gorelikoff
    @gorelikov

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. Disclaimer

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. План
    1. Spring boot
    2. Spring Boot
    3. Что-то еще
    4. Перерыв
    5. Spring Boot
    6. Выводы

    View Slide

  12. А на чем вообще пишут сервисы?

    View Slide

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

    View Slide

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

    View Slide

  15. И как выбирают?
    Корпоративный стандарт
    1. Только Java
    2. Только Java EE
    3. Только Oracle
    4. Только
    Какой стандарт?
    1. Go
    2. Rust
    3. Unicorn
    4. Смузи
    От компании От разработчика

    View Slide

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

    View Slide

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

    View Slide

  18. Идея

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. Java interview 2017
    Что такое
    Spring Boot?
    Эта хрень
    помогает
    проще собрать
    микросервис

    View Slide

  25. @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

    View Slide

  26. Но новичкам-то нравится

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. Кто прав?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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 {

    View Slide

  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 {};
    }

    View Slide

  38. AutoConfigurationImportSelect
    or
    protected List getCandidateConfigurations(...) {
    List configurations = SpringFactoriesLoader.loadFactoryNames(
    getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    ...
    return configurations;
    }

    View Slide

  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

    View Slide

  40. @SpringBootApplication

    View Slide

  41. @SpringBootApplication
    @EnableAutoConfiguration

    View Slide

  42. @SpringBootApplication
    @EnableAutoConfiguration
    ImportSelector

    View Slide

  43. @SpringBootApplication
    @EnableAutoConfiguration
    ImportSelector
    SpringFactoriesLoader

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  53. Из чего состоит
    сервис?
    ● API
    ● Clients
    ○ JSON
    ○ SOAP

    View Slide

  54. Из чего состоит
    сервис?
    ● API
    ● Clients
    ○ JSON
    ○ SOAP
    ● Cloud
    ○ Discovery
    ○ Config

    View Slide

  55. Из чего состоит
    сервис?
    ● API
    ● Clients
    ○ JSON
    ○ SOAP
    ● Cloud
    ○ Discovery
    ○ Config
    ● Metrics
    ○ Healthcheck
    ○ Logs

    View Slide

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

    View Slide

  57. Смотрим код сервиса

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. Service discovery

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. Простое общение с
    @FeignClient("cards-api")
    public interface P2PCardService {
    @RequestMapping(value = "/", method = RequestMethod.POST)
    CardDTO loadDetachedCard(String id);
    }

    View Slide

  77. Отфильтруем друзей
    public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter {
    ...
    @Override
    public List getFilteredListOfServers(List listOfServers) {
    if (listOfServers == null || listOfServers.isEmpty()) return listOfServers;
    List infos = this.discoveryClient.getInstances(listOfServers.first()
    .getMetaInfo()
    .getServiceIdForDiscovery());
    final List 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))

    View Slide

  78. Отфильтруем друзей
    public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter {
    ...
    @Override
    public List getFilteredListOfServers(List listOfServers) {
    if (listOfServers == null || listOfServers.isEmpty()) return listOfServers;
    List infos = this.discoveryClient.getInstances(listOfServers.first()
    .getMetaInfo()
    .getServiceIdForDiscovery());
    final List 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))

    View Slide

  79. Отфильтруем друзей
    public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter {
    ...
    @Override
    public List getFilteredListOfServers(List listOfServers) {
    if (listOfServers == null || listOfServers.isEmpty()) return listOfServers;
    List infos = this.discoveryClient.getInstances(listOfServers.first()
    .getMetaInfo()
    .getServiceIdForDiscovery());
    final List 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))

    View Slide

  80. 80
    Это что?!

    View Slide

  81. Много странного кода
    public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter {
    ...
    @Override
    public List getFilteredListOfServers(List listOfServers) {
    if (listOfServers == null || listOfServers.isEmpty()) return listOfServers;
    List infos = this.discoveryClient.getInstances(listOfServers.first()
    .getMetaInfo()
    .getServiceIdForDiscovery());
    final List 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))

    View Slide

  82. В нашем сервисе есть что-то инородное

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  91. Хочется видеть что-то такое
    spring:
    discovery:
    filter:
    versions:
    someServiceId: 1.0
    api:
    versions: ["1.0", "2.0"]

    View Slide

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

    View Slide

  93. spring:
    discovery:
    filter:
    versions:
    someServiceId: 1.0
    api:
    versions: ["1.0", "2.0"]
    Хочется видеть что-то такое
    И проверять!!!

    View Slide

  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

    View Slide

  95. EnvironmentPostProcessor
    public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment env,
    SpringApplication application) { }
    translateClientVersionProperty(env);
    translateZuulRoutes(env);
    }

    }

    View Slide

  96. @Configuration
    public class DiscoveryVersionFilterConfiguration {
    @Bean
    public void propertyTranslator() { }
    return new PropertyTranslatorPostProcessor();
    }

    }
    Создаём EPP

    View Slide

  97. Чёт
    жужжит, но
    ничего не
    происходит

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  102. запуск
    SpringApplicationInitializer
    Локальные бины
    приложения созданы
    ApplicationEnvironment
    PreparedEvent
    запуск
    EnvironmentPostPorcessors
    Пользовательские
    бины создаются
    слишком поздно

    View Slide

  103. Надо выносить в
    автоконфигурацию

    View Slide

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

    View Slide

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

    View Slide

  106. Autoconfigure

    View Slide

  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);
    }
    ...
    }

    View Slide

  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);
    }
    ...
    }

    View Slide

  109. Starter

    View Slide

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

    View Slide

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

    View Slide

  112. Вся магия
    1. @EnableAutoConfiguration
    2. @ConditionalOn…
    3. spring.factories
    Все не так
    сложно, как
    кажется

    View Slide

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

    View Slide

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

    View Slide

  115. Дело не в spring-cloud
    → Умное логирование
    → Web-фильтры с доп. логикой
    → Любые другие расширения базовых механизмов

    View Slide

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

    View Slide

  117. Все еще много лишнего?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  122. SOAP Services and Apache CXF
    ServiceDefinition.wsdl
    xmlns:tns = "http://www.examples.com/wsdl/HelloService.w












    ...

    View Slide

  123. SOAP Services and Apache CXF
    ServiceDefinition.wsdl
    xmlns:tns = "http://www.examples.com/wsdl/HelloService.w











    ...

    Java Stub
    wsdl2java tool

    View Slide

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

    View Slide

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

    View Slide

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

    address="${lb.address.base}/WSCodeClickDynamic
    address="${lb.address.base}/WSClickPaymentPass
    ...
    address="${ws.address.base}/WSCustomerAddress/
    serviceClass="ru.alfalab...wscustomerinfo9.WSC
    address="${lb.address.base}/WSCustomerInfo/WSC

    View Slide

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

    address="${lb.address.base}/WSCodeClickDynamic
    address="${lb.address.base}/WSClickPaymentPass
    ...
    address="${ws.address.base}/WSCustomerAddress/
    serviceClass="ru.alfalab...wscustomerinfo9.WSC
    address="${lb.address.base}/WSCustomerInfo/WSC

    Call Bean
    Inject ws bean

    View Slide

  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

    View Slide

  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

    View Slide

  130. CXF Stub в библиотеки

    View Slide

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

    View Slide

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

    View Slide


  133. serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType"
    address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/>
    serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
    address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/>
    ...
    serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType"
    address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/>
    serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType"
    address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/>

    WS – ты кто такой

    View Slide


  134. serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType"
    address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/>
    serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
    address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/>
    ...
    serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType"
    address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/>
    serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType"
    address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/>

    WS – ты кто такой

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  141. А что нужно разработчикам?

    View Slide

  142. Сервис!

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  151. EPP нет, можно в обычную
    библиотеку?

    View Slide

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

    View Slide

  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";
    }
    }

    View Slide

  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";
    }
    }

    View Slide

  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();
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  162. Проблемы
    1. Необходимость знания кишков библиотек
    Капитан сложность
    Долой
    инверсию
    контроля!

    View Slide

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

    View Slide

  164. Не в SOAP дело
    ● Thrift/gRPC
    ● Netty
    ● Любая сложная или устаревшая внешняя зависимость

    View Slide

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

    View Slide

  166. Умным сервисам - умные библиотеки

    View Slide

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

    View Slide

  168. А какие ещё стартеры бывают?
    ● Tracing
    ● Logging
    ● Health indicators
    ● Monitoring
    ● Etc
    ● Какие-то ваши стартеры

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  175. Э не не не. Нам нужны свои

    View Slide

  176. Э не не не. Нам нужны свои
    1. Не для всего есть стартеры
    2. Мы можем их тестировать

    View Slide

  177. Э не не не. Нам нужны свои

    View Slide

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

    View Slide

  179. ● ApplicationContextRunner
    ● @SpringBootTest
    ● Всё остальное тестируется как обычный spring код
    Spring Boot Test

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  185. Да чё там тестировать то
    Unit Тесты
    пиши и всё,
    да?

    View Slide

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

    View Slide

  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}"
    Первое использование

    View Slide

  188. starter/autoconfigure — tests
    Unit — per class

    View Slide

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

    View Slide

  190. Starter component tests
    Сформировать часть контекста нам помогут:
    1. @SpringBootTest
    2. @TestConfiguration
    3. @ContextConfiguration
    4. ApplicationContextRunner

    View Slide

  191. @SpringBootTest
    @SpringBootTest(classes = [ContextPartConfiguration])
    class CxfConnectionTimeoutSpec extends Specification {

    }

    View Slide

  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
    }
    ...

    View Slide

  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
    }

    View Slide

  194. @ContextConfiguration
    @ContextConfiguration(classes = ValidConfiguration)
    class InterceptorConfigurerTest extends Specification {
    @Autowired
    CxfInterceptorConfigurer interceptorConfigurer
    def "should add interceptors to JaxWsProxyFactoryBean"() {
    when:
    interceptorConfigurer.configure(...)
    then:
    ...
    }
    }

    View Slide

  195. ApplicationContextRunner
    new ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(
    PropertyPlaceholderAutoConfiguration,
    CxfClientAutoConfiguration
    ))
    .withUserConfiguration(ExampleCxfConfiguration.class)
    .withPropertyValues(*(CXF_ALL_CLIENTS_VARARG+'spring.cxf.useAnyBea
    nAsDefaultSslContext=true'))

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  199. Прошло еще полгода

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  204. Инфраструктурный путь
    Далёкое будущее

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  208. Kubernetes Sidecar
    POD
    Application
    container
    Sidecar
    container

    View Slide

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

    View Slide

  210. Kubernetes Sidecar
    POD
    Application
    container
    Sidecar
    container
    Могут быть:
    ● общая файловая
    система
    ● общая сеть

    View Slide

  211. Kubernetes Sidecar
    POD
    APP
    container
    Logs
    Sidecar
    Proxy
    Sidecar
    Something
    Sidecar

    View Slide

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

    View Slide

  213. Не везде работает
    ● Tracing покрывается не до конца - коннекты к БД и MQ не
    обслуживаются
    ● Логи покрываются не до конца - приложение в любом случае должно
    выдавать структурированный лог

    View Slide

  214. Хакерский путь
    Альтернативное будущее

    View Slide

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

    View Slide

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

    View Slide

  217. Просто контейнеры
    Любой оркестратор:
    ● Kubernetes
    ● Mesos
    ● Swarm
    ● Bash + puppet
    ● Что угодно
    запускающее
    контейнер на хосте

    View Slide

  218. Cloudfoundry buildpacks

    View Slide

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

    View Slide

  220. Cloudfoundry buildpacks
    Container APP
    Buildpack
    Scripts
    Filesystem
    droplet

    View Slide

  221. Cloudfoundry buildpacks
    Container
    APP
    with
    classpath
    additional
    jobs

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    }

    View Slide

  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'
    Транзитивные
    зависимости

    View Slide

  242. Добавляем стартеры
    Проблему
    видишь?

    View Slide

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

    View Slide

  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'

    }

    View Slide

  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'
    }

    View Slide

  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'
    }

    View Slide

  247. Толстеем

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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 …
    }
    ...

    View Slide

  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

    View Slide

  253. Все немного разные

    View Slide

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

    View Slide

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

    View Slide

  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'

    View Slide

  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'
    Ну такое..

    View Slide

  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

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  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'

    View Slide

  266. Сложно обновлять зависимости
    Все немного разные

    View Slide

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

    View Slide

  268. Астанавитесь

    View Slide

  269. Выход №1 – latest version + locking
    dependencyLocking {
    lockAllConfigurations()
    }
    dependencies {
    implementation 'some-starter:0.1.0' // → 'some-starter:+'
    implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+'
    ...
    }

    View Slide

  270. Выход №1 – latest version + locking
    $ ./gradlew build --write-locks

    View Slide

  271. $ ./gradlew build --write-locks
    $ ./gradlew build --update-locks ru.company:starter
    Выход №1 – latest version + locking

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  285. Выход №2 – Release Train
    dependencies {
    implementation 'my-company-uber-jar:0.1.0.RELEASE_TRAIN_0'
    ...
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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
    Декларативный подход

    View Slide


  291. class MyPlugin implements Plugin {
    void apply(Project project) {
    project.configure {
    dependencies {
    compile …
    compile …
    }
    }
    }
    }
    Сила в композиции
    291

    View Slide

  292. Где достать зависимости?
    Application

    View Slide

  293. Gradle знает
    Gradle plugin
    Application

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide


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

    View Slide


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

    View Slide

  302. Сила в композиции
    apply plugin: "your.plugin.all" //2.1.+
    302

    View Slide

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

    View Slide

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

    View Slide

  305. Dependency Lock для плагинов
    buildscript {
    configurations.classpath {
    resolutionStrategy.activateDependencyLocking()
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  312. rebuild
    rebuild
    rebuild
    rebuild
    rebuild
    rebuild
    Как это работает
    Deploy ALL
    test,dev,prod
    И последующему
    деплою и
    тестированию

    View Slide

  313. Систему свою не сегментируй
    Если начал обновление – закончи его до конца

    View Slide

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

    View Slide

  315. Вася, обнови уже этот
    стартер
    Долго обновляется
    Понапихают всякого
    Опытный тех. лид

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  324. Похудел и накачался, но тормоз

    View Slide

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

    View Slide

  326. На что тратится время при старте?
    ● Старт jvm
    ● Загрузка классов
    ● ???

    View Slide

  327. Что делает Spring?
    @ComponentScan
    @Service
    @Component

    View Slide

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

    View Slide

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

    View Slide

  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
    ...

    View Slide

  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`а

    View Slide

  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
    ...
    Чем проаннотировано

    View Slide

  333. И результат...
    Чёт как-то не
    сильно
    ускорилось..

    View Slide

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

    View Slide

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

    View Slide

  336. Новый подход
    MICRONAUT
    ● AST-трансформации для Groovy
    ● Annotation Processor для Java/Kotlin

    View Slide

  337. Новый подход
    MICRONAUT
    Micronaut делает IoC/DI в во время
    компиляции
    Я быстрее, потому
    что делаю все
    заранее

    View Slide

  338. Новый подход на Spring
    Spring Fu
    Надо просто
    немного изменить
    подход

    View Slide

  339. Новый подход на Spring
    Spring Fu
    val app = application(WebApplicationType.SERVLET) {
    logging {
    level = LogLevel.DEBUG
    }
    beans {
    bean()
    }
    webMvc {
    router {
    val service = ref()
    GET("/") {
    ok().body(service.generateMessage())
    }
    }
    converters {
    string()
    jackson {
    indentOutput = true

    View Slide

  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
    Сравним время старта

    View Slide

  341. Не похоже на реальную жизнь
    Зачем мне голый
    сервис,
    где БД/MQ/Cloud???
    TEAM LEAD

    View Slide

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

    View Slide

  343. название →__
    условие →__
    -Xmx/mb время старта/ms
    -Xmx=enough
    micronaut 15 3000
    spring boot 24 8000
    Если с DB/MQ/Cloud

    View Slide

  344. Неужели только на DI?
    Чего он там копается??
    TEAM LEAD

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  349. Как вам такое время старта?
    o.s.b.Spring - Started application in 120 ms

    View Slide

  350. Spring
    Functional
    TEAM LEAD
    Micronaut
    GraalVM

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  366. Эволюция

    View Slide

  367. Дзен?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  374. Эволюция

    View Slide

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

    View Slide

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

    View Slide