Slide 1

Slide 1 text

Spring {Boot,Cloud} Workshop

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

The project structure

Slide 8

Slide 8 text

web.xml org.springframework.web.context.ContextLoaderListener springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* mvc-dispatcher org.springframework.web.servlet.DispatcherServlet 1 mvc-dispatcher /

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

… contextClass param-name> …AnnotationConfigWebApplicationContext param-value> contextConfigLocation param-name> com.inwhite.conf.AppConfig param-value> …

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

WebApplicationInitializer

Slide 18

Slide 18 text

ServletContainerInitializer

Slide 19

Slide 19 text

WebApplicationInitializer

Slide 20

Slide 20 text

ServletContainerInitializer

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

SPI org.springframework.web.SpringServletContainerInitializer content

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Spring ServletContainer Initializer tomcat 7 Spring Jar

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

WebApplicationInitializer W AI WAI tomcat 7 JARS

Slide 40

Slide 40 text

W AI WAI SpringServletContainerInitializer tomcat 7

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Как выглядит наш pom.xml org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Это чужой parent!

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Как выглядит наш 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' } }

Slide 55

Slide 55 text

А я не понял

Slide 56

Slide 56 text

Web Starter ТАЩИТ!

Slide 57

Slide 57 text

Кто всё пакует org.springframework.boot spring-boot-maven-plugin

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

JarLauncher

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

org.springframework.boot spring-boot-maven-plugin conference...OurMainClass MANIFEST.MF ... Start-Class: conference.spring.boot.ripper.OurMainClass ...

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

Хочу executable jar: Maven org.springframework.boot spring-boot-maven-plugin true springBoot { executable = true } Gradle

Slide 72

Slide 72 text

Demo. Executable jar

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

Demo. jar structure

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Типы ConfigurableApplicationContext

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

Web Context Generic Context

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Web Context Generic Context

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

Откуда 436 spring beans?

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

@SpringBootApplication

Slide 96

Slide 96 text

@SpringBootApplication → @ComponentScan → @Configuration → @EnableAutoConfiguration

Slide 97

Slide 97 text

@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 { …

Slide 98

Slide 98 text

@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 { …

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

@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

Slide 102

Slide 102 text

@EnableAutoConfiguration ImportSelector Web Starter Boot Starter … Starter Mongo Starter

Slide 103

Slide 103 text

Spring Factories Loader Jar/Starter spring.factories SpringFactoriesLoader

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

@ConditionalOn***

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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 ФабрикаГильотин( "хрусть хрусть"); } }

Slide 120

Slide 120 text

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. Будет отлично работать

Slide 121

Slide 121 text

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. Будет отлично работать

Slide 122

Slide 122 text

Потому что ASM

Slide 123

Slide 123 text

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; } Что если нужно изменить?

Slide 124

Slide 124 text

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

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

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

Slide 134

Slide 134 text

§ 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); } } Работает по разному

Slide 135

Slide 135 text

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

Slide 136

Slide 136 text

§ 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

Slide 137

Slide 137 text

§ 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 плагины Обязательно

Slide 138

Slide 138 text

§ 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); } } Опционально

Slide 139

Slide 139 text

§ 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 контейнере Опционально

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

No content

Slide 146

Slide 146 text

Замочили web.xml

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

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

Slide 153

Slide 153 text

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": "петух" }] }] }

Slide 154

Slide 154 text

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

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

@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() { }

Slide 159

Slide 159 text

@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() { }

Slide 160

Slide 160 text

@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() { }

Slide 161

Slide 161 text

@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) { }

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

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

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

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

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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": "петух" }] }] }

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

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

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

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

Slide 191

Slide 191 text

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

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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

Slide 202

Slide 202 text

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

Slide 203

Slide 203 text

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

Slide 204

Slide 204 text

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

Slide 205

Slide 205 text

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

Slide 206

Slide 206 text

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

Slide 207

Slide 207 text

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

Slide 208

Slide 208 text

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

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

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

Slide 211

Slide 211 text

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

Slide 212

Slide 212 text

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

Slide 213

Slide 213 text

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

Slide 214

Slide 214 text

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

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

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

Slide 217

Slide 217 text

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

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

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

Slide 220

Slide 220 text

Service Registry – Eureka

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

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

Slide 223

