baeldung - Spring Cloud Config Client Sin Spring Boot
spring cloud git config (5)
Encontré una solución para usar Spring-Cloud-Zookeeper sin Spring Boot, basada en la idea que se proporciona aquí https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
Debe actualizarse fácilmente para satisfacer sus necesidades y utilizar un servidor Spring Cloud Config Server (la clase CloudEnvironement debe actualizarse para cargar el archivo desde el servidor en lugar de Zookeeper)
Primero, cree una clase de CloudEnvironement que creará un PropertySource (ex de Zookeeper):
CloudEnvironement.java
public class CloudEnvironment extends StandardServletEnvironment {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
try {
propertySources.addLast(initConfigServicePropertySourceLocator(this));
}
catch (Exception ex) {
logger.warn("failed to initialize cloud config environment", ex);
}
}
private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) {
ZookeeperConfigProperties configProp = new ZookeeperConfigProperties();
ZookeeperProperties props = new ZookeeperProperties();
props.setConnectString("myzookeeper:2181");
CuratorFramework fwk = curatorFramework(exponentialBackoffRetry(props), props);
ZookeeperPropertySourceLocator propertySourceLocator = new ZookeeperPropertySourceLocator(fwk, configProp);
PropertySource<?> source= propertySourceLocator.locate(environment);
return source ;
}
private CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties) {
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.connectString(properties.getConnectString());
CuratorFramework curator = builder.retryPolicy(retryPolicy).build();
curator.start();
try {
curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit());
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
return curator;
}
private RetryPolicy exponentialBackoffRetry(ZookeeperProperties properties) {
return new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(),
properties.getMaxRetries(),
properties.getMaxSleepMs());
}
}
Luego cree una clase XmlWebApplicationContext personalizada: permitirá cargar el PropertySource de Zookeeper cuando su aplicación web comience y reemplace la magia de arranque de Spring Boot:
MyConfigurableWebApplicationContext.java
public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext {
@Override
protected ConfigurableEnvironment createEnvironment() {
return new CloudEnvironment();
}
}
Por último, en su archivo web.xml , agregue el siguiente parámetro de contexto para usar su clase MyConfigurableWebApplicationContext y detener su CloudEnvironement.
<context-param>
<param-name>contextClass</param-name>
<param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value>
</context-param>
Si usa un configurador de archivos de propiedades estándar, aún debe cargarse para que pueda tener propiedades tanto en un archivo local como en un Zookeeper.
Para que todo esto funcione, debe tener el archivo spring-cloud-starter-zookeeper-config y curator-framework jar en su classpath con su dependencia. Si usa maven, puede agregar lo siguiente a su pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-zookeeper-dependencies</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
</dependencies>
Tenemos una aplicación web de Spring existente implementada como un archivo WAR en Amazon Elastic Beanstalk. Actualmente, cargamos archivos de propiedades como recursos http para proporcionarnos una única fuente de resolución de configuración de marcador de posición de propiedad. Estoy investigando reemplazarlo con el nuevo servidor de configuración de Spring Cloud Cloud para brindarnos los beneficios de las versiones de git, etc.
Sin embargo, la documentación ( http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html ) solo parece describir una aplicación de cliente Spring Boot. ¿Es posible configurar el cliente Spring Cloud Config Client en una aplicación web existente? ¿Debo configurar manualmente el contexto de la aplicación principal de Bootstrap, etc.? ¿Hay algún ejemplo de esto? Nuestra configuración actual de primavera está basada en XML.
Publicar como respuesta porque no tengo suficientes puntos para comentar sobre la excelente respuesta de Dave Syer. Hemos implementado su solución propuesta en producción y está funcionando como se esperaba. Nuestras aplicaciones más nuevas se escriben usando Boot, mientras que nuestras aplicaciones heredadas usan Spring, pero no boot. Pudimos usar Spring Cloud Config para crear un servicio de propiedades que sirve propiedades para ambos. Los cambios fueron mínimos. Moví los archivos de propiedades heredados del archivo war al repositorio de git del servicio de propiedades, y cambié la definición de propiedad de una referencia de classpath a una URL como Dave describe e inserta nuestra variable de entorno del sistema tal como lo hicimos para classpath. Fue fácil y eficaz.
<util:properties id="envProperties" location="https://properties.me.com/property-service/services-#{envName}.properties" />
<context:property-placeholder properties-ref="envProperties" ignore-resource-not-found="true" ignore-unresolvable="true" order="0" />
<util:properties id="defaultProperties" location="https://properties.me.com/property-service/services-default.properties" />
<context:property-placeholder properties-ref="defaultProperties" ignore-resource-not-found="true" ignore-unresolvable="true" order="10" />
Referenciado: https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
Root WebApplicationContext y el Servlet WebApplicationContext utilizan Environment e inicializan PropertySources en función del perfil de Spring. Para las aplicaciones de arranque que no son de primavera, debemos personalizarlas para obtener las propiedades de Config Server y actualizar los beans cuando haya un cambio de propiedad. A continuación se muestran los cambios realizados para que la configuración funcione en SpringMVC. También necesitará una propiedad del sistema para spring.profile.active
Cree un CustomBeanFactoryPostProcessor y establezca lazyInit en todas las definiciones de bean para que sea verdadero para inicializar todo el bean perezos, es decir, los beans se inicializan solo cuando se solicita.
@Component public class AddRefreshScopeProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private static ApplicationContext applicationContext; @SuppressWarnings("unchecked") @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] beanNames = applicationContext.getBeanDefinitionNames(); for(int i=0; i<beanNames.length; i++){ BeanDefinition beanDef = beanFactory.getBeanDefinition(beanNames[i]); beanDef.setLazyInit(true); beanDef.setScope("refresh"); } } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { applicationContext = context; } /** * Get a Spring bean by type. * * @param beanClass * @return */ public static <T> T getBean(Class<T> beanClass) { return applicationContext.getBean(beanClass); } /** * Get a Spring bean by name. * * @param beanName * @return */ public static Object getBean(String beanName) { return applicationContext.getBean(beanName); } }
Cree una clase personalizada que extienda StandardServletEnvironment y reemplace el método initPropertySources para cargar PropertySources adicionales. (desde el servidor de configuración)
public class CloudEnvironment extends StandardServletEnvironment { @Override public void initPropertySources(ServletContext servletContext, ServletConfig servletConfig) { super.initPropertySources(servletContext,servletConfig); customizePropertySources(this.getPropertySources()); } @Override protected void customizePropertySources(MutablePropertySources propertySources) { super.customizePropertySources(propertySources); try { PropertySource<?> source = initConfigServicePropertySourceLocator(this); propertySources.addLast(source); } catch ( Exception ex) { ex.printStackTrace(); } } private PropertySource<?> initConfigServicePropertySourceLocator(Environment environment) { ConfigClientProperties configClientProperties = new ConfigClientProperties(environment); configClientProperties.setUri("http://localhost:8888"); configClientProperties.setProfile("dev"); configClientProperties.setLabel("master"); configClientProperties.setName("YourApplicationName"); System.out.println("##################### will load the client configuration"); System.out.println(configClientProperties); ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(configClientProperties); return configServicePropertySourceLocator.locate(environment); } }
Cree un ApplicatonContextInitializer personalizado y anule el método de inicialización para establecer el entorno personalizado en lugar del entorno de entorno estándar.
public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.setEnvironment(new CloudEnvironment()); } }
Modifique web.xml para usar este inicializador de contexto personalizado tanto para el contexto de la aplicación como para el contexto del servlet.
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextInitializerClasses</param-name> <param-value>com.my.context.ConfigAppContextInitializer</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextInitializerClasses</param-name> <param-value>com.my.context.ConfigAppContextInitializer</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </context-param>
Para actualizar los beans creados en un punto final de actualización, también deberá actualizar el contexto de la aplicación.
@Controller public class RefreshController { @Autowired private RefreshAppplicationContext refreshAppplicationContext; @Autowired private RefreshScope refreshScope; @RequestMapping(path = "/refreshall", method = RequestMethod.GET) public String refresh() { refreshScope.refreshAll(); refreshAppplicationContext.refreshctx(); return "Refreshed"; }
}
RefreshAppplicationContext.java
@Component
public class RefreshAppplicationContext implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void refreshctx(){
((XmlWebApplicationContext)(applicationContext)).refresh();
}
}
Tengo requisitos similares; Tengo una aplicación web que utiliza la configuración XML de Spring para definir algunos beans, el valor de las propiedades se almacena en archivos .property. El requisito es que la configuración se debe cargar desde el disco duro durante el desarrollo y desde un servidor Spring Cloud Config en el entorno de producción.
Mi idea es tener dos definiciones para el PropertyPlaceholderConfigurer; El primero se utilizará para cargar la configuración desde el disco duro:
<bean id="resources" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" doc:name="Bean">
<property name="locations">
<list>
<value>dcm.properties</value>
<value>post_process.properties</value>
</list>
</property>
</bean>
El segundo cargará los .properties desde el servidor de configuración de Spring:
<bean id="resources" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" doc:name="Bean">
<property name="locations">
<list>
<value>http://localhost:8888/trunk/dcm-qa.properties</value>
</list>
</property>
</bean>
Todo lo que "simplemente funciona" con Spring Boot no es más que una configuración. Es solo una aplicación de Spring al final del día. Así que creo que probablemente puedas configurar todo manualmente que Boot hace por ti de forma automática, pero no estoy al tanto de que alguien esté intentando este ángulo en particular. Crear un contexto de aplicación bootstrap es sin duda el enfoque preferido, pero dependiendo de su caso de uso, puede hacer que funcione con un solo contexto si se asegura de que los localizadores de origen de propiedad se ejecuten con la suficiente antelación.
Las aplicaciones que no son de Spring (o que no son Spring Boot) pueden acceder a archivos de texto plano o binarios en el servidor de configuración. Por ejemplo, en Spring podría usar un @PropertySource
con una ubicación de recurso que fuera una URL, como http://configserver/{app}/{profile}/{label}/application.properties
o http://configserver/{app}-{profile}.properties
. Todo está cubierto en la guía del usuario.