Share
Embed
Spring Boot И Не Только
В программе
* spring boot
* способы его расширения и заточки под нужды компании
* сложности возникающие при масштабировании подходов работы с микросервисами
* подходы к их решению с помощью spring boot и gradle
* немного о том как работать в команде и к чему нужно стремиться
Учитесь, развивайтесь, ходите на конференции :)
Промокоды на конференции в 2020 году:
https://jpoint.ru/ – JavaMentorJpointPC
http://heisenbug.ru/ – JavaMentorHeisenbugPC
Kirill Tolkachev
March 01, 2020
Transcript
381 slides
-
Начнём? Господин Best Practice Господин «Аастанавись, Задумайся» Господин тимлид –
он за контроль Господин «№;$?% Магия…» Господин, который просто классно шутит 5 -
Цели Мы пошли в микросервисы, какие проблемы ожидать и как
их решать? Чем разработчику могут помочь Spring Boot и другие инструменты, и какие могут быть проблемы? 9 -
Disclaimer Слушайте нас, но используйте свои мозги Писать код не
будем – есть доки Только опыт, логика и выводы 12 -
План 1. Spring boot 2. Spring Boot 3. Что-то еще
4. Перерыв 5. Spring Boot 6. Выводы 14 -
И как выбирают? Корпоративный стандарт 1. Только Java 2. Только
Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> От компании 17 -
И как выбирают? Корпоративный стандарт 1. Только Java 2. Только
Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> Какой стандарт? 1. Go 2. Rust 3. Unicorn 4. Смузи От компании От разработчика 18 -
Ну, а если серьезно? • Обладает нужной функциональностью • Вписывается
в нефункциональные требования • Поддержка и количество тем на stackoverflow • Знания и интересы команды 19 -
Серьезно? • Обладает нужной функциональностью • Вписывается в нефункциональные требования
• Поддержка и количество тем на stackoverflow • Знания и интересы команды 20 -
Что такое Spring Boot? Эта jar-ник c Tomcat внутри Java
interview 2014 26 -
Java interview 2017 Что такое Spring Boot? Эта хрень помогает
проще собрать микросервис 27 -
@Getter @Setter @Aspect @ToString @EnableWs @Endpoint @EnableWebMvc @EnableCaching @Configuration @RestController
@XmlRootElement @EnableWebSocket @RedisHash("cat") @EnableScheduling @EnableWebSecurity @NoArgsConstructor @ContextConfiguration @SpringBootApplication @Accessors(chain = true) @EnableAspectJAutoProxy @EnableAutoConfiguration @EnableRedisRepositories @EnableWebSocketMessageBroker // generate getters // generate setters // we are an aspect // generate toString() // SOAP is so enterprisy, we definitely need it // Seriously, just read above // we want MVC // and we want to cache stuff // this class can configure itself // we want some REST // this component is marshallable // we want web socket, it's so new-generation // this class is an entity saved in redis // we want scheduled tasks // and some built-in security // generate no args constructor // we want context configuration for unit testing // this is a Sprint Boot application // getters/setters are chained (ala jQuery) // we want AspectJ auto proxy // and auto configuration // since it is an entity we want to enable spring data repositories for redis // we want a broker for web socket messages 28 -
@SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { … 39 -
@EnableAutoConfiguration @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration
{ String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; } 40 -
AutoConfigurationImportSelect or protected List<String> getCandidateConfigurations(...) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); ... return configurations; } 41 -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config 57 -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs 58 -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs • Data • Stream 59 -
Простое общение с @FeignClient("cards-api") public interface P2PCardService { @RequestMapping(value =
"/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); } 79 -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) 80 -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) 81 -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) 82 -
Много странного кода public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ...
@Override public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) 84 -
Конфигурация фильтра eureka: client: registryFetchIntervalSeconds: 5 serviceUrl: defaultZone: ${EUREKA_SERV:http://127.0.0.1:8763/eureka/} instance:
virtualHostName : ${spring.application.name} metadataMap: instanceId: ${spring.application.name}:${random.value} versions: ["1.0", "2.0"] spring: discovery: filter: versions: someServiceId: 1.0 89 -
Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:
filter: versions: someServiceId: 1.0 90 -
Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:
filter: versions: someServiceId: 1.0 2.0 1.0 91 -
Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:
filter: versions: someServiceId: 1.0 1.0 2.0 1.0 2.0 92 -
Конфигурация фильтра eureka: client: metadataMap: versions: ["1.0", "2.0"] spring: discovery:
filter: versions: someServiceId: 1.0 93 -
Хочется видеть что-то такое spring: discovery: filter: versions: someServiceId: 1.0
api: versions: ["1.0", "2.0"] 94 -
Надо просто переложить spring: discovery: api: versions: ["1.0", "2.0"] eureka:
client: metadataMap: versions: ["1.0", "2.0"] 95 -
spring: discovery: filter: versions: someServiceId: 1.0 api: versions: ["1.0", "2.0"]
Хочется видеть что-то такое И проверять!!! 96 -
ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error
java.lang.IllegalStateException: Version of supported API should be specified, e.g.: spring: discovery: api: versions: ["1.0", "2.0"] Run Application 97 -
EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void
postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { } translateClientVersionProperty(env); translateZuulRoutes(env); } … } 98 -
@Configuration public class DiscoveryVersionFilterConfiguration { @Bean public void propertyTranslator() {
} return new PropertyTranslatorPostProcessor(); } … } Создаём EPP 99 -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 102 -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 103 -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 104 -
запуск SpringApplicationInitializer Локальные бины приложения созданы ApplicationEnvironment PreparedEvent запуск EnvironmentPostPorcessors
Пользовательские бины создаются слишком поздно 105 -
Условия @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration
{ @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... } 110 -
Конфигурация @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration
{ @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... } 111 -
Вся магия 1. @EnableAutoConfiguration 2. @ConditionalOn… 3. spring.factories Все не
так сложно, как кажется 115 -
Подробнее – смотри доклад Boot Yourself, Spring is Coming https://habr.com/ru/company/jugru/blog/424503/
https://habr.com/ru/company/jugru/blog/425333/ -
Дело не в spring-cloud → Умное логирование → Web-фильтры с
доп. логикой → Любые другие расширения базовых механизмов 118 -
Доработка и расширение механизмов Spring должны быть в стартерах используй
инструменты в соответствии с уже заложенными принципами -
SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"
... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message> <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service"> 125 -
SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"
... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service"> Java Stub wsdl2java tool 126 -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean 128 -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans> 129 -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans> Call Bean Inject ws bean 130 -
SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub
wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean 131 -
SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub
wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean 132 -
CXF Stub в библиотеки ws-stub transfers-api cards-api ...-api dependencies {
compile 'ru.alfabank.ws:currency-stub:1.0.0' } 135 -
<beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой 136 -
<beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой 137 -
WS – ты кто такой transactions-api – ws.xml targeting-api –
ws.xml transfers-api – ws.xml ... 140 -
1. Добавить зависимость (WSDL/WS-STUB) 2. Настроить бины в ws.xml 3.
Прописать адреса и настройки для добавленных сервисов ${lb.address.base} Как сделать новый RPC вызов 141 -
Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.
Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? 142 -
Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.
Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? Captain Copy-Paste 143 -
Run Application ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup
failed Error creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType 146 -
ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error
creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Run Application 147 -
ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error
creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Process finished with exit code 1 Run Application 148 -
Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...
Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType 150 -
Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...
Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory WSCardsTransactions12PortType Configure Context 151 -
Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory
WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints 152 -
Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory
WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints Или падает с ошибкой если не настроен endpoint FAIL-FAST Error creating bean with name 'ru.alfabank...WSSomeServicet13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank....WSSomeServicet13PortType 153 -
@Slf4j @Configuration @EnableConfigurationProperties @ConditionalOnProperty(name = "spring.cxf.client.enabled", matchIfMissing = true) public
class CxfClientConfiguration { @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment environment) { return new CxfBeanDefinitionPostProcessor(environment); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } @Slf4j @Configuration @ConditionalOnClass({SpringBus.class, JaxWsClientFactoryBean.class, ConfigurationPropertiesBindingPostProcessor.class}) @EnableConfigurationProperties({CxfClientsProperties.class, WSConfiguration.class}) public static class CxfClientFactoryAutoConfiguration { @Bean(name = CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME) @ConditionalOnMissingBean(name = {CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME}) CxfWsStubBeanFactory proxyWsBeanFactory( CxfClientsProperties cxfClientsProperties, Bus bus, CxfInterceptorConfigurer interceptorConfigurer ) { return new CxfWsStubBeanFactory( cxfClientsProperties, bus, interceptorConfigurer ); } Конфигурация библиотек @Bean @ConditionalOnMissingBean(CxfBusConfigurer.class) public CxfBusConfigurer cxfBusConfigurer(CxfClientsProperties cxfClientsProperties) { return new DefaultCxfBusConfigurer(cxfClientsProperties); } @Bean(destroyMethod = "shutdown") public Bus cxf(CxfBusConfigurer cxfBusConfigurer) { SpringBus bus = new SpringBus(); cxfBusConfigurer.configure(bus); return bus; } @Bean @ConditionalOnMissingBean(CxfInterceptorConfigurer.class) public CxfInterceptorConfigurer cxfInterceptorConfigurer( CxfInterceptorAnnotationProcessor cxfInterceptorAnnotationProcessor, BeanFactory beanFactory ) { return new CxfInterceptorConfigurer( beanFactory, cxfInterceptorAnnotationProcessor.getGlobalInterceptors(), cxfInterceptorAnnotationProcessor.getSpecificInterceptors() ); } @Bean @ConditionalOnMissingBean(CxfInterceptorAnnotationProcessor.class) public static CxfInterceptorAnnotationProcessor cxfInterceptorBFPP() { return new CxfInterceptorAnnotationProcessor(); } static final String CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME = "CxfWsClientProxyFactory"; } } 156 -
@Slf4j @Configuration @EnableConfigurationProperties @ConditionalOnProperty(name = "spring.cxf.client.enabled", matchIfMissing = true) public
class CxfClientConfiguration { @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment environment) { return new CxfBeanDefinitionPostProcessor(environment); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } @Slf4j @Configuration @ConditionalOnClass({SpringBus.class, JaxWsClientFactoryBean.class, ConfigurationPropertiesBindingPostProcessor.class}) @EnableConfigurationProperties({CxfClientsProperties.class, WSConfiguration.class}) public static class CxfClientFactoryAutoConfiguration { @Bean(name = CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME) @ConditionalOnMissingBean(name = {CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME}) CxfWsStubBeanFactory proxyWsBeanFactory( CxfClientsProperties cxfClientsProperties, Bus bus, CxfInterceptorConfigurer interceptorConfigurer ) { return new CxfWsStubBeanFactory( cxfClientsProperties, bus, interceptorConfigurer ); } Конфигурация библиотек @Bean @ConditionalOnMissingBean(CxfBusConfigurer.class) public CxfBusConfigurer cxfBusConfigurer(CxfClientsProperties cxfClientsProperties) { return new DefaultCxfBusConfigurer(cxfClientsProperties); } @Bean(destroyMethod = "shutdown") public Bus cxf(CxfBusConfigurer cxfBusConfigurer) { SpringBus bus = new SpringBus(); cxfBusConfigurer.configure(bus); return bus; } @Bean @ConditionalOnMissingBean(CxfInterceptorConfigurer.class) public CxfInterceptorConfigurer cxfInterceptorConfigurer( CxfInterceptorAnnotationProcessor cxfInterceptorAnnotationProcessor, BeanFactory beanFactory ) { return new CxfInterceptorConfigurer( beanFactory, cxfInterceptorAnnotationProcessor.getGlobalInterceptors(), cxfInterceptorAnnotationProcessor.getSpecificInterceptors() ); } @Bean @ConditionalOnMissingBean(CxfInterceptorAnnotationProcessor.class) public static CxfInterceptorAnnotationProcessor cxfInterceptorBFPP() { return new CxfInterceptorAnnotationProcessor(); } static final String CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME = "CxfWsClientProxyFactory"; } } 157 -
Конфигурация библиотек @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment env) { return new
CxfBeanDefinitionPostProcessor(env); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } 158 -
Как все это подключать? • Чтение документации • Debug •
Найти того, кто это писал... 161 -
Конфигурация библиотек Или, не дай бог : @ComponentScan( { "ru.alfa.cxf",
"ru.alfa.discovery", "ru...", "ru..." ... } ) 164 -
Проблемы Толстый сервис 1. Необходимость знания кишков библиотек 2. Лишние
куски кода в самом сервисе 3. Случайное подхваченные бины 166 -
Не в SOAP дело • Thrift/gRPC • Netty • Любая
сложная или устаревшая внешняя зависимость 167 -
Сложность конфигурации библиотеки сопоставима со сложностью логики библиотеки Даешь инверсию
контроля не только для компонентов Spring! -
А какие ещё стартеры бывают? • Tracing • Logging •
Health indicators • Monitoring • Etc 170 -
А какие ещё стартеры бывают? • Tracing • Logging •
Health indicators • Monitoring • Etc • Какие-то ваши стартеры 171 -
Стандартного набора хватит всем! Зачем писать свое? И как тестировать?
Опытный тех. лид 177 -
Э не не не. Нам нужны свои 1. Не для
всего есть стартеры 2. Мы можем их тестировать 179 -
• ApplicationContextRunner • @SpringBootTest • Всё остальное тестируется как обычный
spring код Spring Boot Test 182 -
Структура +-- starter +--- autoconfigure +--- starter \--- examples +--
demo-spec-0 +-- demo-spec-1 \-- demo-spec-N 183 -
Структура +-- starter +--- autoconfigure +--- starter \--- examples +--
demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 184 -
Структура +-- starter +--- autoconfigure +--- starter \--- examples +--
demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 185 -
Структура +-- starter +--- autoconfigure +--- starter \--- examples +--
demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 186 -
Структура +-- starter +--- autoconfigure +--- starter \--- examples +--
demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 187 -
Failed to load ApplicationContext Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition
with name 'ru.some.pack.client.SomeClient' defined in null: Could not resolve placeholder some.url' in value "http://${some.url}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Первое использование 190 -
starter/autoconfigure — tests Unit — per class Component — per
functionality/per spec 192 -
Starter component tests Сформировать часть контекста нам помогут: 1. @SpringBootTest
2. @TestConfiguration 3. @ContextConfiguration 4. ApplicationContextRunner 193 -
@SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { @Requires({
!os.isMacOs() }) def 'should apply timeout to ASYNC requests'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == SocketTimeoutException } ... 195 -
@SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { ...
@Requires({ os.isMacOs() }) def 'should apply timeout to ASYNC requests for OSX'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == ConnectException } 196 -
@ContextConfiguration @ContextConfiguration(classes = ValidConfiguration) class InterceptorConfigurerTest extends Specification { @Autowired
CxfInterceptorConfigurer interceptorConfigurer def "should add interceptors to JaxWsProxyFactoryBean"() { when: interceptorConfigurer.configure(...) then: ... } } 197 -
Мы готовы писать свой стартер! Новичок #1 Новичок #2 Новичок
#3 Новичок #4 Странный новичок 201 -
Есть три пути Далёкое Будущее Инфраструктурный путь Альтернативное Будущее Хакерский
путь Суровое настоящее CI/CD путь 206 -
Далекое будущее... или не очень K U B E R
N E T E S 209 -
Далекое будущее... или не очень K U B E R
N E T E S S I D E C A R 210 -
Kubernetes Sidecar POD Application container Sidecar container Могут быть: •
общая файловая система • общая сеть 213 -
Не везде работает • Tracing покрывается не до конца -
коннекты к БД и MQ не обслуживаются 215 -
Не везде работает • Tracing покрывается не до конца -
коннекты к БД и MQ не обслуживаются • Логи покрываются не до конца - приложение в любом случае должно выдавать структурированный лог 216 -
Просто контейнеры Любой оркестратор: • Kubernetes • Mesos • Swarm
• Bash + puppet • Что угодно запускающее контейнер на хосте 220 -
Подопытный CI/CD N сервисов M стартеров N сервисов с M
стартерами 233 -
Добавляем стартеры transactions-api/build.gradle: dependencies { compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile
'mongodb-starter:0.2.0' … } // → 'hazelcast:3.6.9' // → 'redis:1.1.1' // → 'mongodb:3.6.0' Транзитивные зависимости 244 -
Добавляем стартеры Hazelcast Cluster v2.x.x Hazelcast Client v3.x.x +--- hazelcast-starter:1.0.0
\--- hazelcast:3.6.6 246 -
Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'
compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } 247 -
Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'
compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0' compile 'fat-starter:100.50.0' } 248 -
Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'
compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0' compile 'fat-starter:100.50.0' } 249 -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... 253 -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ... 254 -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ... Captain Copy-Paste 255 -
'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'
'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 259 -
'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'
'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' Ну такое.. 260 -
'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'
'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' Ну такое.. Буду складывать jar`ы в VCS 261 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 262 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 263 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 264 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 265 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 266 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 267 -
Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0
'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 268 -
Выход №1 – latest version dependencies { implementation 'some-starter:0.1.0' //
→ 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... } 270 -
Выход №1 – latest version + locking dependencyLocking { lockAllConfigurations()
} dependencies { implementation 'some-starter:0.1.0' // → 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... } 272 -
Выход №1 – latest version + locking $ ./gradlew build
--write-locks 273 -
$ ./gradlew build --write-locks $ ./gradlew build --update-locks ru.company:starter Выход
№1 – latest version + locking 274 -
Выход №1 – latest version + locking $ ./gradlew build
--write-locks $ ./gradlew build --update-locks ru.company:starter $ git status modified: gradle/dependency-locks/annotationProcessor.lockfile modified: gradle/dependency-locks/buildscript-classpath.lockfile modified: gradle/dependency-locks/compileClasspath.lockfile modified: gradle/dependency-locks/runtimeClasspath.lockfile modified: gradle/dependency-locks/testAnnotationProcessor.lockfile modified: gradle/dependency-locks/testCompileClasspath.lockfile modified: gradle/dependency-locks/testRuntimeClasspath.lockfile 275 -
Выход №1 – latest version + locking diff ./gradle/dependency-locks/buildscript-classpath.lockfile: -com.netflix.nebula:nebula-publishing-plugin:11.0.0
-com.netflix.nebula:nebula-release-plugin:10.1.1 +com.netflix.nebula:nebula-publishing-plugin:12.0.0 +com.netflix.nebula:nebula-release-plugin:10.1.2 276 -
rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest
version + locking update-lock 277 -
rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest
version + locking update-lock 278 -
rebuild rebuild rebuild rebuild rebuild rebuild Выход №1 – latest
version + locking update-lock 279 -
Хей, у нас новые новая система метрик! Пример Инф раструктура
Но я не умею экспортировать метрики в таком формате 282 -
Хей, у нас новые новая система метрик! Пример Но я
не умею экспортировать метрики в таком формате Инф раструктура Ща мы тебя пересоберём 283 -
Ща мы тебя пересобирём Пример Инф раструктура Теперь я умею
экспортироавть Метрики в нужном формате 286 -
Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в
нужном формате Пример Инф раструктура Ща мы тебя задеплоим! 287 -
Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в
нужном формате Пример Инф раструктура Ща мы тебя задеплоим! 288 -
build.gradle: apply plugin: "version.plugin" apply plugin: "maven.plugin" apply plugin: "check.plugin"
apply plugin: "findbugs.plugin" apply plugin: "verify.plugin" apply plugin: "publish.plugin" apply plugin: "awesome.plugin" 294 Декларативный подход -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } } } Сила в композиции 295 -
Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj
{ version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines 302 -
Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj
{ version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines ugin 303 -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 304 -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) project.tasks.withType(SomeTaskType) { //configure } } } 305 Сила в композиции -
Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 307 Автоапдейт минорных
версий Путь героев – но это уже другая история -
Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 308 Автоапдейт минорных
версий Путь героев – но это уже другая история И dependency locking тоже работает! -
На больших проектах нужна инверсия контроля для сборки и зависимостей
Идея Gradle plugin 310 -
Как это работает starter Gradle plugin Любое изменение – повод
пересобрать всё и передеплоить dependency 312 -
Как это работает build rebuild rebuild rebuild rebuild rebuild rebuild
Обновление приводит к сборке новой версии сервисов dependency 313 -
rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy
ALL production И последующему деплою 314 -
rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy
ALL production И последующему деплою 315 -
rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy
ALL test,dev,prod И последующему деплою и тестированию 316 -
Актуализация версий Ручное + понятно + стабильно - геморойно -
сложно масштабировать - нет контроля запуска - ... CI/CD + единый процесс для всех + стабильно - много работы - цементирует процессы - СI/CD — 24/7 - ... Runtime + Всё из CI/CD + без пересборки + быстрый запуск + полный контроль - очень много работы - опасно - сложно тестировать - ... 318 -
Update strategy • Скачать и поднять новый инстанс • Дождаться
пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс 326 -
Update strategy • Скачать и поднять новый инстанс • Дождаться
пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс 327 -
На что тратится время при старте? • Старт jvm •
Загрузка классов • ??? 330 -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... 334 -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Классы проаннотированные аннотацией Spring`а 335 -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Чем проаннотировано 336 -
Не там копаем indexer Я старался, но у тебя бинов
не так много 338 -
Радикальный путь К черту ваш Spring, у меня новый друг
TEAM LEAD MICRONAUT Я сделан по новым принципам 339 -
Новый подход MICRONAUT • AST-трансформации для Groovy • Annotation Processor
для Java/Kotlin 340 -
Новый подход MICRONAUT Micronaut делает IoC/DI в во время компиляции
Я быстрее, потому что делаю все заранее 341 -
Новый подход на Spring Spring Fu val app = application(WebApplicationType.SERVLET)
{ logging { level = LogLevel.DEBUG } beans { bean<SampleService>() } webMvc { router { val service = ref<SampleService>() GET("/") { ok().body(service.generateMessage()) } } converters { string() jackson { indentOutput = true 343 -
название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 6
950 +- 0.04 spring boot 11 1500 +- 0.1 spark 3 350 +- 0.01 spring Fu 5 1041 +- 0.08 Сравним время старта 344 -
Не похоже на реальную жизнь Зачем мне голый сервис, где
БД/MQ/Cloud??? TEAM LEAD 345 -
название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 19
2400 spring boot 15 3400 Если с mongo 346 -
название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 15
3000 spring boot 24 8000 Если с DB/MQ/Cloud 347 -
GraalVM - в погоне за скоростью - Собираем долго -
Запускаем быстро 352 -
Где оптимизировать • Не забывать обновляться • Следить за лишними
зависимостями 365 -
Где оптимизировать • Не забывать обновляться • Следить за лишними
зависимостями • Следить за тем, что и как инициализируются в приложениях 366 -
Где оптимизировать • Не забывать обновляться • Следить за лишними
зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer 367 -
Где оптимизировать • Не забывать обновляться • Следить за лишними
зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+] 368 -
Где оптимизировать • Не забывать обновляться • Следить за лишними
зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+] • Отказаться от actuator 369 -
Решения на одном уровне порождают проблемы на другом Нельзя закрыть
проблемы всего стека на одном уровне