• 一种在【微服务体系】下的【参数配置神器】


    最近工作上遇到了一类需求,就是要对接大量外部系统,而需要对接的外部系统又存在各种环境,比如开发,测试,正式环境,他们的配置不尽相同。

    一般情况下,我们可以通过配置application-dev.yml、application-test.yml、application-prod.yml等springboot配置文件来配置这些参数。

    但是由于我们对接外部系统数量极多,而且不停的在增加,所以如果考虑将外部系统的地址,账户密码配置进yml文件,会导致yml文件爆炸且不太好管控,所以需要思考一个更快捷的办法。

    中间我们考虑将这样的配置写进数据库,但是几个环境的数据库都要分别配置,也挺麻烦的。

    经过思考,我们想出了一个代码配置的办法,这种办法可以让程序员在代码级别自由发挥,不受制于运维,仅供大家参考。

    1.首先增加一个接口,定义若干环境

    /**
     * 代码配置参数神器
     *
     * @author xiaokek
     * @since 2020年5月13日 下午4:01:47
     */
    public interface CoreConfig {
        default Map<String, String> getLocal(){ return Collections.EMPTY_MAP;};
        default Map<String, String> getTest(){ return getLocal();};
        default Map<String, String> getProd(){ return getLocal();};
    }

    2.增加一个配置获取器

    /**
     * 各环境配置参数
     *
     * @author xiaokek
     * @since 2020年5月13日 下午4:02:01
     */
    public class CoreConfigs {
        private final Map<String, String> c;
        
        public CoreConfigs(Map<String, String> c) {
            this.c = c;
        }
        public String getConfig(String key) {
            if(!c.containsKey(key)) {
                throw BizException.error("配置项不存在:"+key);
            }
            return c.get(key);
        }
    
    }

    3.增加一个自动配置

    /**
     * 基于不同环境的配置项
     *
     * @author xiaokek
     * @since 2020年2月27日 下午12:35:27
     */
    @Configuration
    @ConditionalOnBean(CoreConfig.class)
    public class EnvAutoConfiguration implements InitializingBean{
    
        @Bean
        @Profile({
            "local", "my", "dev"
        })
        public CoreConfigs localConfig(List<CoreConfig> cs) {
            Map<String, String> r = new LinkedCaseInsensitiveMap<>();
            for(CoreConfig cc : cs) {
                r.putAll(cc.getLocal());
            }
            return new CoreConfigs(r);
        }
    
        @Bean
        @Profile({
            "test"
        })
        public CoreConfigs testConfig(List<CoreConfig> cs) {
            Map<String, String> r = new LinkedCaseInsensitiveMap<>();
            for(CoreConfig cc : cs) {
                r.putAll(cc.getTest());
            }
            return new CoreConfigs(r);
        }
    
        @Bean
        @Profile({
            "prod"
        })
        public CoreConfigs prodConfig(List<CoreConfig> cs) {
            Map<String, String> r = new LinkedCaseInsensitiveMap<>();
            for(CoreConfig cc : cs) {
                r.putAll(cc.getProd());
            }
            return new CoreConfigs(r);
        }
    
        
        @Autowired(required=false)List<CoreConfig> cs;
        @Override
        public void afterPropertiesSet() throws Exception {
            if(CollectionUtils.isEmpty(cs)) {
                return;
            }
            Set<String> all = null;
            for(CoreConfig cc : cs) {
                check(cc);
                all = check(all, cc.getLocal().keySet(),"local", cc);
            }
            all = null;
            for(CoreConfig cc : cs) {
                all = check(all, cc.getTest().keySet(),"test", cc);
            }
            all = null;
            for(CoreConfig cc : cs) {
                all = check(all, cc.getProd().keySet(),"prod", cc);
            }
            
        }
    
      /**
       以开发环境为准,防止参数漏配检查
      */
    private void check(CoreConfig cc) { int size = (cc.getLocal() == null ? 0: cc.getLocal().size()); if(size != (cc.getSit() == null ? 0: cc.getSit().size())) { throw BizException.error(cc.getClass().getName()+" -test的配置参数缺失, 请检查"); }if(size != (cc.getProd() == null ? 0: cc.getProd().size())) { throw BizException.error(cc.getClass().getName()+" -prod的配置参数缺失, 请检查"); } } private Set<String> check(Set<String> all, Set<String> next, String env, CoreConfig cc) { if(all == null) { all = Sets.newHashSet(); } SetView<String> in = Sets.intersection(all, next); if(in != null && in.size() > 0) { throw BizException.error("发现" +cc.getClass().getName() +" - " +env+"的重复配置键: "+in); } all.addAll(next); return all; } }

    4.上面3个是放在一个配置基础jar包,其他微服务小组可以依赖上述jar包,在自己的工程做自己的配置

    关键的一点来了,这样的配置类,可以有无限个,适合拆分在各自的包里

    @Component
    public class JobParamConfig implements CoreConfig{
        @Override
        public Map<String, String> getLocal() {
            Map<String, String> c = Maps.newHashMap();
            //YY接口
            c.put("xxx.webservice.url", "http://xxxx:9040/xxx.asmx?WSDL");
            
            //XX接口
            c.put("350100.url", "http:/xxxx:4321/ylxwjk/");
            c.put("350100.username", "1_1");
            c.put("350100.password", "2");
            
            return c;
        }
        @Override
        public Map<String, String> getProd() {
            Map<String, String> c = Maps.newHashMap();
            //YY接口
            c.put("xxx.webservice.url", "http://220.xx.xx.xx:9040/xxx.asmx?WSDL");
            
            //XX接口
            c.put("350100.url", "http://10.xx.xxx.xx:8089/ylxwjk/");
            c.put("350100.username", "222");
            c.put("350100.password", "222");
    
            return c;
        }
    }

    5.接下来可以快乐使用参数拉!

    @Resource CoreConfigs c;    
    private String initToken() {
            String body = "{"username":""+c.getConfig("350100.username")+"","password":""+c.getConfig("350100.password")+""}";
            String token = null;
            String json = "";
            try {
                ResponseEntity<String> en = rt.exchange( RequestEntity.post(URI.create(c.getConfig("350100.url")+"/user/login")).contentType(MediaType.APPLICATION_JSON).body(body), String.class);
                json = en.getBody();
                Result<String> r = JsonUtil.fromJson(json, Result.class);
                if(StringUtils.equals(r.getCode(),"200")) {
                    token = r.getData();
                }else {
                    throw BizException.error("身份验证获取token失败!:"+r.getMessage());
                }
            }catch (BizException e) {
                throw e;
            }return token;
        }
  • 相关阅读:
    第一次练习总结
    第一次上机总结
    写在程序组干活之前
    虚拟机Centos7安装Mysql
    第一章 开发体验
    如何优雅的移植JavaScript组件到Blazor
    Asp.net core中RedisMQ的简单应用
    docker容器安装mysql
    Centos 8安装Docker
    c# 定时启动一个操作、任务(版本2)
  • 原文地址:https://www.cnblogs.com/xiaokek/p/14009712.html
Copyright © 2020-2023  润新知