storm 提交拓扑,引用外部配置文件

烈酒焚心 提交于 2020-01-23 03:45:37

背景

由于 jar 包需要发布到 storm 分布式环境中运行,所以每次都要将配置文件打进 jar 包里,这样程序运行的时候,才能从 classpath 中找到配置文件。每次发布程序的时候,由于各项环境不同,都要修改配置文件,很麻烦。所以试想如何加载 jar 包外部配置文件。

配置文件

配置文件包括

  1. 程序配置文件 .properties
  2. spring 配置文件 .xml
    其中 spring 配置文件引用了 .properties 文件

思路

  1. 将 .properties 配置读取,放到 stormConf 对象中,这样,当 jar 包提交到 storm 拓扑环境中时,也能从 stormConf 对象中加载配置
  2. 首先,去 xml 配置,spring 使用注解 @Configuration 配置类,然后通过工具类,从 stormConf 中获取需要的配置。

代码示例

本文只是演示思路,不介绍 spring 注解配置,请查阅其他资料

  1. 从外部文件读取配置,放入 stormConfig
public static  void main(String[] args) {
    
	final TopologyBuilder builder = new TopologyBuilder();
	
	// 获取当前 jar 包路径
    String programPath = ClassUtils.getCurrentProgramPath();
    // 从外部读取 jdbc.properties 文件
    String jdbcProperties = FileUtils.readFileContent(programPath + "/jdbc.properties");
	
	Config conf = new Config();
	// 放入 jdbc 配置
	conf.put("jdbc.properties", jdbcProperties );
	
	StormSubmitter.submitTopology("test", conf, builder.createTopology());
	
}	
  1. spout、bolt 中获取配置对象
@Override
public void prepare(Map stormConf, TopologyContext context) {
    
    // 获取 jdbc 配置
    //String jdbcProperties = stormConf.get("jdbc.properties");
    
    // 然后可以用于初始化 spring 容器
    SpringContext.SpringContextInit(stormConf);
    
}
  1. ClassUtils 工具类
import java.io.File;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.util.Objects;

/**
 * 类的工具类
 *
 * @author wangtan
 * @date 2019-09-28 11:07:12
 * @since 1.0
 */
public class ClassUtils {

    /**
     * Don't let anyone else instantiate this class
     */
    private ClassUtils() {
    }

    /**
     * 获取当前程序所在路径
     *
     * @return String
     * @date 2019-09-28 11:08:39
     * @since 1.0
     */
    public static String getCurrentProgramPath() {
        CodeSource codeSource = ClassUtils.class.getProtectionDomain().getCodeSource();

        File jarFile = null;
        try {
            jarFile = new File(codeSource.getLocation().toURI().getPath());
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        return Objects.requireNonNull(jarFile, "jar file should not be null.").getParentFile().getPath();
    }

}
  1. FileUtils 文件工具类
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 文件工具类
 *
 * @author wangtan
 * @date 2020-01-07 17:08:21
 * @since 1.0
 */
public class FileUtils {

    /**
     * Don't let anyone else instantiate this class
     */
    private FileUtils() {
    }

    /**
     * 读取文件内容
     *
     * @param filePath 文件路径
     * @return 文件内容
     * @date 2020-01-07 17:08:56
     * @since 1.0
     */
    public static String readFileContent(String filePath) {
        File file = new File(filePath);
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
            String tempStr;
            while ((tempStr = reader.readLine()) != null) {
                sb.append(tempStr).append(System.getProperty("line.separator"));
            }
            reader.close();
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

}
  1. spring 容器初始化
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Map;

/**
 *
 * 初始化Spring容器
 *
 * @author wangtan
 * @date 2020-01-09 11:11:11
 * @since 1.0
 */
public class SpringContext
{
    private static ApplicationContext applicationContext;

