实战:基于 Spring 的应用配置如何迁移至阿里云应用配置管理 ACM

原文链接:https://segmentfault.com/a/1190000017300808

最近遇到一些开发者朋友,准备将原有的Java Spring的应用配置迁移到 阿里云应用配置管理 ACM 中。迁移过程中,遇到不少有趣的问题。本文将通过一个简单的样例来还原迁移过程中遇到的问题和相关解决思路,以期达到和读者交流的目的。

什么样的配置适合进入配置中心
这是所有准备迁移配置到配置中心的用户遇到的第一个问题。我们将从时效性和安全这两个维度进行分析。

图片描述

时效性:静态 VS 动态
静态配置是指程序版本一旦发布,基本上不会修改的配置内容,例如:

软件版本号:显然版本号一经确定,产品基本上不需要改。
日志样式:日志的layout,如时间戳,文件名,日志级别等排版,基本上也不需要大改。
三方软件LicenseKey:基本上也是一经发布,很少变化。不排除中途三方软件License升级,但是这种升级一般都可以根据软件重新发版来解决配置变更。
PaaS平台连接串:如数据库连接串,中间包含数据库,用户名和密码等。除非因为合规原因修改密码,或者数据发生迁移等,否则也是很少变化。
动态配置是指程序运行时的一些配置变化,通常会影响到程序的一些运行行为,例如:

限流降级参数:限流降级参数一般都不太固定。系统一般在运行时最好是需要根据实际workload pattern来动态调节限流参数如阈值RT,峰值TPS,等。
监控报警阈值:如交易环比下跌20%产生error报警,下跌50%产生critical报警。 对于监控系统来讲,线上业务特征由于变化比较频繁,因此一般也不将报警的阈值写死,
日志打印级别:线上一旦出现诡异的行为,希望吧日志打印级别从error比如调高到debug,一般都比较希望能动态的去调整,而不需要重启应用。
容灾多活:一旦站点反生灾难,一定是希望切换是越快越好。因此配置必须动态秒级生效,尽可能降低资损。
从时效性的维度来讲,一般建议用户把静态配置存放到自己文件中,尽可能保持简单,但是需要把动态配置放到配置中心里,以加强灵活性和应用动态变更的实效性。

安全:非敏感 VS 敏感
非敏感配置一般指偏向技术类,暴露后不会导致配置上的安全隐患,例如:

软件版本号:跟产品迭代相关,无业务属性,非敏感配置。
日志样式:一般跟程序事后诊断相关,非敏感配置。
日志打印级别:影响日志打印的多或少,非敏感配置。
限流降级参数:限流降级主要为维持内部应用稳定,非敏感配置。
监控报警阈值:主要是影响业务的报警精度,非敏感配置。
容灾多活:一般和数据主备配置和业务分片相关,非敏感配置。
敏感配置通常和业务数据相关,一旦泄露将引起安全隐患,例如:

三方软件LicenseKey:一旦泄露容易发生LicenseKey被盗用,为敏感配置。
PaaS平台连接串:典型如数据库连接串,一旦泄露,无论内部或外部用户,都可以很容易地登到业务数据库接触到业务敏感信息,为敏感配置。
从安全的维度来看,我们通常建议用户把非敏感配置存放到自己的文件中,尽可能保持简单,但是需要把敏感配置放到配置中心里,并加密且做好鉴权,尽量不要让无关人员接触到。

图片描述

时效性和安全分析总结

基于 Spring 框架的Java应用配置如何迁移
使用Spring框架的Java开发者一般经常用到的一种配置注解姿势是利用Spring的@value注解。

原始的纯静态文件场景
例如这个配置,包含两个配置参数,一个是软件的版本号,一个是数据库连接串:

图片描述

通过@PropertySource和@value的注解来自动注入配置。

@Configuration
@ComponentScan("com.alibaba")
@PropertySource("classpath:myApp.properties")
public class AppConfig {

@Value(value="${url}")
private String URL;
@Value(value="${dbuser}")
private String USER;
@Value(value="${driver}")
private String DRIVER;
@Value(value="${dbpassword}")
private String PASSWORD;

@Value(value="${appVersion}")
private String version;
}
以上代码省略了相关数据库连接初始化等操作。

开始配置迁移,进入混合配置场景
目前由于安全合规或配置时效等原因,要开始迁移配置到ACM上。经过分析,我们发现部分数据库的配置最好迁移到ACM,以红色字体标注。红色部分将全部被迁移到ACM中。

接下来主要三个改动,先归纳下。

在ACM控制台种增加相关配置的记录。
Java工程包中增加 ACM SDK 相关依赖。
少许修改代码,增加在ACM中取配置的注解代码。
第一步,直接到ACM中创建配置项,名字为myapp.dbconfig.properties,并把配置内容编辑在对应编辑框中。详细操作指南请参见 ACM 快速入门文档,操作截图如下。

图片描述

第二步,在maven的pom.xml中增加依赖,如下。

<dependency>

<groupId>com.alibaba.nacos</groupId> 
<artifactId>nacos-spring-context</artifactId> 
<version>0.2.1- RC1</version> 

</dependency>
第三步,在对应AppConfig.java代码中植入API注解,通过ACM去获取动态配置。代码增加部分如红色字体部分。

