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

Spring boot/cloud workshop at jpoint 2018

Spring boot/cloud workshop at jpoint 2018

Part 1: Spring Boot
* How to cook microservice
* Theory
* Modeling with Lombok
* Jackson configuration
* RestTemplate and advanced configuration
Part 2: Spring Boot
* How to cook microserviceS
* Serivce Discovery (Eureka)
* Config Server (+Spring Cloud Bus)
* Configuration live update
* Gateway (Zuul)
* Monitoring (Hystrix+Zipkin)

Kirill Tolkachev

April 08, 2018
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring {Boot,Cloud}
    Workshop

    View Slide

  2. Тренинг с конференции Joker 2017

    View Slide

  3. https://goo.gl/xRL4wU
    https://t.me/joker2017st

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. The project structure

    View Slide

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

  9. View Slide

  10. View Slide



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


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


    View Slide

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

    View Slide

  13. create Spring
    ContextLoaderListener
    start applicationContext
    Create filter
    mapping
    Create
    security filter
    login
    Create spring
    dispatcher
    servlet
    Delegate to
    Controller

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. WebApplicationInitializer

    View Slide

  18. ServletContainerInitializer

    View Slide

  19. WebApplicationInitializer

    View Slide

  20. ServletContainerInitializer

    View Slide

  21. View Slide

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

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

    View Slide

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

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

    View Slide

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

    View Slide

  27. Как? 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

  28. SPI
    org.springframework.web.SpringServletContainerInitializer
    content

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

  37. Spring
    ServletContainer
    Initializer
    tomcat 7
    Spring
    Jar

    View Slide

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

    View Slide

  39. WebApplicationInitializer
    W
    AI
    WAI
    tomcat 7
    JARS

    View Slide

  40. W
    AI
    WAI
    SpringServletContainerInitializer
    tomcat 7

    View Slide

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

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

    View Slide

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

    View Slide

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

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

    View Slide


  45. org.springframework.boot
    spring-boot-starter-parent
    1.5.3.RELEASE


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

    View Slide

  46. View Slide

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

    View Slide

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

    View Slide

  49. View Slide




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



    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. А я не понял

    View Slide

  56. Web Starter ТАЩИТ!

    View Slide

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



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



    View Slide

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

    View Slide

  59. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  63. JarLauncher

    View Slide

  64. Анатомия 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

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

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

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

    View Slide




  68. org.springframework.boot
    spring-boot-maven-plugin
    conference...OurMainClass



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

    View Slide

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

    View Slide

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

    View Slide

  71. Хочу executable jar: Maven



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

    true



    springBoot {
    executable = true
    }
    Gradle

    View Slide

  72. Demo. Executable jar

    View Slide

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

    View Slide

  74. Demo. jar structure

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. Типы ConfigurableApplicationContext

    View Slide

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

    View Slide

  84. Web Context Generic Context

    View Slide

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

    View Slide

  86. Web Context Generic Context

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  92. Что за 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

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

    View Slide

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

    View Slide

  95. @SpringBootApplication

    View Slide

  96. @SpringBootApplication
    → @ComponentScan
    → @Configuration
    → @EnableAutoConfiguration

    View Slide

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

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

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

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

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

  102. @EnableAutoConfiguration
    ImportSelector
    Web Starter Boot Starter

    Starter
    Mongo Starter

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  114. @ConditionalOn***

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

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

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

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

    View Slide

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

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

    View Slide

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

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

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

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

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

  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 a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Никогда не запустится в tomcat
    Только java -jar

    View Slide

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

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

  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);
    }
    }
    Никогда не запустится в embedded режиме
    Только в Tomcat контейнере
    Опционально

    View Slide

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

    View Slide

  141. А как убрать 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

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

    View Slide

  143. Анатомия 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

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

    View Slide

  145. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

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

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  176. → Если вы настраиваете руками:
    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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  181. Задание 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

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

  187. Дальше пишем 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

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

    View Slide

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

    View Slide

  190. Как задать 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

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    → RPC подход

    View Slide

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

    View Slide

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


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  210. Тренинг с конференции Joker 2017

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  218. Как воткнуть в 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

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

    View Slide

  220. Service Registry – Eureka

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  224. Your own Discovery Server
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServer {
    public static void main(String[] args) {
    SpringApplication.run(EurekaServer.class, args);
    }
    }

    View Slide

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

    View Slide

  226. Переводим на Eureka
    ● Создаём модуль eureka-server
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    ● Интегрируем клиента (examinator-service, theology-service, math-service)
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'

    View Slide

  227. Переводим на Eureka
    ● Создаём модуль eureka-server
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    ● Интегрируем клиента (examinator-service, theology-service, math-service)
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    compile 'org.springframework.boot:spring-boot-starter-actuator'
    @EnableDiscoveryClient vs @EnableEurekaClient
    eureka.client.service-url.default-zone: http://127.0.0.1:8761/eureka/

    View Slide

  228. Переводим на Eureka
    ● Создаём модуль eureka-server
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    ● Интегрируем клиента (examinator-service, theology-service, math-service)
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    compile 'org.springframework.boot:spring-boot-starter-actuator'
    @EnableDiscoveryClient vs @EnableEurekaClient
    eureka.client.service-url.default-zone: http://127.0.0.1:8761/eureka/

    View Slide

  229. @EnableDiscoveryClient
    Будет включен по умолчанию при добавлении стартера c Edgware
    spring-cloud release train

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  233. {
    "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
    },
    ...

    View Slide

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

    View Slide

  235. True Discovery
    ● Изменить логику резолва сервиса использую имя сервиса
    Use @LoadBalanced or LoadBalancerInterceptor Luke

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  244. Fluent annotations
    244
    @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

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

    View Slide

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

    View Slide

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

    View Slide

  248. Your own Config Server
    ● Создать модуль config-server
    compile 'org.springframework.cloud:spring-cloud-config-client'
    @EnableConfigServer
    spring:
    cloud:
    config:
    server:
    git:
    uri: https://github.com/lavcraft/spring-boot-and-cloud-workshop-joker2017-configs.git

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  264. Spring Cloud Bus

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  273. Spring Cloud Gateway

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  279. Reverse Proxy Problem
    @EnableZuulProxy
    vs
    @EnableZuulServer

    View Slide

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

    View Slide

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

    View Slide

  282. 282

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  288. 288
    Circuit Breaker

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  294. hystrix/apache camel/akka
    294

    View Slide

  295. 295

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  300. spring-cloud-sleuth/open zipkin
    300

    View Slide

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

    View Slide

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

    View Slide

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

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

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

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

  307. 307

    View Slide

  308. 308

    View Slide

  309. 309
    Rent Service
    Payment Service

    View Slide

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

    View Slide

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

    View Slide

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

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

  314. documentation → smart documentation
    314

    View Slide

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

    View Slide

  316. 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[]
    316

    View Slide

  317. 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[]
    317

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide