文章 62
浏览 15135
自研网关day03-加载核心配置

自研网关day03-加载核心配置

当 core 模块中 BootStra 启动,需要加载环境的核心配置信息,比如网关核心配置,包括配置类默认值,配置文件,环境变量,jvm 参数,运行参数,还需要加载配置中心的配置(Nacos)和注册中心相关配置

加载网关核心配置

初始化核心配置

config 类作为配置文件加载的核心配置类,后续想当于集成 Spring 环境作为加载 spring,yaml 的一些配置信息类

public class Config {

    private int port = 8888;

    private String applicationName = "api-gateway";

    private String registryAddress = "127.0.0.1:8848";

    private String env = "dev";

    private int eventLoopGroupBossNum = 1;

    private int eventLoopGroupWorkerNum = Runtime.getRuntime().availableProcessors();

    private int maxContentLength = 64 * 1024 * 1024;

    //默认单异步模式
    private boolean whenComplete = true;


}

初始化配置加载器

**需要有一个配置类加载器来封装启动加载配置类,因为加载配置类会有一个顺序问题,优先级高的会覆盖优先级低的属性值,加载顺序为: **运行参数->jvm 参数-> 环境变量-> 配置文件-> 配置对象的默认值

package com.xiaohu.core;

import com.xiaohu.common.utils.PropertiesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xiaohugg
 * @version 1.0
 * @date 2024/4/18 21:28:36
 */
public class ConfigLoader {
    private static final Logger log = LoggerFactory.getLogger(ConfigLoader.class);
    private static final String CONFIG_FILE = "gateway.properties";
    private static final String ENV_PREFIX = "GATEWAY_";
    private static final String JVM_PREFIX = "gateway.";
    private static ConfigLoader INSTANCE;
  
    private Config config;


    private ConfigLoader() {
    }

    public static synchronized ConfigLoader getInstance() {
        if (INSTANCE == null) {
            synchronized (ConfigLoader.class) {
                INSTANCE = new ConfigLoader();
            }
        }
        return INSTANCE;
    }

    /**
     * 优先级高的会覆盖优先级低的
     * 运行参数 -> jvm参数 -> 环境变量 -> 配置文件 -> 配置对象对默认值
     */
    public Config load(String[] args) {
        //配置对象对默认值
        config = new Config();

        //配置文件
        loadFromConfigFile();

        //环境变量
        loadFromEnv();

        //jvm参数
        loadFromJvm();

        //运行参数
        loadFromArgs(args);

        return config;
    }

    private void loadFromArgs(String[] args) {
        //--port=1234
        Map<String, Object> propertiesMap = new HashMap<>();
        Optional.ofNullable(args)
                .map(Arrays::stream)
                .ifPresent(argsStream ->
                        argsStream.filter(arg -> arg.startsWith("--") && arg.contains("="))
                                .map(arg -> arg.split("=", 2))
                                .forEach(pair -> propertiesMap.put(pair[0].substring(2), pair[1]))
                );
        PropertiesUtils.properties2Object(propertiesMap, config);
    }

    private void loadFromJvm() {
        Properties properties = System.getProperties();
        PropertiesUtils.properties2Object(propertiesToMap(properties), config, JVM_PREFIX);
    }

    private void loadFromEnv() {
        Map<String, String> env = System.getenv();
        Map<String, Object> propertiesMap = new HashMap<>(env);
        PropertiesUtils.properties2Object(propertiesMap, config, ENV_PREFIX);
    }

    private void loadFromConfigFile() {
        try (InputStream inputStream = ConfigLoader.class.getClassLoader().getResourceAsStream(CONFIG_FILE)) {
            if (Objects.isNull(inputStream)) {
                return;
            }
            Properties properties = new Properties();
            properties.load(inputStream);
            PropertiesUtils.properties2Object(propertiesToMap(properties), config);
        } catch (Exception e) {
            log.error("Error loading configuration file: {}", CONFIG_FILE, e);
        }
    }

    public Map<String, Object> propertiesToMap(Properties properties) {
        return properties.entrySet().stream()
                .collect(Collectors.toMap(
                        e -> String.valueOf(e.getKey()),
                        Map.Entry::getValue
                ));
    }
}

因为考虑到核心配置类加载器一个只能启动一次,所以需要保证他为单例,采用单例模式,避免安全性问题

有个优化点,这里考虑到传统的 Properties 底层是 hashTab,每个方法都加了 synchronized,目前的环境是单例的,所以采用 hashMap 来优化

通过加载已知环境的属性参数,最后进行反射转为对应的配置类实体

package com.xiaohu.common.utils;

import lombok.extern.slf4j.Slf4j;

import java.beans.*;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Stream;

@Slf4j
public class PropertiesUtils {

    /**
     * 将属性集合中的属性值批量设置到指定对象的对应属性上。
     * @param properties 属性集合,键值对形式,其中键包含属性名可能的前缀。
     * @param object 需要设置属性值的目标对象。
     * @param prefix 属性名的前缀,用于匹配和从属性集合中选择正确的属性。
     */
    public static void properties2Object(final Map<String,Object> properties, final Object object, String prefix) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
            Stream.of(beanInfo.getPropertyDescriptors())
                    .filter(pd -> hasProperty(properties, pd.getName(), prefix))
                    .forEach(pd -> setProperty(object, pd, properties.get(prefix + pd.getName())));
        } catch (IntrospectionException e) {
            log.warn("properties2Object error:{}", e.getMessage());
        }
    }

    private static boolean hasProperty(Map<String,Object> properties, String propertyName, String prefix) {
        return properties.containsKey(prefix + propertyName);
    }

    /**
     * 设置对象的属性值。
     *
     * @param object 需要设置属性值的目标对象。
     * @param pd 属性描述器,描述目标属性的信息。
     * @param value 需要设置的属性值。
     */
    private static void setProperty(Object object, PropertyDescriptor pd, Object value) {
        Optional.ofNullable(PropertyEditorManager.findEditor(pd.getPropertyType()))
                .ifPresent(editor -> {
                    editor.setAsText(value.toString());
                    try {
                        pd.getWriteMethod().invoke(object, editor.getValue());
                    } catch (ReflectiveOperationException e) {
                        log.warn("setProperty error:{}", e.getMessage());
                    }
                });
    }

    public static void properties2Object(final Map<String,Object> propertyMap, final Object object) {
        properties2Object(propertyMap, object, "");
    }
}

运行测试

**可以看到默认的 config 对象里的 port=8888,applicationName=api-gateway,通过在 resurce 目录下创建一个前缀文件 gateway.properties**

gateway.properties

env=prod
registryAddress=124.223.222.174:8848
whenComplete=false
isxxx=xxxx

可以看到相同的属性,优先级高的会覆盖优先级低的,不属于该类的字段不会加载


标题:自研网关day03-加载核心配置
作者:xiaohugg
地址:https://xiaohugg.top/articles/2024/04/18/1713452569382.html

人民有信仰 民族有希望 国家有力量