![](/img/trans.png)
[英]Spring cloud config client without Eureka, Ribbon and spring boot
[英]Spring Cloud Config Client Without Spring Boot
我们将现有的 Spring Web 应用程序作为 WAR 文件部署到 Amazon Elastic Beanstalk 中。 目前,我们将属性文件作为 http 资源加载,以便为我们提供属性占位符配置解析的单一来源。 我正在研究用新的 spring 云配置服务器替换它,以便为我们提供 git 版本控制等的好处。
然而,文档( http://cloud.spring.io/spring-cloud-config/spring-cloud-config.html )似乎只描述了 Spring Boot 客户端应用程序。 是否可以在现有的 Web 应用程序中设置 Spring Cloud Config Client? 我是否需要手动设置 Bootstrap 父应用程序上下文等 - 有没有这样的例子? 我们当前的 spring 配置是基于 XML 的。
参考: https : //wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
Root WebApplicationContext
和Servlet WebApplicationContext
使用 Environment 并根据 spring 配置文件初始化 PropertySources。 对于非 spring 启动应用程序,我们需要自定义这些以从 Config Server 获取属性并在属性更改时刷新 bean。 以下是使配置在 SpringMVC 中工作所需的更改。 您还需要spring.profile.active
的系统属性
创建CustomBeanFactoryPostProcessor
和一套lazyInit
所有的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
方法以加载其他 PropertySource(来自配置服务器)。
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
方法以设置custom Enviroment
而不是StandardServletEnvironment
。
public class ConfigAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { applicationContext.setEnvironment(new CloudEnvironment()); } }
修改web.xml
以将此自定义上下文初始值设定项用于application context
和servlet context
。
<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>
要刷新创建刷新端点的 bean,您还需要刷新application 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"; } }
刷新应用程序上下文.java
@Component
public class RefreshAppplicationContext implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void refreshctx(){
((XmlWebApplicationContext)(applicationContext)).refresh();
}
}
我有类似的需求; 我有一个 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>
与 Spring Boot 一起“正常工作”的一切实际上只不过是一些配置。 归根结底,这只是一个 Spring 应用程序。 所以我相信你可以手动设置 Boot 自动为你做的一切,但我不知道有人真正尝试过这个特定的角度。 创建引导应用程序上下文当然是首选方法,但根据您的用例,如果您确保足够早地执行属性源定位器,您可能会使其与单个上下文一起工作。
非 Spring(或非 Spring Boot)应用程序可以访问配置服务器中的纯文本或二进制文件。 例如,在 Spring 中,您可以使用带有 URL 资源位置的@PropertySource
,例如http://configserver/{app}/{profile}/{label}/application.properties
或http://configserver/{app}-{profile}.properties
。 这一切都包含在用户指南中。
我找到了一个没有 Spring Boot 的使用 spring-cloud-zookeeper 的解决方案,基于这里提供的想法https://wenku.baidu.com/view/493cf9eba300a6c30d229f49.html
它应该很容易更新以满足您的需求并使用 Spring Cloud Config Server(需要更新 CloudEnvironement 类以从服务器而不是 Zookeeper 加载文件)
首先,创建一个 CloudEnvironement 类,它将创建一个 PropertySource(例如来自 Zookeeper):
云环境.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 类:它可以在您的 web 应用程序启动时从 Zookeeper 加载 PropertySource 并替换 Spring Boot 的引导魔法:
MyConfigurableWebApplicationContext.java
public class MyConfigurableWebApplicationContext extends XmlWebApplicationContext {
@Override
protected ConfigurableEnvironment createEnvironment() {
return new CloudEnvironment();
}
}
最后,在您的web.xml文件中添加以下上下文参数以使用您的 MyConfigurableWebApplicationContext 类并引导您的 CloudEnvironement。
<context-param>
<param-name>contextClass</param-name>
<param-value>com.kiabi.config.MyConfigurableWebApplicationContext</param-value>
</context-param>
如果你使用标准的属性文件配置器,它仍然应该被加载,这样你就可以在本地文件和 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>
作为答案发布,因为我没有足够的分数来评论 Dave Syer 的出色答案。 我们已经在生产中实施了他提出的解决方案,并且按预期工作。 我们较新的应用程序是使用 Boot 编写的,而我们的旧应用程序使用 Spring,但不使用 boot。 我们能够使用 Spring Cloud Config 创建一个为两者提供属性的属性服务。 变化很小。 我将遗留属性文件从 war 文件中移到属性服务 git 存储库中,并将属性定义从类路径引用更改为 Dave 描述的 URL,并插入我们的系统环境变量,就像我们对类路径所做的一样。 这是简单而有效的。
<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" />
@locimathiew:我应该如何在客户端使用Zookeper从github中提取Spring Cloud Config Sever属性? 。 我的春季云配置服务器应用程序在端口8888上运行。Zoookepeer如何与云服务器通信?
@Grinish:在beanDef.setScope(“ refresh”);行中; ,它不会引发刷新范围,我尝试了所有步骤,无法得到解决方案。 你能帮我一下吗
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.