Slide 223 text

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

Slide 224

Slide 224 text

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

Slide 225

Slide 225 text

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

Slide 226

Slide 226 text

Переводим на 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'

Slide 227

Slide 227 text

Переводим на 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/

Slide 228

Slide 228 text

Переводим на 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/

Slide 229

Slide 229 text

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

Slide 230

Slide 230 text

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

Slide 231

Slide 231 text

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

Slide 232

Slide 232 text

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

Slide 233

Slide 233 text

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

Slide 234

Slide 234 text

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

Slide 235

Slide 235 text

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

Slide 236

Slide 236 text

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

Slide 237

Slide 237 text

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

Slide 238

Slide 238 text

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

Slide 239

Slide 239 text

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

Slide 240

Slide 240 text

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

Slide 241

Slide 241 text

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

Slide 242

Slide 242 text

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

Slide 243

Slide 243 text

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

Slide 244

Slide 244 text

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

Slide 245

Slide 245 text

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

Slide 246

Slide 246 text

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

Slide 247

Slide 247 text

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

Slide 248

Slide 248 text

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

Slide 249

Slide 249 text

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

Slide 250

Slide 250 text

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

Slide 251

Slide 251 text

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

Slide 252

Slide 252 text

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

Slide 253

Slide 253 text

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

Slide 254

Slide 254 text

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

Slide 255

Slide 255 text

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

Slide 256

Slide 256 text

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

Slide 257

Slide 257 text

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

Slide 258

Slide 258 text

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

Slide 259

Slide 259 text

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

Slide 260

Slide 260 text

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

Slide 261

Slide 261 text

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

Slide 262

Slide 262 text

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

Slide 263

Slide 263 text

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

Slide 264

Slide 264 text

Spring Cloud Bus

Slide 265

Slide 265 text

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

Slide 266

Slide 266 text

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

Slide 267

Slide 267 text

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

Slide 268

Slide 268 text

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

Slide 269

Slide 269 text

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

Slide 270

Slide 270 text

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

Slide 271

Slide 271 text

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

Slide 272

Slide 272 text

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

Slide 273

Slide 273 text

Spring Cloud Gateway

Slide 274

Slide 274 text

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

Slide 275

Slide 275 text

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

Slide 276

Slide 276 text

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

Slide 277

Slide 277 text

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

Slide 278

Slide 278 text

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

Slide 279

Slide 279 text

Reverse Proxy Problem @EnableZuulProxy vs @EnableZuulServer

Slide 280

Slide 280 text

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

Slide 281

Slide 281 text

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

Slide 282

Slide 282 text

282

Slide 283

Slide 283 text

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

Slide 284

Slide 284 text

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

Slide 285

Slide 285 text

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

Slide 286

Slide 286 text

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

Slide 287

Slide 287 text

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

Slide 288

Slide 288 text

288 Circuit Breaker

Slide 289

Slide 289 text

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

Slide 290

Slide 290 text

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

Slide 291

Slide 291 text

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

Slide 292

Slide 292 text

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

Slide 293

Slide 293 text

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

Slide 294

Slide 294 text

hystrix/apache camel/akka 294

Slide 295

Slide 295 text

295

Slide 296

Slide 296 text

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

Slide 297

Slide 297 text

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

Slide 298

Slide 298 text

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

Slide 299

Slide 299 text

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

Slide 300

Slide 300 text

spring-cloud-sleuth/open zipkin 300

Slide 301

Slide 301 text

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

Slide 302

Slide 302 text

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

Slide 303

Slide 303 text

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

Slide 304

Slide 304 text

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

Slide 305

Slide 305 text

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

Slide 306

Slide 306 text

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

Slide 307

Slide 307 text

307

Slide 308

Slide 308 text

308

Slide 309

Slide 309 text

309 Rent Service Payment Service

Slide 310

Slide 310 text

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

Slide 311

Slide 311 text

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

Slide 312

Slide 312 text

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

Slide 313

Slide 313 text

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

Slide 314

Slide 314 text

documentation → smart documentation 314

Slide 315

Slide 315 text

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

Slide 316

Slide 316 text

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

Slide 317

Slide 317 text

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

Slide 318

Slide 318 text

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

Slide 319

Slide 319 text

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

Slide 320

Slide 320 text

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