首页 文章

没有Spring Boot的Spring Cloud Config客户端

提问于
浏览
28

我们将现有的Spring Web应用程序部署为Amazon Elastic Beanstalk中的WAR文件 . 目前,我们将属性文件作为http资源加载,以便为我们提供单一的属性占位符配置解析源 . 我正在调查用新的Spring Cloud 配置服务器替换它,以便为我们提供git版本控制等的好处 .

但是文档(http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html)似乎只描述了一个Spring Boot客户端应用程序 . 是否可以在现有的Web应用程序中设置Spring Cloud Config Client?我是否需要手动设置Bootstrap父应用程序上下文等 - 这是否有任何示例?我们当前的spring配置是基于XML的 .

5 回答

  • 4

    使用Spring Boot“正常工作”的一切实际上只不过是一些配置 . 这一切都只是一天结束时的Spring应用程序 . 所以我相信你可以手动设置Boot自动为你做的一切,但我不知道有人真正尝试过这个特定的角度 . 创建引导程序应用程序上下文当然是首选方法,但是如果确保属性源定位器足够早地执行,则根据您的用例,您可以使用单个上下文 .

    非Spring(或非Spring Boot)应用程序可以访问配置服务器中的纯文本或二进制文件 . 例如 . 在Spring中,您可以使用 @PropertySource ,其资源位置是一个URL,如 http://configserver/{app}/{profile}/{label}/application.propertieshttp://configserver/{app}-{profile}.properties . 这些都包含在用户指南中 .

  • 1

    我有类似的要求;我有一个Web应用程序,它使用Spring XML配置来定义一些bean,属性的值存储在.property文件中 . 要求是应在开发期间从硬盘加载配置,并从 生产环境 环境中的Spring Cloud Config服务器加载配置 .

    我的想法是对PropertyPlaceholderConfigurer有两个定义;第一个将用于从硬盘加载配置:

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

    第二个将从Spring Config Server加载.properties:

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

    发布作为答案,因为我没有足够的评论来评论Dave Syer的优秀答案 . 我们已经在 生产环境 中实施了他提出的解决方案,它正在按预期工作 . 我们的新应用程序是使用Boot编写的,而我们的旧应用程序使用的是Spring,但不能启动 . 我们能够使用Spring Cloud Config创建一个为两者提供属性的属性服务 . 变化很小 . 我将遗留属性文件从war文件移到属性服务git存储库,并将属性定义从类路径引用更改为URL,如Dave所描述和插入我们的系统环境变量,就像我们对类路径所做的那样 . 这很简单有效 .

    <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" />
    
  • 2

    参考:https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

    Root WebApplicationContext和Servlet WebApplicationContext使用Environment并基于spring配置文件初始化PropertySources . 对于非Spring启动应用程序,我们需要自定义这些应用程序以从Config Server获取属性,并在存在属性更改时刷新Bean . 以下是在SpringMVC中使配置工作所做的更改 . 你还需要 spring.profile.active 的系统属性

    • 创建一个CustomBeanFactoryPostProcessor并将所有bean定义上的lazyInit设置为true以便懒惰地初始化所有bean,即仅在请求时初始化bean .
    @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);
      }
    }
    
    • 创建一个扩展StandardServletEnvironment的自定义类,并重写initPropertySources方法以加载其他PropertySources . (来自配置服务器)
    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);
        }
    
      }
    
    • 创建自定义ApplicatonContextInitializer并覆盖initialize方法以设置自定义Enviroment而不是StandardServletEnvironment .
    public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.setEnvironment(new CloudEnvironment());
      }
    }
    
    • 修改web.xml以将此自定义上下文初始化程序用于应用程序上下文和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>
    
    • 要刷新创建刷新 endpoints 的bean,您还需要刷新应用程序Context .
    @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();
        }
    }
    
  • 3

    我找到了一个使用spring-cloud-zookeeper而没有Spring Boot的解决方案,基于这里提供的想法https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html

    应该可以轻松更新以满足您的需求并使用Spring Cloud Config Server(需要更新CloudEnvironement类以从服务器而不是Zookeeper加载文件)

    首先,创建一个CloudEnvironement类,它将创建一个PropertySource(来自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()); 
      } 
    
    }
    

    然后创建一个自定义的XmlWebApplicationContext类:它将允许在webapplication启动时从Zookeeper加载PropertySource并替换Spring Boot的引导魔法:

    MyConfigurableWebApplicationContext.java

    public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext { 
    
      @Override 
      protected ConfigurableEnvironment createEnvironment() { 
        return new CloudEnvironment(); 
      } 
    }
    

    最后,在 web.xml 文件中添加以下context-param,以便使用MyConfigurableWebApplicationContext类并引导CloudEnvironement .

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

    如果使用标准属性文件configurer,则仍应加载它所以你可以在本地文件和Zookeeper中都有属性 .

    要使所有这些工作,您需要在类路径中使用spring-cloud-starter-zookeeper-config和curator-framework jar及其依赖性,如果您使用maven,则可以将以下内容添加到 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>
    

相关问题