    public static synchronized void SpringContextInit(Map stormConf)
    {
        if (applicationContext == null)
        {
            //applicationContext = new ClassPathXmlApplicationContext("spring/application-context.xml","spring/application-redis.xml");

            /* start: 使用 AnnotationConfigApplicationContext 注册配置类 @author wangtan @date 2020-01-09 11:11:11 */

            // 加载配置
            ConfigUtils.loadProps(stormConf);

            /*
             * spring 注册配置类
             * Dependency Injection,依赖注入 注入方法:构造器注入
             *
             * spring 的依赖注入是有顺序的,当先注入的对象 A 依赖后注入的对象 B 时,会把 A 标记挂起,
             * 等待所有的对象注入完毕后,再回过头来,重新注入被标记挂起的对象 A,而此时,A 依赖的 B 已经注入完毕,
             * 所以此时可以注入 A。辅助理解配置类注入过程(其实和 xml 注入原理一样)。
             */
            applicationContext = new AnnotationConfigApplicationContext(ApplicationContextConfiguration.class,
                    DataSourceConfiguration.class, SqlSessionFactoryConfiguration.class,
                    TransactionManagementConfiguration.class, SqlSessionTemplateConfiguration.class,
                    JedisPoolConfiguration.class, AiJedisConnectionFactoryConfiguration.class,
                    StringRedisTemplateConfiguration.class);

            /* end: 使用 AnnotationConfigApplicationContext 注册配置类 @author wangtan @date 2020-01-09 11:11:30 */

        }
    }
}
  1. ConfigUtils 配置文具类,将 配置信息写入静态变量
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * 配置文件工具类
 *
 * @author wangtan
 * @date 2020-01-08 11:03:23
 * @since 1.0
 */
public class ConfigUtils {

    /**
     * jdbc 配置对象
     */
    public static Properties jdbcProperties = new Properties();
    
    /**
     * Don't let anyone else instantiate this class
     */
    private ConfigUtils() {
    }

    /**
     * 加载配置
     *
     * @param stormConf storm 配置对象
     * @date 2020-01-09 15:07:56
     * @since 1.0
     */
    public static void loadProps(Map stormConf) {

        try {

            jdbcProperties.load(new StringReader(StringUtils.getStr(stormConf.get("jdbc.properties"))));
            
        } catch (IOException e) {
            LOGGER.error("从 stormConf 加载配置失败。", e);
        }
       
    }
    
    /**
     * 根据 key 获取 jdbc 配置
     *
     * @param key 配置项名称
     * @return 配置项值
     * @date 2020-01-09 14:00:04
     * @since 1.0
     */
    public static String getJdbcProperty(String key) {
        return jdbcProperties.getProperty(key);
    }

}
  1. DataSource 配置类
import com.alibaba.druid.pool.DruidDataSource;
import com.****.ete.util.ConfigUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.sql.SQLException;

/**
 * DataSource 配置类
 *
 * @author wangtan
 * @date 2020-01-09 10:27:09
 * @since 1.0
 */
@Configuration
public class DataSourceConfiguration {

    /**
     * 配置 DruidDataSource
     * <p>
     * 不可省略 (initMethod = "init", destroyMethod = "close")
     *
     * @return DruidDataSource 数据源对象
     * @throws SQLException SQLException
     * @date 2020-01-09 09:53:46
     * @since 1.0
     */
    @Bean(initMethod = "init", destroyMethod = "close")
    @Primary
    public DruidDataSource dataSource() throws SQLException {

        DruidDataSource druidDataSource = new DruidDataSource();

        // 基本属性 url、user、password
        druidDataSource.setUrl(ConfigUtils.getJdbcProperty("jdbc.url"));
        druidDataSource.setUsername(ConfigUtils.getJdbcProperty("jdbc.username"));
        druidDataSource.setPassword(ConfigUtils.getJdbcProperty("jdbc.password"));

        // 配置初始化大小、最小、最大
        druidDataSource.setInitialSize(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.initialSize")));
        druidDataSource.setMinIdle(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.minIdle")));
        druidDataSource.setMaxActive(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.maxActive")));

        // 配置获取连接等待超时的时间
        druidDataSource.setMaxWait(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.maxWait")));

        // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        druidDataSource.setTimeBetweenEvictionRunsMillis(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.timeBetweenEvictionRunsMillis")));

        // 配置一个连接在池中最小生存的时间,单位是毫秒
        druidDataSource.setMinEvictableIdleTimeMillis(NumberUtils.toLong(ConfigUtils.getJdbcProperty("druid.minEvictableIdleTimeMillis")));
        druidDataSource.setValidationQuery("select 'X' from dual");
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTestOnBorrow(false);
        druidDataSource.setTestOnReturn(false);

        // 打开 PSCache,并且指定每个连接上 PSCache 的大小 如果用 Oracle,
        // 则把 poolPreparedStatements 配置为 true,mysql 可以配置为 false。
        druidDataSource.setPoolPreparedStatements(BooleanUtils.toBoolean(ConfigUtils.getJdbcProperty("druid.poolPreparedStatements")));
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(NumberUtils.toInt(ConfigUtils.getJdbcProperty("druid.maxPoolPreparedStatementPerConnectionSize")));

        // 配置监控统计拦截的 filters
        druidDataSource.setFilters(ConfigUtils.getJdbcProperty("druid.filters"));

        return druidDataSource;
    }

}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!