2015-02-06 16 views
23

Wir haben eine vorhandene Spring-Web-App als WAR-Datei in Amazon Elastic Beanstalk implementiert. Derzeit laden wir Eigenschaftendateien als http-Ressourcen, um uns eine einzige Quelle der Eigenschaft Platzhalterkonfigurationsauflösung zu geben. Ich untersuche das Ersetzen dieses durch den neuen Frühlingswolkenkonfigurationsserver, um uns die Vorteile der git Versionierung etc. zu geben.Spring Cloud Config-Client ohne Spring Boot

Jedoch scheint die Dokumentation (http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html) nur eine Spring Boot Klientenanwendung zu beschreiben. Ist es möglich, den Spring Cloud Config Client in einer bestehenden Web-App einzurichten? Muss ich den Bootstrap-Hauptanwendungskontext usw. manuell einrichten - gibt es dafür Beispiele? Unsere aktuelle Frühjahrskonfiguration basiert auf XML.

+0

Hallo. Irgendwelche Updates dazu? Ich bin in der gleichen Situation –

+0

@David Geary jemals herausfinden? – Selwyn

+0

Entschuldigung, ich habe mich nicht weiter damit befasst, da es ein bisschen manuelle Anstrengung erfordern würde, um dies in einer nicht-Spring-Boot-App zu erreichen, Duplizieren von Quellcode usw. Leider scheint sich das Spring Cloud-Zeug nur auf Spring Boot zu konzentrieren. –

Antwort

4

Alles, was mit Spring Boot "nur funktioniert" ist eigentlich nicht mehr als eine Konfiguration. Es ist alles nur eine Spring-Anwendung am Ende des Tages. Also glaube ich, dass Sie wahrscheinlich alles manuell einstellen können, dass Boot automatisch für Sie tut, aber ich bin mir nicht bewusst, dass irgendjemand diesen bestimmten Winkel tatsächlich ausprobiert. Das Erstellen eines Bootstrap-Anwendungskontexts ist sicherlich der bevorzugte Ansatz, aber abhängig von Ihrem Anwendungsfall erhalten Sie möglicherweise, dass es mit einem einzelnen Kontext arbeitet, wenn Sie sicherstellen, dass die Quelllokalisierer der Eigenschaft früh genug ausgeführt werden.

Nicht-Spring-Anwendungen (oder nicht Spring-Boot-Anwendungen) können im Konfigurationsserver auf Nur-Text- oder Binärdateien zugreifen. Z.B. im Frühjahr könnten Sie eine @PropertySource mit einem Ressourcenstandort verwenden, der eine URL war, wie http://configserver/{app}/{profile}/{label}/application.properties oder http://configserver/{app}-{profile}.properties. Es ist alles in der Bedienungsanleitung abgedeckt.

+5

Dave @dave, könnten Sie genauere Informationen darüber geben, wie Sie damit fortfahren? Der config-Server würde ein viel breiteres Publikum erreichen, wenn er leicht in existierende Feder-mvc-Anwendungen integriert werden könnte. – cmadsen

+1

Ich stimme mit cmadsen überein, es macht mir nichts aus, den Spring-Boot für den Konfigurationsserver selbst zu benutzen, da dies eine neue Komponente in unserem System wäre, aber der Konfigurations-Client sollte wirklich einfach mit existierendem Code verwendet werden können. Eigentlich sollte es ein "Cloud" -Projekt sein, daher scheint die Integration mit Standard-Spring-Web-Apps, die in etablierten Cloud-Umgebungen wie Elastic Beanstalk eingesetzt werden, ein häufiger Anwendungsfall zu sein. –

+0

Um zu beginnen, schauen Sie sich den Code an, der beim Booten automatisch geladen wird: https://github.com/spring-cloud/spring-cloud-config/blob/master/spring-cloud-config-client/src/main/resources /META-INF/spring.factories Diese Klassen sind die Einstiegspunkte in den Spring-Cloud-Client. Sie könnten damit davonkommen, 'RefreshAutoConfiguration',' LifecycleMvcEndpointAutoConfiguration' und 'RestartListener' zu ignorieren. – spencergibb

3

Ich habe ähnliche Anforderung; Ich habe eine Webanwendung, die Spring XML-Konfiguration verwendet, um einige Beans zu definieren, der Wert der Eigenschaften wird in .property-Dateien gespeichert. Voraussetzung ist, dass die Konfiguration während der Entwicklung von der Festplatte und von einem Spring Cloud Config-Server in der Produktionsumgebung geladen wird.

Meine Idee ist, zwei Definitionen für den PropertyPlaceholderConfigurer zu haben; die erste wird verwendet werden, um die Konfiguration von der Festplatte zu laden:

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

Die zweiten die .properties aus dem Frühjahr Config Server geladen werden:

<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> 
+0

Jetzt brauche ich eine einfache Möglichkeit, Wiederholungen hinzuzufügen. Wie würden Sie diese Bean im Code hinzufügen? – cerebrotecnologico

+0