@Configuration @ComponentScan("com.journaldev")
@PropertySource("classpath:myApp.properties")
@EnableNacosConfig(globalProperties =
@NacosProperties(endpoint = "acm.aliyun.com", namespace = "xxx", accessKey = "xxx", secretKey = "xxx"))
@NacosPropertySource(dataId = "myApp.dbconfig.properties", autoRefreshed = true) public class AppConfig {
@Value(value="${url}") private String URL;
@Value(value="${dbuser}") private String USER;
@Value(value="${driver}") private String DRIVER;
@Value(value="${dbpassword}") private String PASSWORD;

@Value(value="${appVersion}")
private String version; public String getVersion() {

  return version; 

}
}
至此,改动结束。通过ACM SDK支持Spring的@value注解能力,代码几乎0改动。

几点注意事项
在以上代码实例中,有几样事情需要注意:

代码中使用的ACM SDK为Nacos SDK。Nacos (http://nacos.io) 为ACM的开源实现,ACM无缝兼容所有Nacos的接口。
在代码示例中,使用了明文注解来写死ACM的endpoint, namespace, AK, SK, 等等。在实际操作种,相关变量其实不用写死。

endpoint namespace等可以通过ACM的相关文件配置或系统变量来解决。详情参见:https://help.aliyun.com/docum...
AK, SK等敏感信息可以通过ECS Ram Role功能来让系统自行判断,代码不用写死。详情参见:https://help.aliyun.com/docum...
代码中未包含动态配置监听的回调部分。基于Spring的动态配置相关使用方法请参见:https://github.com/nacos-grou...

展开阅读全文

spring实战》里基于java配置的问题

01-16

书上的启动代码,我跟着敲了一遍,最后在tomcat跑不起来~报404错。。。。想不出哪里出错了,求指导。。。。。tomcat 用的8.0.29,是servlet3.0+的rnrn代码很简单,(注册DispatcherServlet,启动应用上下文)。。。。rnrn[img=https://img-bbs.csdn.net/upload/201701/16/1484579578_821984.jpg][/img]rnrn(我估计是注册DispatcherServlet不成功,但是不知道为什么?)rnrnrn目录如下:rn[img=https://img-bbs.csdn.net/upload/201701/16/1484579336_327995.jpg][/img]rnrn然后贴代码:rnSpitterWebInitializer.java (核心)rn[code=java]rnpackage spittr.config;rnrnimport org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;rnrn/**rn * 扩展了AbstractAnnotationConfigDispatcherServletInitializer的类会自动配置DispatcherServlet和Spring应用上下文rn * @author c00284934rn *rn */rnpublic class SpitterWebInitializer extendsrn AbstractAnnotationConfigDispatcherServletInitializer rnrn /**rn * 返回带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的beanrn * ContextLoaderListener加载的bean通常是驱动应用后端的中间层和数据层组件rn */rn @Overridern protected Class[] getRootConfigClasses() rn return new Class[]RootConfig.class;rn rnrn /**rn * 带有@Configuration注解的类会用来定义为DispatcherServlet应用上下文中的beanrn */rn @Overridern protected Class[] getServletConfigClasses() rn /**rn * 当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件中所声明的beanrn */rn return new Class[]WebConfig.class;//指定配置类rn rnrn @Overridern protected String[] getServletMappings() rn return new String[]"/";//将DispatcherServlet映射到“/”rn rnrnrn[/code]rnrnWebConfig.javarn[code=java]rnpackage spittr.config;rnrnimport org.springframework.context.annotation.ComponentScan;rnimport org.springframework.context.annotation.Configuration;rnimport org.springframework.web.servlet.ViewResolver;rnimport org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;rnimport org.springframework.web.servlet.config.annotation.EnableWebMvc;rnimport org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;rnimport org.springframework.web.servlet.view.InternalResourceViewResolver;rnrn@Configurationrn@EnableWebMvc //启用SpringMVCrn@ComponentScan("spittr.web") //启用组件扫描rnpublic class WebConfig extends WebMvcConfigurerAdapterrnrn public ViewResolver viewResolver()rn InternalResourceViewResolver resolver = new InternalResourceViewResolver(); //配置jsp视图解析器rn resolver.setPrefix("/WEB-INF/views/");rn resolver.setSuffix(".jsp");rn resolver.setExposeContextBeansAsAttributes(true);rn return resolver;rn rnrn @Overridern public void configureDefaultServletHandling(rn DefaultServletHandlerConfigurer configurer) //配置静态资源的处理rn //我们要求DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求rn configurer.enable();rn rnrnrnrn[/code]rnrnRootConfig.javarn[code=java]rnpackage spittr.config;rnrnimport org.springframework.context.annotation.ComponentScan;rnimport org.springframework.context.annotation.ComponentScan.Filter;rnimport org.springframework.context.annotation.Configuration;rnimport org.springframework.context.annotation.FilterType;rnimport org.springframework.web.servlet.config.annotation.EnableWebMvc;rnrnrn@Configurationrn@ComponentScan(basePackages="spittr",excludeFilters=@Filter(type=FilterType.ANNOTATION,value=EnableWebMvc.class))rnpublic class RootConfig rnrnrn[/code]rnrnHomeController.javarn[code=java]rnpackage spittr.web;rnrnimport static org.springframework.web.bind.annotation.RequestMethod.*;rnrnimport org.springframework.stereotype.Controller;rnimport org.springframework.ui.Model;rnimport org.springframework.web.bind.annotation.RequestMapping;rnrn@Controllerrn@RequestMapping("/")rnpublic class HomeController rnrn @RequestMapping(method = GET)rn public String home(Model model) rn return "home";rn rnrn[/code]rnrnhome.jsprn[code=html]rn<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>rn<%@ page session="false" %>rnrn rn Spitterrn " >rn rn rn Welcome to Spitterrnrn ">Spittles | rn ">Registerrn rnrn[/code] 论坛

没有更多推荐了,返回首页