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

Spring Boot And Cloud Ripper June

Spring Boot And Cloud Ripper June

Kirill Tolkachev

June 15, 2019
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring {Boot, Cloud}
    Workshop

    View Slide

  2. Часть 1
    Основы микросервисописания

    View Slide

  3. Как будет проходить тренинг?
    Теория – мы рассказываем вы слушаете

    View Slide

  4. Как будет проходить тренинг?
    Практика

    View Slide

  5. All your classes
    All your pages and xmls
    Regular web.xml with spring dispatcher
    servlet, spring listener and security filter name
    Security filters, users
    and roles
    Spring MVC beans
    Application beans,
    Import security.xml
    The project structure

    View Slide

  6. web.xml

    org.springframework.web.context.ContextLoaderListener


    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy


    springSecurityFilterChain
    /*


    mvc-dispatcher
    org.springframework.web.servlet.DispatcherServlet
    1


    mvc-dispatcher
    /

    View Slide

  7. View Slide

  8. View Slide



  9. contextClass param-name>
    …AnnotationConfigWebApplicationContext param-value>


    contextConfigLocation param-name>
    com.inwhite.conf.AppConfig param-value>


    View Slide

  10. Deployment
    →Use maven/gradle to build a war from that project
    →Copy it to webapps directory of tomcat

    View Slide

  11. How does it work?
    User type in his browser: http://localhost:8080/myApp
    create Spring
    ContextLoaderListener
    Start tomcat
    start applicationContext
    In bootsrap
    In bootsrap
    Create filter
    mapping
    Create
    security filter
    login
    Create spring
    dispatcher
    servlet
    Delegate to
    Controller

    View Slide

  12. Теперь давайте замочим XML

    View Slide

  13. Жизнь без web.xml
    false
    Java Servlet Specification 3.+
    Tomcat 7+

    View Slide

  14. Что будем имплементировать
    вместо web.xml?

    View Slide

  15. WebApplicationInitializer

    View Slide

  16. ServletContainerInitializer

    View Slide

  17. WebApplicationInitializer

    View Slide

  18. ServletContainerInitializer

    View Slide

  19. View Slide

  20. § 8.2.4 Shared libraries / runtimes pluggability
    The ServletContainerInitializer class is looked up via
    the jar services API. For each application, an instance
    of the ServletContainerInitializer is created by the
    container at application startup time. The framework
    providing an implementation of the
    ServletContainerInitializer MUST bundle in the
    META-INF/services directory of the jar file a file
    called javax.servlet.ServletContainerInitializer, as per
    the jar services API, that points to the implementation
    class of the ServletContainerInitializer.
    3.+

    View Slide

  21. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI

    View Slide

  22. § 8.2.4 Shared libraries / runtimes pluggability
    The ServletContainerInitializer’s onStartup method get's
    a Set of Classes that either extend / implement the
    classes that the initializer expressed interest in or if
    it is annotated with any of the classes specified via
    the @HandlesTypes annotation.
    3.+

    View Slide

  23. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all MyInitializer classes (from @HandlesTypes)

    View Slide

  24. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all WebApplicationInitializer classes (from @HandlesTypes)
    org.springframework.web.WebApplicationInitializer

    View Slide

  25. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all WebApplicationInitializer classes (from @HandlesTypes)
    |→ call
    ServletContainerInitializer.onStartup(classes,servletCtx)
    according to @Order order

    View Slide

  26. SPI
    org.springframework.web.SpringServletContainerInitializer
    content

    View Slide

  27. § Class ServiceLoader
    loader =
    ServiceLoader.load(ServletContainerInitializer.class)
    6.+

    View Slide

  28. § Class ServiceLoader
    loader =ServiceLoader.load(ServletContainerInitializer.class)
    6.+
    Все имплементации ServletContainerInitializer

    View Slide

  29. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    ...

    View Slide

  30. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {

    View Slide

  31. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)

    View Slide

  32. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)
    ...
    AnnotationAwareOrderComparator.sort(initializers);
    ...

    View Slide

  33. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)
    ...
    AnnotationAwareOrderComparator.sort(initializers);
    ...
    initializers.forEach(initializer -> initializer.onStartup(ctx));
    }

    View Slide

  34. Дружок, дай ка мне
    ServletContainerInitializer
    tomcat 7
    Spring
    Jar

    View Slide

  35. Spring
    ServletContainer
    Initializer
    tomcat 7
    Spring
    Jar

    View Slide

  36. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer
    implements ServletContainerInitializer

    View Slide

  37. WebApplicationInitializer
    W
    AI
    WAI
    tomcat 7
    JARS

    View Slide

  38. W
    AI
    WAI
    SpringServletContainerInitializer
    tomcat 7

    View Slide

  39. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  40. А попроще нельзя?

    View Slide

  41. Можно, но со Spring Boot`ом – Демо

    View Slide

  42. Как выглядит наш pom.xml

    org.springframework.boot
    spring-boot-starter-parent
    2.1.4.RELEASE

    View Slide


  43. org.springframework.boot
    spring-boot-starter-parent
    2.1.4.RELEASE


    org.springframework.boot
    spring-boot-dependencies
    2.1.4.RELEASE

    View Slide

  44. View Slide

  45. В чём проблема получить
    dependency management таким путём?

    View Slide

  46. Это чужой parent!

    View Slide

  47. View Slide




  48. io.spring.platform
    platform-bom
    Brussels-SR2
    pom
    import



    View Slide

  49. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    Пакует
    приложение

    View Slide

  50. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    Добавляем
    зависимости

    View Slide

  51. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    Automatic
    Dependency
    Management
    No Version

    View Slide

  52. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    dependencyManagement {
    imports {
    mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE'
    }
    }

    View Slide

  53. А я не понял

    View Slide

  54. Web Starter ТАЩИТ!

    View Slide

  55. Кто всё пакует



    org.springframework.boot
    spring-boot-maven-plugin



    View Slide

  56. Jar как Jar, но если зайти внутрь...

    View Slide

  57. View Slide

  58. Анатомия SpringBoot Jar
    jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot

    View Slide

  59. java -jar myapp.jar
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    MANIFEST.MF
    ...
    Main-Class: ???
    ...

    View Slide

  60. Перед запуском main – сформируй classpath

    View Slide

  61. JarLauncher

    View Slide

  62. Анатомия SpringBoot Jar
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...

    View Slide

  63. myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    Анатомия SpringBoot Jar
    MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...

    View Slide

  64. MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    Анатомия SpringBoot Jar
    JarLauncher

    View Slide

  65. А кто прописывает манифест этому джару?

    View Slide




  66. org.springframework.boot
    spring-boot-maven-plugin

    conference...OurMainClass




    MANIFEST.MF
    ...
    Start-Class: conference.spring.boot.ripper.OurMainClass
    ...

    View Slide

  67. Как выбирается mainClass если его не указать
    Spring boot plugin сканирует проект – ищет мейны
    ● если есть только один – то это он :)
    ● если больше одного – смотрит где стоит
    @SpringBootApplication и выбирает его
    ● если @SpringBootApplication нет или >1 – ошибка о
    множественных main при сборке

    View Slide

  68. Жмя и Работает
    executable jar для бабушки
    java -jar слишком сложно

    View Slide

  69. Хочу executable jar: Maven



    org.springframework.boot
    spring-boot-maven-plugin

    true




    springBoot {
    executable = true
    }
    Gradle bootJar {
    launchScript()
    }
    Spring boot 1.x.x
    Spring boot 2.x.x

    View Slide

  70. Demo. Executable jar

    View Slide

  71. Внутренний мир executable Jar
    ● Бабушка хочет чтобы жмя и работало
    ● windows “click click”
    ● $ ./app.jar
    ● ...

    View Slide

  72. Demo. jar structure

    View Slide

  73. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive

    View Slide

  74. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive
    Начало файла

    View Slide

  75. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive
    Начало файла
    Начало zip архива

    View Slide

  76. Где контекс?
    @SpringBootApplilcation
    class App {
    public static void main(String[] args) {
    SpringApplication.run(App.class,args);
    }
    }

    View Slide

  77. Вот контекст!
    @SpringBootApplilcation
    class App {
    public static void main(String[] args) {
    ApplicationContext context =
    SpringApplication.run(App.class,args);
    }
    }

    View Slide

  78. Разрешите представиться

    View Slide

  79. Давайте поговорим о контексте
    Малыш знает про
    ClassPathXmlApplicationContext
    А какие контексты знаешь ТЫ?

    View Slide

  80. Типы ConfigurableApplicationContext

    View Slide

  81. Это я решаю какой контекст создать
    Я до создания
    контекста ещё много
    всего делаю, но об
    этом потом.
    SpringApplication

    View Slide

  82. Web Context Generic Context

    View Slide

  83. Web Context Generic Context
    Если в classpath есть
    Servlet.class...

    View Slide

  84. Web Context Generic Context

    View Slide

  85. Web Context Generic Context
    Если в classpath есть
    Servlet.class...

    View Slide

  86. Если есть
    javax.servlet.Servlet ConfigurableWebApplicationContext
    +
    AnnotationConfigEmbedded WebApplicationContext
    AnnotationConfig ApplicationContext
    Иначе

    View Slide

  87. И что там в контексте то?

    View Slide

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

    View Slide

  89. Откуда взялись все
    эти бины?

    View Slide

  90. Не знаю как, но мы тоже так сделаем

    View Slide

  91. Задание
    1. Печатать предупреждение при вызове
    @Deprecated
    2. Печатать время исполнения методов
    @Benchmark
    3. * Отправить email на адрес alarm.email если
    вызвали @Deprecated или @Benchmark

    View Slide

  92. AOP
    ● Joint Point
    ● Pontcut
    ● Advise
    ● Aspect

    View Slide

  93. Pointcut
    ● execution
    ● within
    ● this
    ● target
    ● args
    ● bean
    ● @annotation

    View Slide

  94. Пример
    @Aspect
    @Component
    public class TestAspect {
    @Pointcut("execution(* *.doSomething(..)) && args(list,..) ")
    public void callMethod(List list) {
    ...
    }
    }

    View Slide

  95. Что за spring.factories ?
    § 44.1 Understanding auto-configured beans
    Under the hood, auto-configuration is implemented with standard @Configuration classes. Additional @Conditional annotations
    are used to constrain when the auto-configuration should apply. Usually auto-configuration classes use @ConditionalOnClass and
    @ConditionalOnMissingBean annotations. This ensures that auto-configuration only applies when relevant classes are found and
    when you have not declared your own @Configuration.
    You can browse the source code of spring-boot-autoconfigure to see the @Configuration classes that we provide (see
    theMETA-INF/spring.factories file).

    View Slide

  96. Даёшь инверсию контроля для стартеров

    View Slide

  97. @SpringBootApplication
    Всему голова

    View Slide

  98. @SpringBootApplication

    View Slide

  99. @SpringBootApplication
    → @ComponentScan
    → @Configuration
    → @EnableAutoConfiguration

    View Slide

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

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

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

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

  104. @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 {};
    }
    ImportSelector

    View Slide

  105. @EnableAutoConfiguration
    ImportSelector
    Web Starter Boot Starter

    Starter
    Mongo Starter

    View Slide

  106. Spring Factories Loader
    Jar/Starter
    spring.factories
    SpringFactoriesLoader

    View Slide

  107. SpringFactoriesLoader
    static List loadFactories(
    Class factoryClass,
    ClassLoader cl
    )
    static List loadFactoryNames(
    Class> factoryClass,
    ClassLoader cl
    )

    View Slide

  108. Даёшь инверсию контроля для стартеров

    View Slide

  109. spring-boot-autoconfigure.jar/spring.factories
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
    ….

    View Slide

  110. spring-boot-autoconfigure.jar
    @Import(CacheConfigurationImportSelector.class)
    public class CacheAutoConfiguration {

    View Slide

  111. CacheConfigurations
    private static final Map> MAPPINGS;
    static {
    Map> mappings = new HashMap>();
    mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
    mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
    mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
    mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
    mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
    mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
    mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
    mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
    addGuavaMapping(mappings);
    mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
    mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
    MAPPINGS = Collections.unmodifiableMap(mappings);
    }

    View Slide

  112. Где то внутри CacheAutoConfiguration
    for (int i = 0; i < types.length; i++) {
    Imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
    }
    return imports;

    View Slide

  113. Захардкодили

    View Slide

  114. И что, они всегда грузятся?

    View Slide

  115. Спокойствие, есть @Conditional
    Они фильтруются

    View Slide

  116. Почему бин то есть то нет?
    Какие условия?

    View Slide

  117. @ConditionalOn***

    View Slide

  118. Conditional и друзья
    @ConditionalOnBean
    @ConditionalOnClass
    @ConditionalOnCloudPlatform
    @ConditionalOnExpression
    @ConditionalOnJava
    @ConditionalOnJndi
    @ConditionalOnMissingBean
    @ConditionalOnMissingClass
    @ConditionalOnNotWebApplication
    @ConditionalOnProperty
    @ConditionalOnResource
    @ConditionalOnSingleCandidate
    @ConditionalOnWebApplication
    ...

    View Slide

  119. Паззлер
    @Configuration
    @ConditionalOnWinterIsHere
    public class UndeadArmyConfiguration {
    @Bean
    public WinterUndeadArmy cursedArmy() {
    return new WinterUndeadArmy();
    }
    @Bean
    @ConditionalOnWinterIsHere
    public DragonGlassFactory dragonGlassFactory() {
    return new DragonGlassFactory();
    }

    View Slide

  120. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    }

    View Slide

  121. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    }

    View Slide

  122. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }

    View Slide

  123. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }
    1. ClassDefNotFound ?
    2. Так вообще нельзя, не компилируется
    3. Будет работать
    4. Будет отлично работать

    View Slide

  124. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }
    1. ClassDefNotFound ?
    2. Так вообще нельзя, не компилируется
    3. Будет работать
    4. Будет отлично работать

    View Slide

  125. Потому что ASM

    View Slide

  126. OnPropertyCondition
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {
    String[] value() default {};
    String prefix() default "";
    String[] name() default {};
    String havingValue() default "";
    boolean matchIfMissing() default false;
    boolean relaxedNames() default true;
    }
    Что если нужно изменить?

    View Slide

  127. Люблю Spring Boot
    Но хочу Tomcat

    View Slide

  128. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  129. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  130. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext){
    SpringApplication.run(RipperApplication.class);
    }
    }

    View Slide

  131. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
    SpringApplication.run(RipperApplication.class);
    }
    }
    @SpringBootApplication
    public class RipperApplication {
    public static void main(String[] args) {
    SpringApplication.run(RipperApplication.class, args);
    }
    }

    View Slide

  132. А в Tomcat не запустилось!
    В Tomcat`e
    не пашет

    View Slide

  133. public class WebStarter implements WebApplicationInitializer{
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    SpringApplication.run(RipperApplication.class);
    }
    }
    Tomcat в Tomcat`е

    View Slide

  134. Читаем документацию

    View Slide

  135. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  136. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  137. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Работает по
    разному

    View Slide

  138. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  139. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Никогда не запустится в tomcat
    Только java -jar

    View Slide

  140. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Без main не соберется
    см. maven/gradle плагины
    Обязательно

    View Slide

  141. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Опционально

    View Slide

  142. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Никогда не запустится в embedded режиме
    Только в Tomcat контейнере
    Опционально

    View Slide

  143. Анатомия War
    .war
    |- META-INF
    |- WEB-INF
    |- lib
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  144. А как убрать tomcat из tomcat?
    If you are using a version of Gradle that supports compile only dependencies (2.12
    or later), you should continue to use providedRuntime. Among other limitations,
    compileOnly dependencies are not on the test classpath so any web-based
    integration tests will fail.

    View Slide

  145. Анатомия War
    .war
    |- META-INF
    |- WEB-INF
    |- lib
    |- lib-provided
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  146. Анатомия SpringBoot Jar/War
    jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org/springframework/boot/loader
    |- JarLauncher and friends
    war
    |- META-INF
    |- WEB-INF
    |- lib
    |- lib-provided
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  147. Jar стал похож на War

    View Slide

  148. View Slide

  149. Замочили web.xml

    View Slide

  150. Замочили web.xml
    Вместе с Tomcat`ом
    Вопросы?

    View Slide

  151. Вспомним Spring MVC
    Перед самым важным

    View Slide

  152. @RestController("/examine")
    Ошибку видишь? А она есть

    View Slide

  153. @RestController("/examine")
    Это имя spring bean, не http path
    И это даже может работать:
    ● Если нет ни одного контроллера с путём (всегда будет вызываться)
    ● Если потом указали полный путь в @RequestMapping

    View Slide

  154. @RequestMapping и его братья
    Управляет маршрутизацией запросов в метод контроллера. Маршрутизирует
    по:
    ● name/value – сам http path
    ● method – http method
    ● params
    ● headers

    View Slide

  155. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST, headers = {"my-app=appid"})
    public CheckedExam ex3( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces =
    MimeTypeUtils. APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }

    View Slide

  156. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex4() { }

    View Slide

  157. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex4() { }

    View Slide

  158. @RequestMapping и его братья
    //curl -XPOST /examine -d '{ "title": {..} }'
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam exam) { }
    //curl -XGET /examine/ mytitle
    @GetMapping("/examine/{title}")
    public CheckedExam ex3( @PathVariable String title) { }
    //curl -XGET /examine/mytitle ?debug=true
    @GetMapping("/examine/{title}")
    public CheckedExam ex3( @PathVariable String title, @RequestParam Boolean debug)
    { }

    View Slide

  159. @RequestMapping автовпрыскивание
    @PostMapping("/examine")
    public CheckedExam ex6(HttpServletRequest httpServletRequest,
    Authentication auth,
    UserDetails user,
    etc
    ) { }

    View Slide

  160. Controllers endpoints in logs since Spring 5
    logging.level:
    //print mapped controllers
    org.springframework.web.servlet.mvc.method.annotation: TRACE
    web: DEBUG //print requests to console

    View Slide

  161. Наконец то будем писать код!

    View Slide

  162. Задание 0
    Напишем стартер
    DoD:
    1. Все контроллеры помеченые @FrontendController аннотацией
    должны обворачивать возвращаемый результат в дополнительный Json
    {
    "result": {
    оригинальный_json
    }
    }
    2. https://github.com/lavcraft/spring-boot-and-cloud-ripper-2019
    3.

    View Slide

  163. Talk is cheap. Show me the code
    Linus Torvalds
    https://github.com/lavcraft/spring-boot-and-cloud-ripper-2019

    View Slide

  164. Задание 1. Напишем Экзаминатора
    public static void main(String[] args){
    SpringApplication.run(
    ExaminatorApplication.class,
    args);
    }

    View Slide

  165. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    E=?
    Количество вопросов Количество вопросов
    Экзаминатор

    View Slide

  166. С чего начинают строить микросервис
    →Контроллеры
    →Сервисы
    →Dao
    →Model

    View Slide

  167. С чего начинают строить микросервис
    →Контроллеры
    →Сервисы
    →Dao
    →Model – поскольку модель используется на всех слоях,
    пожалуй лучше начинать с неё

    View Slide

  168. in: {
    "student": "Борисов Евгений",
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]},
    {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }
    out: {
    "mark": 99,
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]},
    {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }

    View Slide

  169. Data model - задание 1.1*
    Придумайте архитектуру модели, которая позволит написать Сервис:
    - принимающий на вход объект SolvedExam
    - отдающий CheckedExam с оценкой
    - Должны проходить тесты
    @Test
    public void checkExamineContract() throws Exception {
    ...
    mockMvc.perform(post("/examine"))
    ...
    }
    Use @RestController and @RequestMapping > @*Mapping Luke

    View Slide

  170. Data model - задание 1.1*
    Придумайте архитектуру модели, которая позволит написать Сервис:
    - принимающий на вход объект SolvedExam
    - отдающий CheckedExam с оценкой
    - Смотрите в src/test/resources/*.json
    - Должны проходить тесты
    @Test
    public void checkExamineContract() throws Exception {
    ...
    mockMvc.perform(post("/examine"))
    ...
    }
    Use @RestController and @RequestMapping > @*Mapping Luke

    View Slide

  171. Зло наследования

    View Slide

  172. Композиция намного лучше

    View Slide

  173. У композиции нет границ

    View Slide

  174. У композиции нет границ

    View Slide

  175. Lombok – composition / delegate and friends
    Annotation Processor
    Работает на этапе компиляции
    Генерит код, чтобы мы не писали
    Делает джаву более похожим на нормальный язык

    View Slide

  176. Про lombok, модели и не только
    @Data – POJO (@Getter, @Setter, @ToString, @EqualsAndHashcode)
    @Value – immutable POJO
    @AllArgumentConstruct(onConstructor = @_(@Autowired))

    View Slide

  177. Про lombok, модели и не только
    @Data – POJO (@Getter, @Setter, @ToString, @EqualsAndHashcode)
    @Value – immutable POJO
    @AllArgumentConstruct(onConstructor = @_(@Autowired))
    @RequireArgumentConstructor /@NoArgumentConstructor
    @Builder / @Singular
    @Delegate – примерно как в груви
    @SneakyThrows
    @Slf4j / @Log4j / …

    View Slide

  178. Теперь давайте разбираться Джэксоном

    View Slide

  179. Как Джексон пишет в джейсон
    1. Использует Java Getters
    a. Методы начинающиеся с Get
    2. @JsonIgnore – игнорируем Getter
    3. Нет ни одного Getter – падает

    View Slide

  180. Как Джексон пишет в объект
    1. Нет конструктора с @ConstructorProperties
    → выставляет через Setters
    → если нет Setter и есть Getter – выставляет напрямую в филды
    → это все только при наличии пустого конструктора
    2. Есть конструктор с @ConstructorProperties
    → выставляет значение через него
    3. После зачем то вызовет все геттеры
    * Но если есть то @JsonIgnore всё по другому
    * Это поведение по умолчанию

    View Slide

  181. Меняем дефолтное поведение
    @JsonIgnoreProperties
    @JsonIgnoreType
    @JsonIgnore
    @JsonProperty

    View Slide

  182. Что не так с JSR 310?

    View Slide

  183. → Если вы настраиваете руками:
    ObjectMapper mapper = new ObjectMapper()
    .registerModule( new ParameterNamesModule())
    .registerModule( new Jdk8Module())
    .registerModule( new JavaTimeModule());
    → Или так:
    mapper.findAndRegisterModules();
    Не забыть зависимости:
    compile 'com.fasterxml.jackson.module:jackson-module-parameter-names'
    compile 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
    compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
    C последних версий есть поддержка Java8

    View Slide

  184. Достаточно одних зависимостей
    Если вы работаете с Spring Boot 1.x.x

    View Slide

  185. Вообще ничего не нужно
    Если вы работаете с Spring Boot 2.x.x

    View Slide

  186. Как Spring MVC работает с Джексоном
    Напишите метод который принимает json и класс, а возвращает объект
    данного класса
    Пробуем использовать objectMapper Jackson2

    View Slide

  187. @DateTimeFormat
    Ну вы в курсе…
    А ещё можно в application.properties

    View Slide

  188. Кстати о сериалайзерах
    Давайте как то без них, по возможности

    View Slide

  189. Кто использует Jackson в мире Spring MVC
    1. ClassLoader
    2. Dispatcher Servlet
    3. Tomcat
    4. BeanPostProcessor

    View Slide

  190. Кто использует Jackson в мире Spring MVC
    1. ClassLoader
    2. Dispatcher Servlet
    3. Tomcat
    4. BeanPostProcessor

    View Slide

  191. Задание 2
    Микросервис – Теологии
    1. Добавлять вопросы по теологии … (CRUD)
    2. Получать нужное количество рандомальных вопросов
    3. Тест должен проходить
    @Test
    public void should_return_random_exercise() throws Exception {
    mvc.perform(get("/exercise/random?count=3")
    .contentType( APPLICATION_JSON)
    )

    Use JpaRepository and @RequestMapping Luke

    View Slide

  192. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор
    Person p = restTemplate.getForObject("/{name}/details", Person.class, name);
    Для связи сервисов используем RestTemplate

    View Slide

  193. Конфликт портов — relaxed properties
    1. server.port в application.properties
    2. export SERVER_PORT=8081
    3. java -jar --server.port=8081
    4. SPRING_APPLICATION_JSON = '{"server":{"port":8081}}'

    View Slide

  194. in: {
    "student": "Борисов Евгений",
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]
    }, {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }

    View Slide

  195. compile 'org.springframework.data:spring-data-rest-webmvc'
    Подключаем Spring Data Rest

    View Slide

  196. Подключаем Spring Data Rest
    compile 'org.springframework.data:spring-data-rest-webmvc'
    или
    compile 'org.springframework.boot:spring-boot-starter-data-rest'

    View Slide

  197. Подключаем Spring Data Rest
    compile 'org.springframework.data:spring-data-rest-webmvc'
    или
    compile 'org.springframework.boot:spring-boot-starter-data-rest'

    View Slide

  198. Дальше пишем Entity + Repository
    Which repositories get exposed by defaults?
    Name Description
    DEFAULT Exposes all public repository interfaces but considers
    @Repository/@RestResource’s `exported flag
    ALL Exposes all repositories independently of type visibility and
    annotations
    ANNOTATION Only repositories annotated with @Repository/@RestResource
    are exposed, unless their exported flag is set to false
    VISIBILITY Only public repositories annotated are exposed

    View Slide

  199. Как поменять стратегию
    @Component
    public class MyWebConfiguration extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    config.setRepositoryDetectionStrategy( ALL);
    }
    }

    View Slide

  200. Как задать URL
    @Configuration
    class CustomRestMvcConfiguration {
    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
    return new RepositoryRestConfigurerAdapter() {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    configuration.setBasePath( "/api")
    }
    };
    }
    }

    View Slide

  201. Как задать URL
    @Configuration
    class CustomRestMvcConfiguration {
    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
    return new RepositoryRestConfigurerAdapter() {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    configuration.setBasePath( "/api")
    }
    };
    }
    }
    Или в том же RepositoryRestConfigurerAdapter прописать
    в application.properties - spring.data.rest.base-path=/api

    View Slide

  202. Spring Data Rest official supports
    →Spring Data JPA
    →Spring Data MongoDB
    →Spring Data Neo4j
    →Spring Data GemFire
    →Spring Data Cassandra

    View Slide

  203. @RepositoryRestResource
    @RepositoryRestResource (collectionResourceRel = "people", path = "people")
    public interface PersonRepository extends MongoRepository {
    @RestResource(path = "byname")
    List findByLastName(@Param( "name") String name);
    }

    View Slide

  204. URL’s и методы
    localhost:8080/exercise – список всех (GET)
    localhost:8080/exercise/1 – дай человека с айдишником 1 (GET)
    localhost:8080/exercise/1 – стереть с айдишником 1 (DELETE)
    localhost:8080/exercise/1 – заменить с айдишником 1 (PUT)
    localhost:8080/exercise/1 – проапдэйтить с айдишником 1 (PATCH)
    localhost:8080/exercise/search/findByName?name=Lanister

    View Slide

  205. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор

    View Slide

  206. Задание 3
    Связать сервисы Экзаменатора и Предметы
    ● Запустить и проверить самим
    ● Должен отдаваться запрос с ответами другого сервиса
    {
    "География":5,
    "Физика" :3
    }

    View Slide

  207. --server.port
    Конфликт портов решается с помощью relaxed properties
    export SERVER_PORT=8081
    java -jar --server.port=8081
    SPRING_APPLICATION_JSON = '{"server":{"port":8081}}'

    View Slide

  208. RestTemplate
    ● you can create it even with new / or inject configured bean
    ● postForEntity / postForObject/ getForEntity / getForObject
    restTemplate.getForObject(
    "http://"+serviceName+"/exercice/random?count=" + number,
    Exercice[].class);
    ● exchange
    restTemplate.exchange(url.toString(),
    HttpMethod.POST,
    entity,
    String.class);

    View Slide

  209. Шаринг кода
    модель/бизнес
    логика/инфраструктурный код
    ● Шарить
    ● Или не шарить

    View Slide

  210. Подход №1 - общий код
    Зачем разбивать микросервис на модули?
    → Java API для сервисов
    → фиксация контракта на уровне зависимостей языка
    → утилитарная функция, избавления от бойлерплейта

    View Slide

  211. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-...

    View Slide

  212. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-api
    |- project-domain
    |- project-...
    Пфф

    View Slide

  213. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-api
    |- project-domain
    |- project-...
    Пфф
    KISS

    View Slide

  214. Подход №2 - общий контракт
    → разделение кода и контракта
    → поставка контракта отдельно

    View Slide

  215. Обратимся к истории
    → REST подход

    → RPC подход

    View Slide

  216. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход

    View Slide

  217. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход
    ○ jax-ws/jax-rpc
    ○ corba
    ○ json rpc


    View Slide

  218. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход
    ○ jax-ws/jax-rpc
    ○ corba
    ○ json rpc
    ○ thrift
    ○ protobuf/grpc
    ○ etc

    View Slide

  219. Задание 5
    ● пишем SDK для вызова микросервиса по теологии
    ● и тесты для него
    ● тестируем контракт
    ● тестируем взаимодействие
    ○ WireMock/Spring MockServer
    ○ Spring Boot Test
    ○ Context Scanning behaviour
    Тест SDK Тест Контракта Тест Взаимодействия

    View Slide

  220. Разбиваем микросервис на модули
    Кому отсыпать
    наносервисов?

    View Slide

  221. Конец 1й части
    приходите на Spring Boot Ripper

    View Slide

  222. Тренинг с конференции JPoint 2019

    View Slide

  223. Часть 2
    Микросервисы при масштабировании

    View Slide

  224. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    Энтропия не?
    Количество вопросов Количество вопросов
    Экзаминатор

    View Slide

  225. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор
    Бог
    Не грешно ли лезть в атом?

    View Slide

  226. Математика – сервис
    ● Сделать новый модуль
    ● Реализовать метод "/exercise/random?amount=10"
    ● Возвращать список автогенерируемых упражнение (как в Теологии)

    View Slide

  227. Как воткнуть в Examinator?

    View Slide

  228. Как воткнуть в Examinator math-service?
    exercises:
    urls:
    "theology" : "http://localhost:8080/"

    View Slide

  229. Как воткнуть в Examinator math-service?
    exercises:
    urls:
    "theology" : "http://localhost:8080/"
    "math" : "http://localhost:8082/" Потому что на 8081
    Examinator

    View Slide

  230. Как воткнуть в Examinator math-service?
    exercises:
    urls:
    "theology" : "http://localhost:8080/"
    "math" : "http://localhost:8082/"
    "examinator": "http://localhost:8081/"
    "..." : "http://localhost:8083/"
    Service Registry в application.yml

    View Slide

  231. А как шарить service registry в yml?

    View Slide

  232. Service Registry

    View Slide

  233. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Количество вопросов
    Количество вопросов
    Экзаминатор
    Экзаминатор
    Теология
    Service Registry

    View Slide

  234. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Количество вопросов
    Количество вопросов
    Экзаминатор
    Экзаминатор
    Теология
    Service Registry
    Clients
    Server

    View Slide

  235. Service Registry

    View Slide

  236. Что хранится в Service Registry?
    ● serviceId
    ● Ip адрес и порт
    ● Данные о хелсчеках
    ● Метаданные
    ○ Данные о геозонах
    ○ Пользовательские данные

    View Slide

  237. Кстати о хелсчеках
    Service Service registry
    Heartbeat

    View Slide

  238. Кстати о хелсчеках
    Service Service registry
    HealthCheck

    View Slide

  239. /actuator/health
    {
    "status": "UP",
    }

    View Slide

  240. /actuator/health
    management:
    endpoint:
    health:
    show-details: "ALWAYS"

    View Slide

  241. /actuator/health
    {
    "status": "UP",
    "details": {
    "diskSpace": {
    "status": "UP",
    "details": {
    "total": 499963174912,
    "free": 354837377024,
    "threshold": 10485760
    }
    },
    "refreshScope": {
    "status": "UP"
    },
    ….

    View Slide

  242. Можно конечно и хелсчек
    eureka:
    client:
    healthcheck:
    enabled: true

    View Slide

  243. Service
    Eureka
    Marathon
    Marathon Lb

    View Slide

  244. Eureka Server and Client
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'

    View Slide

  245. Eureka Server and Client
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    С Greenwich релиза
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    Spring Boot 1.x.x
    Spring Boot 2.x.x

    View Slide

  246. Собственный Eureka Server
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServer {
    public static void main(String[] args) {
    SpringApplication.run(EurekaServer.class, args);
    }
    }
    server.port: 8761
    spring.application.name: registry
    eureka:
    client.register-with-eureka: false
    server.enable-self-preservation: false

    View Slide

  247. Переводим на Eureka
    ● Создаём модуль eureka-server
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    Or
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

    View Slide

  248. Переводим на Eureka
    ● Подключаем новый bom
    enforcedPlatform( "org.springframework.cloud:
    spring-cloud-dependencies:Greenwich.RELEASE"),
    ● Создаём модуль eureka-server
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    Or
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
    ● Интегрируем клиента (examinator-service, theology-service, math-service)
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    Or
    compile 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

    View Slide

  249. Переводим на Eureka
    @EnableDiscoveryClient vs @EnableEurekaClient
    eureka.client.service-url.default-zone: http://127.0.0.1:8761/eureka/
    spring.application.name: EXAMINATOR
    @Никаких Аннотация не нужно в 2хх
    eureka.client.service-url.default-zone: http://127.0.0.1:8761/eureka/
    spring.application.name: EXAMINATOR
    Spring Boot 1.x.x
    Spring Boot 2.x.x

    View Slide

  250. @EnableDiscoveryClient
    Будет включен по умолчанию при добавлении стартера c Edgware
    spring-cloud release train
    application.yml:
    server.port: 8081
    spring.application.name: examinator
    eureka.client.service-url.defaultZone:http://localhost:8671/eureka

    View Slide

  251. @EnableEurekaServer
    Будет включен по умолчанию при добавлении стартера c Edgware
    spring-cloud release train
    application.yml:
    server.port: 8761
    spring.application.name: registry
    eureka:
    client.register-with-eureka= false

    View Slide

  252. @EnableEurekaServer
    open http://localhost:8761

    View Slide

  253. И как это использовать?
    Как переделать Examinator, чтобы он соблюдал open close?

    View Slide

  254. Кто это у нас там спрятался?
    Смотрим какие сервисы загрузились
    $ http :8082/health
    {
    "description": "Remote status from Eureka server",
    "status": "DOWN"
    }
    {
    "description": "Spring Cloud Eureka Discovery Client",
    "status": "UP"
    }
    Spring Boot 1.x.x

    View Slide

  255. Кто это у нас там спрятался?
    Выключаем security для управляющий API
    management:
    security:
    enabled: false
    Теперь
    $ http :8082/health
    Spring Boot 1.x.x

    View Slide

  256. {
    "description": "Spring Cloud Eureka Discovery Client",
    "discoveryComposite": {
    "description": "Spring Cloud Eureka Discovery Client",
    "discoveryClient": {
    "description": "Spring Cloud Eureka Discovery Client",
    "services": [
    "examinator-service",
    "math-service",
    "theology-service"
    ],
    "status": "UP"
    },
    "eureka": {
    "applications": {
    "EXAMINATOR-SERVICE": 1,
    "MATH-SERVICE": 1,
    "THEOLOGY-SERVICE": 1
    },
    "description": "Remote status from Eureka server",
    "status": "UP"
    },
    "status": "UP"
    },
    "diskSpace": {
    "free": 190254260224,
    "status": "UP",
    "threshold": 10485760,
    "total": 499055067136
    },
    ...
    Spring Boot 1.x.x

    View Slide

  257. Кто это у нас там спрятался?
    application.yml
    management:
    endpoints:
    web:
    exposure:
    include: '*'
    Spring Boot 2.x.x

    View Slide

  258. Кто это у нас там спрятался?
    Смотрим какие сервисы загрузились
    $ http :8082/actuator/health
    {
    "status": "UP"
    }
    $ http :8082/actuator/metrics
    ...
    $ http :8082/actuator
    ...
    Spring Boot 2.x.x

    View Slide

  259. Как обратиться к сервисам по имени?
    restTemplate.getForObject(
    "http://"+serviceName+"/exercice/random?count=" + number,
    Exercice[].class
    );

    View Slide

  260. True Discovery
    ● Написать контроллер, который возвращает все зарегистрированные
    сервисы
    ● Запустить несколько сервисов одного типа, посмотреть результат
    @Autowired private DiscoveryClient discoveryClient;
    discoveryClient.getServices()
    discoveryClient.getInstances("theology")

    View Slide

  261. True Discovery
    ● Включить логику резолва сервиса в адресе запроса
    Use @LoadBalanced or LoadBalancerInterceptor Luke

    View Slide

  262. Health checks
    starting

    View Slide

  263. Health checks
    http available
    starting

    View Slide

  264. Health checks
    app available
    http available
    starting

    View Slide

  265. Health checks
    marathon window
    app available
    http available
    starting

    View Slide

  266. Health checks
    unavailable window
    marathon window
    app available
    http available
    available window
    starting

    View Slide

  267. Health checks
    unavailable window
    marathon window
    app available
    http available
    started
    available window
    starting working

    View Slide

  268. Self preservation

    View Slide

  269. Self preservation
    Service Registry 2
    Service A
    Instance 1
    Service B
    Instance 1
    Service A
    Instance 2
    Service B
    Instance 2
    Service Registry 1

    View Slide

  270. Self preservation
    Service Registry 2
    Service A
    Instance 1
    Service B
    Instance 1
    Service A
    Instance 2
    Service B
    Instance 2
    Service Registry 1

    View Slide

  271. Self preservation
    eureka:
    server.enable-self-preservation: false

    View Slide

  272. Discovery и Load Balancing
    Две стороны одного и того же

    View Slide

  273. Discovery и Load Balancing
    Две стороны одного и того же
    Server Side vs Client Side

    View Slide

  274. 274
    Service Client
    Registry-aware
    HTTP Client
    Service Registry
    Service Instance 1
    Service Instance N
    Service Instance ...
    Load balance request
    Client side discovery
    Ribbon +
    Eureka Discovery client

    View Slide

  275. 275
    Service Client
    Service Registry
    Service Instance 1
    Service Instance N
    Service Instance ...
    Load balance request
    Router/Proxy
    Server side discovery

    View Slide

  276. сервис
    ошибки
    задержки сети
    балансировка
    конфигурация
    инфраструктура

    View Slide

  277. Резюмируем Discovery

    View Slide

  278. Разберемся
    HTTP_PORT
    MANAGEMENT_PORT
    HTTP_ADVERTIZED_PORT
    MANAGEMENT_ADVERTIZED_PORT
    Внутри
    контейнера
    Внутренние
    порты
    Внешние
    порты
    App in Docker
    Во вне

    View Slide

  279. @EnableEurekaServer
    open http://localhost:8761
    HTTP_ADVERTIZED_PORT

    View Slide

  280. Немного о HTTP Clients
    RestTemplate/Feign/OkHttp/Apache HTTP/Java
    HTTP

    View Slide

  281. Feign Client с обратной стороны стороны
    @RestController
    @RequestMapping(value = "/v0/forms/latestByUserId")
    public class FormsController {
    @GetMapping
    Form get(@RequestHeader("x-userid") String userId) {
    return new Form(..)
    }
    }

    View Slide

  282. Feign Client с другой стороны
    @RestController
    @RequestMapping(value = "/v0/forms/latestByUserId")
    public class FormsController {
    @GetMapping
    Form get(@RequestHeader("x-userid") String userId) {
    return new Form(..)
    }
    }

    View Slide

  283. Feign Client
    @FeignClient(
    name = "myApplicationName",
    url = "${config.myApplicationName}")
    @RequestMapping(value = "/v0/forms/latestByUserId")
    public interface FormsClient {
    @GetMapping
    Form get(@RequestHeader("x-userid") String userId);
    }

    View Slide

  284. Feign Client
    @FeignClient(
    name = "myApplicationName",
    url = "${config.myApplicationName}")
    @RequestMapping(value = "/v0/forms/latestByUserId")
    public interface FormsClient {
    @GetMapping
    Form get(@RequestHeader("x-userid") String userId);
    }

    View Slide

  285. Блеск и нищета RestTemplate
    restTemplate.exchange(
    "/cik",
    HttpMethod.GET,
    HttpEntity.newInstance(headers),
    Form[].class).body

    View Slide

  286. Блеск и нищета RestTemplate
    restTemplate.exchange(
    "/forms",
    HttpMethod.GET,
    HttpEntity.newInstance(headers),
    Form[].class).body
    restTemplate.exchange(
    "/forms",
    HttpMethod.GET,
    HttpEntity.newInstance(headers),
    new ParameterizedTypeReference>() {}).body

    View Slide

  287. Применимость
    FeignClient – the best если есть готовы контракт с которым есть интеграция
    * Но есть ньюансы
    RestTemplate — более общий инструмент, подойдёт в большем спектре
    случай, но решение будет читаться хуже и будет больше технических
    деталей

    View Slide

  288. Получили Client Side LB
    Нахаляву!

    View Slide

  289. Попроще никак?

    View Slide

  290. Принципы SOA
    290
    1. Standardized service contract
    2. Loose coupling
    3. Encapsulation
    4. Reusability
    5. Autonomy
    6. Statelessness
    7. Discoverability

    View Slide

  291. Принципы SOA
    291
    1. Standardized service contract
    2. Loose coupling
    3. Encapsulation
    4. Reusability
    5. Autonomy
    6. Statelessness
    7. Discoverability

    View Slide

  292. Fluent annotations
    292
    @Getter // generate getters
    @Setter // generate setters
    @Aspect // we are an aspect
    @ToString // generate toString()
    @EnableWs // SOAP is so enterprisy, we definitely need it
    @Endpoint // Seriously, just read above
    @EnableWebMvc // we want MVC
    @EnableCaching // and we want to cache stuff
    @Configuration // this class can configure itself
    @RestController // we want some REST
    @XmlRootElement // this component is marshallable
    @EnableWebSocket // we want web socket, it's so new-generation
    @RedisHash("cat") // this class is an entity saved in redis
    @EnableScheduling // we want scheduled tasks
    @EnableWebSecurity // and some built-in security
    @NoArgsConstructor // generate no args constructor
    @ContextConfiguration // we want context configuration for unit testing
    @SpringBootApplication // this is a Sprint Boot application
    @Accessors(chain = true) // getters/setters are chained (ala jQuery)
    @EnableAspectJAutoProxy // we want AspectJ auto proxy
    @EnableAutoConfiguration // and auto configuration

    View Slide

  293. Парадокс централизации
    Чтобы эффективно разрабатывать распределённые
    приложения, нам нужны очень хорошие
    централизованные библиотеки и инструменты
    Например: логирование, health-checking, метрики,
    обработка типовых ошибок, автодокументирование
    293

    View Slide

  294. Парадокс централизации
    Чтобы эффективно разрабатывать распределённые
    приложения, нам нужны очень хорошие
    централизованные библиотеки и инструменты
    Но: не выносите бизнес-логику или доменные объекты!
    Не размывайте бизнес-контекст вашего API
    294

    View Slide

  295. Управление конфигурацией

    View Slide

  296. Spring Сloud Сonfig
    Тот, что хранит настройки

    View Slide

  297. Что лежит внутри
    ● application.yml
    ● application-test.yml
    ● examinator.yml
    ● examinator-dev.yml

    View Slide

  298. Your own Config Server
    ● Создать модуль config-server
    клиент
    implementation 'org.springframework.cloud:spring-cloud-starter-config'
    сервер
    implementation 'org.springframework.cloud:spring-cloud-config-server'
    @EnableConfigServer
    spring:
    cloud:
    config:
    server:
    git:
    uri: адрес репозитория

    View Slide

  299. Your own Config Server
    Можно проверить конфиги по адресу:
    http://host:port/{label}/{application_name}-{profile}.yml
    или
    ● /{label}/{name}-{profiles}.yml
    ● /{name}-{profiles}.yml
    ● /{name}-{profiles}.yaml
    ● /{name}/{profile}/{label}
    ● ...

    View Slide

  300. Проблема курица и яйца
    Откуда брать координаты eureka?
    ● из сonfig-server (для всех же одинаковые)
    ● из локальных настроек (ENV/*.yml/etc)
    Откуда брать координаты сonfig-server
    ● из eureka
    ● из локальных настроек (ENV/*.yml/etc)

    View Slide

  301. App Instance #0
    App Instance #N
    Config Server
    Eureka
    Bootstrap Server URL
    ИЛИ

    View Slide

  302. App Instance #0
    App Instance #N
    Config Server
    Eureka
    Bootstrap Server URL
    ИЛИ

    View Slide

  303. Config Server Discovery
    Через Service Discovery
    App Instance #0
    App Instance #N
    Config Server
    По прямой ссылке
    Eureka

    View Slide

  304. Config Server Discovery
    Через Service Discovery
    App Instance #0
    App Instance #N Eureka
    Config Server
    register on start
    get Config Server by name

    View Slide

  305. Config Server Discovery
    Через Service Discovery
    App Instance #0
    App Instance #N Eureka
    Config Server
    register on start
    return config server instances
    location
    get Config Server by name

    View Slide

  306. Config Server Discovery
    Через Service Discovery
    App Instance #0
    App Instance #N Eureka
    Config Server
    register on start
    return config server instances
    location
    get Config Server by name
    get configs for App

    View Slide

  307. Config Server Discovery
    По прямой ссылке
    App Instance #0
    App Instance #N
    Config Server
    get configs for App
    Http Load Balancer

    View Slide

  308. Config Client
    ● интегрируем config-server и *-service через eureka
    ● включаем/отключаем management.security

    View Slide

  309. Обновление на лету
    @RefreshScope

    View Slide

  310. Начнём с примера
    @RestController
    @RefreshScope
    public class TestController {
    @Value("${foo.bar}")
    String test;
    @GetMapping("/foo")
    public String getFooBar() {
    return test;
    }
    }

    View Slide

  311. Начнём с примера
    @RestController
    @RefreshScope
    public class TestController {
    @Value("${foo.bar}")
    String test;
    @GetMapping("/foo")
    public String getFooBar() {
    return test;
    }
    }

    View Slide

  312. Начнём с примера
    @RestController
    @RefreshScope
    public class TestController {
    @Value("${foo.bar}")
    String test;
    @GetMapping("/foo")
    public String getFooBar() {
    return test;
    }
    }

    View Slide

  313. Три простых шага
    1. Ставим @RefreshScope над бином который нужно обновлять
    2. Обновляем в config server
    foo.bar: some val
    a. Можно не править а дёрнуть /env если есть actuator
    3. Дёргаем у приложения /refresh
    a. Для этого нужен actuator

    View Slide

  314. А как обновить всё разом?

    View Slide

  315. Spring Cloud Bus

    View Slide

  316. Физика Математика
    Экзаминатор
    Экзаминатор
    Теология
    Теология
    Config Server
    ALL APP
    Discovery Server
    ALL APP

    View Slide

  317. Физика Математика
    Экзаминатор
    Экзаминатор
    Теология
    Теология
    Queue Config Server
    ALL APP
    Discovery Server
    ALL APP
    ALL APP

    View Slide

  318. Spring Cloud Bus
    dependencyManagement {
    imports {
    mavenBom 'org.springframework.cloud:spring-cloud-bus:2.1.1.RELEASE'
    }
    }
    dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
    }

    View Slide

  319. Настройки в сonfig server application.yml
    spring:
    rabbitmq:
    host: localhost
    port: 5672

    View Slide

  320. Теперь достаточно /refresh на config-server
    1. Все приложения обновят конфигурации при получении refresh ивента в
    rabbitmq

    View Slide

  321. Теперь достаточно /refresh на config-server
    1. Все приложения обновят конфигу при получении refresh ивента в
    rabbitmq
    2. Для автоматизации можно использовать config-monitor + git webhook
    compile 'org.springframework.cloud:spring-cloud-config-monitor'
    3. Не забывать @RefreshScope

    View Slide

  322. spring-cloud-config-monitor
    GitHub
    commit Config Monitor
    webhook
    Queue
    refresh
    event

    View Slide

  323. spring-cloud-config-monitor
    Config Server
    Экзаминатор
    Экзаминатор
    GitHub
    commit Config Monitor
    webhook
    Queue
    refresh
    event
    Теология
    Теология
    Теология
    ...
    auto /refresh on event

    View Slide

  324. Spring Cloud Gateway

    View Slide

  325. Физика Математика
    Теология
    Теология
    Queue
    Экзаминатор
    Config Server
    ALL APP
    Discovery Server
    ALL APP
    ALL APP

    View Slide

  326. Физика Математика
    Экзаминатор
    Экзаминатор
    Теология
    Теология
    Queue Config Server
    ALL APP
    Discovery Server
    ALL APP
    ALL APP

    View Slide

  327. Физика Математика
    Экзаминатор
    Экзаминатор
    Теология
    Теология
    Queue Config Server
    ALL APP
    Discovery Server
    ALL APP
    ALL APP
    Экзаминатор

    View Slide

  328. Экзаминатор:8080
    Экзаминатор:8081
    Экзаминатор:8082
    Экзаминатор:32634
    Экзаминатор:...

    View Slide

  329. Физика Математика
    Экзаминатор
    Экзаминатор
    Теология
    Теология
    Gateway Server
    Queue Config Server
    ALL APP
    Discovery Server
    ALL APP
    ALL APP

    View Slide

  330. Reverse Proxy Problem
    @EnableZuulProxy
    vs
    @EnableZuulServer
    Spring boot 1.x.x

    View Slide

  331. Зависимости Zuul – Gateway
    dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-zuul'
    }
    Spring boot 1.x.x

    View Slide

  332. Зависимости Spring Cloud Gateway
    dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-gateway'
    //for webflux
    compile 'org.springframework.cloud:spring-cloud-gateway-webflux'
    //or for mvc
    compile 'org.springframework.cloud:spring-cloud-gateway-mvc'
    }
    Spring boot 2.x.x

    View Slide

  333. Зависимости Spring Cloud Gateway
    dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-gateway'
    //for webflux
    compile 'org.springframework.cloud:spring-cloud-gateway-webflux'
    //or for mvc
    compile 'org.springframework.cloud:spring-cloud-gateway-mvc'
    }
    application.yml:
    spring:
    cloud.gateway.discovery.locator.enabled: true
    Spring boot 2.x.x

    View Slide

  334. Был Zuul, стал cloud gateway - зачем?

    View Slide

  335. Почему стоит мигрировать?
    ● Zuul устарел и почти не поддерживается, Zuul 2 ждали долго
    ● Zuul не совместим с Spring Boot 2.1
    ● Spring Cloud Gateway работает на Netty
    ● Spring Cloud Gateway имеет сильно больше фильтров “из коробки”

    View Slide

  336. Роутинг в конфигурации zuul
    zuul:
    ignoredServices: '*'
    routes:
    student:
    path: /exam/**
    stripPrefix: true
    serviceId: examinator

    View Slide

  337. Zuul.Фильтры
    @Component
    public class ChangeSearchFilter extends ZuulFilter {
    public String filterType() { return PRE_TYPE; }
    public int filterOrder() { return PRE_DECORATION_FILTER_ORDER + 1;}
    public boolean shouldFilter() {
    HttpServletRequest request = RequestContext.
    getCurrentContext()
    .getRequest();
    return request.getRequestURI().startsWith(
    "/exam") &&
    StringUtils.isEmpty(request.getHeader("studentId"));
    }
    ...

    View Slide

  338. Zuul.Фильтры
    @Component
    public class ChangeSearchFilter extends ZuulFilter {
    ...
    public Object run() throws ZuulException {
    RequestContext ctx = RequestContext.
    getCurrentContext();
    ctx.addZuulRequestHeader(
    "Request-time",
    Long.toString(System.currentTimeMillis()));
    return null;
    }
    }

    View Slide

  339. Роутинг в конфигурации gateway
    spring:
    cloud:
    gateway:
    routes:
    - id: math
    uri: lb://math
    predicates:
    - Path=/math/**

    View Slide

  340. Gateway.Фильтры
    @Bean
    public RouteLocator route(RouteLocatorBuilder builder) {
    return builder.routes()
    .route(r -> r.host(
    "**.some.org").and().path("/exam")
    .filters(f ->f.addResponseHeader(
    "Request-time", currentTime))
    .uri("lb://examinator")
    ).build();
    }

    View Slide

  341. Gateway
    ● Создаем проект с gateway
    ● Подключаем его к service-discovery
    ● Пишем конфигурацию для роутинга в examinator

    View Slide

  342. Сказка о балансировщике который не смог

    View Slide

  343. Сказка о балансировщике который не смог
    External LB

    View Slide

  344. Сказка о балансировщике который не смог
    External LB
    Local Node LB

    View Slide

  345. Сказка о балансировщике который не смог
    External LB
    Local Node LB
    App Level LB

    View Slide

  346. Сказка о балансировщике который не смог
    External LB
    Local Node LB
    App Level LB Zuul
    haproxy
    F5
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB

    View Slide

  347. Сказка о балансировщике который не смог
    External LB
    Local Node LB
    App Level LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    Zuul
    haproxy
    F5 SSL/DDOS/
    Protection/BGP/Publication
    Data Locality/Cluster HA/
    Cluster Health
    App Level LB/Metrics/
    Advanced Routing/Auth

    View Slide

  348. Сказка о балансировщике который не смог
    External LB
    Local Node LB
    App Level LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    Zuul
    haproxy
    F5 SSL/DDOS/
    Protection/BGP/Publication
    Data Locality/Cluster HA/
    Cluster Health
    App Level LB/Metrics/
    Advanced Routing/Auth

    View Slide

  349. Сказка о балансировщике который не смог
    External LB
    Local Node LB
    App Level LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type A
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    App Type B
    Client Side LB
    Zuul
    haproxy
    F5 SSL/DDOS/
    Protection/BGP/Publication
    App Level LB/Metrics/
    Advanced Routing/Auth
    Discovery service
    Discovery service
    Discovery service
    Data Locality/Cluster HA/
    Cluster Health

    View Slide

  350. Защити себя сам
    350

    View Slide

  351. 351
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    5мс

    View Slide

  352. 352
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс

    View Slide

  353. 353
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс

    View Slide

  354. 354
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс

    View Slide

  355. 355
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс

    View Slide

  356. 356
    Circuit Breaker

    View Slide

  357. 357
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс

    View Slide

  358. 358
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс
    Open

    View Slide

  359. 359
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    300мс
    Half-Open

    View Slide

  360. 360
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    5мс
    Half-Open

    View Slide

  361. 361
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    5мс

    View Slide

  362. Уиии, хочу hystrix!!!

    View Slide

  363. Из чего состоит Hystrix
    ● Механизм Circuit breaker

    View Slide

  364. Из чего состоит Hystrix
    ● Механизм Circuit breaker
    ● Механизм Timeout

    View Slide

  365. Из чего состоит Hystrix
    ● Механизм Circuit breaker
    ● Механизм Timeout
    ● Механизм BulkHead

    View Slide

  366. Режимы работы
    ● Threads
    ● Semaphore

    View Slide

  367. Rent Service
    Payment Service
    Security Service
    Blockchain Service
    Insurance Service
    threadpool size
    timeout
    x = 10 настроек

    View Slide

  368. Rent Service
    Payment Service
    Security Service
    Blockchain Service
    Insurance Service
    threadpool size
    connection timeout
    read timeout
    timeout
    x = 20 настроек

    View Slide

  369. 369
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Open

    View Slide

  370. 370
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Open
    Надо подкрутить таймауты!

    View Slide

  371. 371
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service

    View Slide

  372. 372
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Open

    View Slide

  373. 373
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Open
    Надо подкрутить таймауты!

    View Slide

  374. View Slide

  375. 375
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Open
    Да блин!

    View Slide

  376. Прохождение запроса в системе

    View Slide

  377. А еще я жру потоки,
    когда не следят

    View Slide

  378. … но проблема-то конечно не
    инструментах :)

    View Slide

  379. Кто-нибудь рассчитывает timeout?

    View Slide

  380. Может быть мониторите circuitbreaker?

    View Slide

  381. 381

    View Slide

  382. hystrix/apache camel/akka
    382

    View Slide

  383. 383
    Spring boot 1.x.x

    View Slide

  384. Hystrix устарел

    View Slide

  385. Новичок resilience4j

    View Slide

  386. Отличия

    View Slide

  387. Отличия
    ● Более легковесный (из коробки тянет только vavr)
    ● Не пытается сам управлять пулом потоков

    View Slide

  388. Отличия
    ● Более легковесный (из коробки тянет только vavr)
    ● Не пытается сам управлять пулом потоков
    ● Нет request collapser
    ● Есть rate limiter
    ● Есть retry

    View Slide

  389. Отличия
    ● Более легковесный (из коробки тянет только vavr)
    ● Не пытается сам управлять пулом потоков
    ● Нет request collapser
    ● Есть rate limiter
    ● Есть retry
    ● Удобное оборачивание методов “слоями” в функциональном стиле или
    набором аннотаций
    ● Собранные “обертки” можно переиспользовать

    View Slide

  390. Gateway
    ● Делаем так, чтобы один из сервисов(java,math) возращал ошибку в 50%
    случаев
    ● Подключаем hystrix и настраиваем для него cicruitbreaker на закрытие
    ● Пишем конфигурацию для роутинга в examinator

    View Slide

  391. … но инструмент все еще не решит
    все проблемы сам :)

    View Slide

  392. Gateway + Hystrix + Dashboard + Discovery
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-starter-actuator'
    compile 'org.springframework.cloud:spring-cloud-starter-zuul'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    compile 'org.springframework.cloud:spring-cloud-starter-hystrix'
    compile 'org.springframework.cloud:spring-cloud-starter-hystrix-dashboard'
    }
    Spring boot 1.x.x

    View Slide

  393. Следи за своим ПО
    393

    View Slide

  394. Нет трассировки - нет проблем? :)
    394

    View Slide

  395. X-Request-Id = X-Request-Id ?: new ID
    Простой вариант с ServletFilter
    395

    View Slide

  396. spring-cloud-sleuth/open zipkin
    396

    View Slide

  397. 397
    Rent Service
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A

    View Slide

  398. 398
    Rent Service
    Payment Service
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = C

    View Slide

  399. 399
    Rent Service
    Payment Service
    Blockchain Service
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = F

    View Slide

  400. 400
    Rent Service
    Payment Service
    Security Service Blockchain Service
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = F
    TraceId = X
    SpanId = G

    View Slide

  401. 401
    Rent Service
    Payment Service
    Security Service Blockchain Service
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = F
    TraceId = X
    SpanId = G

    View Slide

  402. 402
    Rent Service
    Payment Service
    Security Service Blockchain Service
    TraceId = X
    SpanId = A
    No TraceId
    No SpanId
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = B
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = C
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = D
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = E
    TraceId = X
    SpanId = F
    TraceId = X
    SpanId = G

    View Slide

  403. 403

    View Slide

  404. 404

    View Slide

  405. 405
    Rent Service
    Payment Service

    View Slide

  406. 406
    Rent Service
    Payment Service
    SpanId = B
    Client Send
    TraceId = X
    SpanId = A

    View Slide

  407. 407
    Rent Service
    Payment Service
    SpanId = B
    Client Send
    SpanId = B
    Server Received
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = C

    View Slide

  408. 408
    Rent Service
    Payment Service
    SpanId = B
    Client Send
    SpanId = B
    Server Received
    SpanId = B
    Server Send
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = C

    View Slide

  409. 409
    Rent Service
    Payment Service
    SpanId = B
    Client Send
    SpanId = B
    Server Received
    SpanId = B
    Client Received
    SpanId = B
    Server Send
    TraceId = X
    SpanId = A
    TraceId = X
    SpanId = C

    View Slide

  410. documentation → smart documentation
    410

    View Slide

  411. Not smart
    = This is main documentation
    This document describes how to be the most fundamental and important
    document in the world of documents
    ...
    COPY-PASTE documentation from another document
    ...
    411

    View Slide

  412. Not so smart
    = This is main documentation
    This document describes how to be the most fundamental and important document
    in the world of documents
    include::https://raw.github.com/asciidoctor/asciidoctor/master/Gemfile[]
    include::../other.adoc[]
    include::/home/tolkv/git/docs-0/superdoc.adoc[]
    412

    View Slide

  413. Really smart
    = This is main documentation
    This document describes how to be the most fundamental and important document
    in the world of documents
    include::https://raw.github.com/asciidoctor/asciidoctor/master/Gemfile[]
    include::gradle://gradle-advanced:service-with-deps:1.0/deps.adoc[]
    include::gradle://:service/doc.adoc[]
    413

    View Slide

  414. Payment Service[jar,doc] Insurance Service [jar,doc]
    One Point of View
    [UberDoc.zip]
    Rent Service[jar,doc] Other Service [jar,doc]
    Агрегация информации
    414

    View Slide

  415. Парадокс централизации
    Чтобы эффективно разрабатывать распределённые
    приложения, нам нужны очень хорошие
    централизованные библиотеки и инструменты
    Например: логирование, health-checking, метрики,
    обработка типовых ошибок, автодокументирование
    415

    View Slide

  416. Парадокс централизации
    Чтобы эффективно разрабатывать распределённые
    приложения, нам нужны очень хорошие
    централизованные библиотеки и инструменты
    Но: не выносите бизнес-логику или доменные объекты!
    Не размывайте бизнес-контекст вашего API
    416

    View Slide

  417. Пришло время поговорить про Reactive

    View Slide

  418. Зачем мне туда лезть?

    View Slide

  419. Реактивные микросервисы!

    View Slide

  420. Reactive manifesto
    Отзывчивость
    Эластичность Стабильность
    Асинхронное
    адресное
    общение * * у сообщения есть адресат
    * топология системы динамическая
    * компоненты слабо связаны

    View Slide

  421. И как это сделать?

    View Slide

  422. Ну тут условий много...

    View Slide

  423. Reactive manifesto
    Отзывчивость
    Эластичность Стабильность
    Асинхронное
    адресное
    общение * * у сообщения есть адресат
    * топология системы динамическая
    * компоненты слабо связаны

    View Slide

  424. Изоляция сбоев и selfhealing...

    View Slide

  425. Reactive manifesto
    Отзывчивость
    Эластичность Стабильность
    Асинхронное
    адресное
    общение * * у сообщения есть адресат
    * топология системы динамическая
    * компоненты слабо связаны

    View Slide

  426. Слабая связность...

    View Slide

  427. Reactive manifesto
    Отзывчивость
    Эластичность Стабильность
    Асинхронное
    адресное
    общение * * у сообщения есть адресат
    * топология системы динамическая
    * компоненты слабо связаны

    View Slide

  428. Стримминг на всех уровнях

    View Slide

  429. Reactive manifesto
    Отзывчивость
    Эластичность Стабильность
    Асинхронное
    адресное
    общение * * у сообщения есть адресат
    * топология системы динамическая
    * компоненты слабо связаны

    View Slide

  430. NIO и MQ

    View Slide

  431. 431
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service

    View Slide

  432. 432
    Хочу бегемота!
    Rent Service
    Payment Service
    Security Service Blockchain Service
    Insurance Service
    Sync
    Sync
    Sync

    View Slide

  433. Controller Service
    HttpClient
    DBClient

    View Slide

  434. Controller Service
    HttpClient
    DBClient

    View Slide

  435. Controller Service
    HttpClient
    DBClient

    View Slide

  436. Controller Service
    HttpClient
    DBClient

    View Slide

  437. Частичная асинхронность
    тут не поможет...

    View Slide

  438. Реактивные стримы!

    View Slide

  439. Не могу больше пить.
    Давайте смотреть

    View Slide

  440. Publisher Subscriber
    подписка
    данные
    Реактивные стримы

    View Slide

  441. Publisher
    Publisher/
    Subscriber
    подписка
    данные
    Реактивные стримы
    Subscriber
    подписка
    данные

    View Slide

  442. Publisher
    Publisher/
    Subscriber
    подписка
    данные
    Реактивные стримы
    Subscriber
    подписка
    данные

    View Slide

  443. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    Управляющие сигналы

    View Slide

  444. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    Subscribe
    Cancel
    Request

    View Slide

  445. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    данные

    View Slide

  446. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    Next
    Complete
    Error

    View Slide

  447. Publisher
    Publisher/
    Subscriber
    Реактивные стримы
    Subscriber
    Next
    Complete
    Error

    View Slide

  448. И как вот это все
    встраивается в
    приложение?

    View Slide

  449. Netty. Асинхронный вход
    Client
    Client
    Client
    Async
    threads
    Event
    loop
    Single
    thread
    Selector
    Delegate

    View Slide

  450. Reactor IPC
    Netty
    Controllers
    Spring WebFlux
    WebFlux

    View Slide

  451. Reactor IPC
    Netty
    Dispatcher
    Handler
    Controllers
    Channel
    Operations
    Spring WebFlux
    WebFlux

    View Slide

  452. Reactor IPC
    Netty
    WebFlux
    Dispatcher
    Handler
    Controllers
    Channel
    Operations
    Publisher
    Spring WebFlux

    View Slide

  453. Reactor IPC
    Netty
    WebFlux
    Dispatcher
    Handler
    Controllers
    Channel
    Operations
    Publisher
    Publisher
    Spring WebFlux

    View Slide

  454. Reactor IPC
    Netty
    WebFlux
    Dispatcher
    Handler
    Controllers
    Channel
    Operations
    Publisher
    Publisher
    Spring WebFlux

    View Slide

  455. Ну это на входе,
    А с другого конца?

    View Slide

  456. Асинхронный клиент
    Reactor
    IPC
    Netty ... WebClient

    View Slide

  457. Асинхронный клиент
    Netty ... WebClient
    Netty ... Controller
    App logic

    View Slide

  458. Асинхронный клиент
    Netty ... WebClient
    Netty ... Controller
    App logic

    View Slide

  459. Config Client
    ● Пробуем сделать examinator реактивным
    ● Подключаем spring-boot-starter-webflux
    ● Возвращаем из контроллера Mono
    ● Заменяем RestTemplate на WebClient
    ● Выстраиваем стрим от клиента до контроллера

    View Slide

  460. View Slide

  461. Для чего RPC
    Чтобы жизнь была легкой

    View Slide

  462. gRPC
    это счастье
    это радость
    это будущее в каждый дом

    View Slide

  463. Да ладно,
    и нет минусов?

    View Slide

  464. Давайте
    посмотрим

    View Slide

  465. 1. Сборка

    View Slide

  466. Gradle
    apply plugin: "com.google.protobuf"
    ...

    View Slide

  467. Gradle
    apply plugin: "com.google.protobuf"
    protobuf {
    protoc {
    artifact = 'com.google.protobuf:protoc'
    }
    generatedFilesBaseDir = "$projectDir/generated"
    plugins {
    grpc {
    artifact = 'io.grpc:protoc-gen-grpc-java'
    }
    reactor {
    artifact = "com.salesforce.servicelibs:reactor-grpc:0.9.0:jdk8@jar"
    }
    }
    generateProtoTasks {
    ofSourceSet('main')*.plugins {
    grpc { }
    reactor { }
    }

    View Slide

  468. reactor {
    artifact = "com.salesforce.servicelibs:reactor-grpc:0.9.0:jdk8@jar"
    }
    }
    generateProtoTasks {
    ofSourceSet('main')*.plugins {
    grpc { }
    reactor { }
    }
    }
    }
    clean {
    delete protobuf.generatedFilesBaseDir
    }
    idea {
    module {
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/java")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/reactor")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/grpc")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/rsocketRpc")
    }
    }
    dependencies {
    compile 'com.google.protobuf:protobuf-java'
    Gradle

    View Slide

  469. ofSourceSet('main')*.plugins {
    grpc { }
    reactor { }
    }
    }
    }
    clean {
    delete protobuf.generatedFilesBaseDir
    }
    idea {
    module {
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/java")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/reactor")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/grpc")
    generatedSourceDirs += file("${protobuf.generatedFilesBaseDir}/main/rsocketRpc")
    }
    }
    dependencies {
    compile 'com.google.protobuf:protobuf-java'
    compile 'io.grpc:grpc-stub'
    compile 'io.grpc:grpc-protobuf'
    compile 'com.salesforce.servicelibs:reactor-grpc-stub'
    }
    Gradle

    View Slide

  470. 2. Описание

    View Slide

  471. *.proto
    import "google/api/annotations.proto";
    package my.error_service;
    service Errors {
    rpc GetErrorClass (GetErrorClassRequest) returns (GetErrorClassResponse) {
    option (google.api.http) = {
    post: "/errorclass"
    body: "*"
    };
    }
    }
    message GetErrorClassRequest {
    string error_id = 1;
    }
    message GetErrorClassResponse {
    string error_class = 1;
    }

    View Slide

  472. Почему это
    не сделали
    в плагине?

    View Slide

  473. View Slide

  474. View Slide