Das funktioniert nicht für mich. Der Wert für localhost wird nicht von der Anwendung verwendet – Jay

1

Posting als Antwort, weil ich don Ich habe nicht genug Punkte, um Dave Syers ausgezeichnete Antwort zu kommentieren. Wir haben seine vorgeschlagene Lösung in der Produktion implementiert und es funktioniert wie erwartet. Unsere neueren Apps werden mit Boot geschrieben, während unsere älteren Apps Spring verwenden, aber nicht booten. Wir konnten mithilfe von Spring Cloud Config einen Eigenschaftenservice erstellen, der Eigenschaften für beide bereitstellt. Die Änderungen waren minimal. Ich habe die Legacy-Eigenschaftendateien aus der WAR-Datei in das Property-Service-Git-Repository verschoben und die Eigenschaftendefinition aus einer Klassenpfadreferenz in eine URL geändert, als Dave unsere Systemumgebungsvariable beschreibt und einfügt, genau wie bei Classpath. Es war einfach und effektiv.

<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" /> 
+0

Ich fand es nützlich. Aber mein Problem ist, dass der Servername in jeder Umgebung anders sein wird. Ich habe versucht, den Servernamen aus einer anderen Eigenschaftendatei zu erhalten, aber es funktioniert nicht. , Brauche ich etwas wie \t \t . Wie kann ich config.server.url dynamisch setzen? irgendeine Idee – Vins

1

fand ich eine Lösung für die Verwendung von feder Cloud-zookeeper ohne Frühling Stiefel, basierend auf der Idee, hier zur Verfügung gestellten https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

Es sollte leicht Ihre Bedürfnisse aktualisiert werden angepasst und mit einem Feder Cloud-Config Server (muss die CloudEnvironement Klasse, um die Datei laden vom Server statt Zookeeper)

erstellen Sie zunächst eine CloudEnvironement Klasse aktualisiert werden, die eine PropertySource (ex von Zookeeper) schaffen:

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

} 

Dann eine benutzerdefinierte XmlWebApplicationContext Klasse erstellen: es ermöglicht die PropertySource von Zookeeper zu laden, wenn Sie Ihre Webapplikation den Bootstrap Magie des Frühlings-Boot starten und ersetzen:

MyConfigurableWebApplicationContext.java

public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext { 

    @Override 
    protected ConfigurableEnvironment createEnvironment() { 
    return new CloudEnvironment(); 
    } 
} 

Zuletzt fügen Sie in Ihrer Datei web.xml den folgenden Kontextparameter für die Verwendung von y hinzu unsere MyConfigurableWebApplicationContext-Klasse und bootstraining Ihre CloudEnvironement.

<context-param>   
     <param-name>contextClass</param-name> 
     <param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value> 
    </context-param> 

Wenn Sie einen Standardeigenschaftendatei configurer verwenden, sollte es noch geladen werden, so dass Sie sowohl eine lokale Datei und Zookeeper in Eigenschaften haben können.

Für all dies arbeiten Sie Feder-Cloud-Starter-Zoowärter-config und Kurator-Rahmen Glas in Ihrem Classpath mit ihren dependancy haben müssen, wenn Sie Maven verwenden, können Sie die folgenden Ihre pom.xml hinzufügen

<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> 
1

Refrenced: https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

Wurzel WebApplicationContext und die Servlet WebApplicationContext Umgebung verwendet und initialisiert PropertySources auf dem Federprofil. Für Anwendungen ohne Springstart müssen diese angepasst werden, um die Eigenschaften vom Konfigurationsserver abzurufen und die Beans bei jeder Änderung der Eigenschaften zu aktualisieren. Im Folgenden sind die Änderungen aufgeführt, mit denen die Konfiguration in SpringMVC funktioniert hat. Sie müssen auch eine Systemeigenschaft für spring.profile.active

  1. ein CustomBeanFactoryPostProcessor Erstellen und lazyInit auf alle Bohnens Definitionen auf true gesetzt alle Bean zu initialisieren lazily heißt Bohnen nur auf Anforderung initialisiert werden.

    @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); 
        } 
    } 
    
  2. eine benutzerdefinierte Klasse erstellen und das Überschreiben der StandardServletEnvironment initPropertySources Verfahren verlauf zusätzliche PropertySources zu laden. (Von config server)

    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); 
        } 
    
        } 
    
  3. Erstellen eines benutzerdefinierten ApplicatonContextInitializer und überschreiben die Methode initialize die benutzerdefinierte Enviroment anstelle des StandardServletEnvironment einzustellen.

    public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { 
    
    @Override 
    public void initialize(ConfigurableApplicationContext applicationContext) { 
        applicationContext.setEnvironment(new CloudEnvironment()); 
        } 
    } 
    
  4. Ändern web.xml dieses initializer benutzerdefinierten Kontext für beide Anwendungskontext und Servlet-Kontext zu verwenden.

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

  5. die Bohnen So aktualisieren Sie einen Refresh-Endpunkt erstellt auch den Anwendungskontext aktualisieren müssen.

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