All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.summerboot.jexpress.integration.cache.RedisConfig Maven / Gradle / Ivy

/*
 * Copyright 2005-2022 Du Law Office - The Summer Boot Framework Project
 *
 * The Summer Boot Project licenses this file to you under the Apache License, version 2.0 (the
 * "License"); you may not use this file except in compliance with the License and you have no
 * policy prohibiting employee contributions back to this file (unless the contributor to this
 * file is your current or retired employee). You may obtain a copy of the License at:
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package org.summerboot.jexpress.integration.cache;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.summerboot.jexpress.boot.config.ConfigUtil;
import org.summerboot.jexpress.boot.config.JExpressConfig;
import org.summerboot.jexpress.security.SecurityUtil;
import org.summerboot.jexpress.util.BeanUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import static org.summerboot.jexpress.boot.config.ConfigUtil.ENCRYPTED_WARPER_PREFIX;

/**
 * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵
 */
public class RedisConfig implements JExpressConfig {

    protected static final String PK = "primary";

    protected static Logger log = LogManager.getLogger(RedisConfig.class);
    protected File cfgFile;
    protected volatile List jedisPools;
    protected volatile JedisPool masterPool;
    protected volatile List nodes;
    protected volatile int reconnectRetryIntervalMinutes;
    protected volatile int sendAlertIntervalMinutes;

    public static final RedisConfig cfg = new RedisConfig();

    public RedisConfig() {
    }

    @Override
    public File getCfgFile() {
        return cfgFile;
    }

    @Override
    public String name() {
        return "Redis Config";
    }

    @Override
    public String info() {
        try {
            return BeanUtil.toJson(this, true, false);
        } catch (JsonProcessingException ex) {
            return ex.toString();
        }
    }

    @Override
    public JExpressConfig temp() {
        return new RedisConfig();
    }

    @Override
    public void load(File cfgFile, boolean isReal) throws Exception {
        if (log == null) {
            log = LogManager.getLogger(getClass());
        }
        this.cfgFile = cfgFile.getAbsoluteFile();
        Properties props = new Properties();
        try (InputStream is = new FileInputStream(cfgFile);) {
            props.load(is);
        }
        if (jedisPools == null) {
            jedisPools = new ArrayList();
        } else {
            jedisPools.clear();
        }
        if (nodes == null) {
            nodes = new ArrayList();
        } else {
            nodes.clear();
        }
        ConfigUtil helper = new ConfigUtil(this.cfgFile.getAbsolutePath());
        reconnectRetryIntervalMinutes = helper.getAsInt(props, "redis.Reconnect.Retry.IntervalMinutes", 1);
        sendAlertIntervalMinutes = helper.getAsInt(props, "redis.SendAlert.IntervalMinutes", 10);

        masterPool = null;

        Set _keys = props.keySet().stream().map(o -> o.toString()).collect(Collectors.toSet());
        List keys = new ArrayList<>(_keys);
        Collections.sort(keys);
        keys.forEach((name) -> {
            if (name.startsWith("redis.node")) {
                String url = props.getProperty(name);// url: arbitrary_usrname:password@host:port
                String[] fields = url.split("\\:");
                //String user = fields[0];
                String[] f2 = fields[1].split("\\@");
                String encPwd = f2[0];
                String pwd = null;
                if (StringUtils.isNotBlank(encPwd)) {
                    try {
                        if (encPwd.startsWith(ENCRYPTED_WARPER_PREFIX + "(") && encPwd.endsWith(")")) {
                            pwd = SecurityUtil.decrypt(encPwd, true);
                        }
                    } catch (GeneralSecurityException ex) {

                    }
                }
                String host = f2[1];
                int port = Integer.parseInt(fields[2]);
                JedisPool jp = new JedisPool(new JedisPoolConfig(),
                        host,
                        port,
                        Protocol.DEFAULT_TIMEOUT,
                        pwd);
                jedisPools.add(jp);
                nodes.add(host + ":" + port);
            }
        });
        String master = autoFailover(null);
        if (master != null) {
            nodes.add("current master=" + master);
        }
    }

    @Override

    public void shutdown() {
        jedisPools.forEach((p) -> {
            try {
                p.destroy();
            } catch (Throwable ex) {
            }
        });
    }

    public String autoFailover(Throwable cause) {
        if (cause != null) {
            log.error(cause);
        }
        long lastFailoveredMasterPoolTTL = 0;
        masterPool = null;
        String key = PK;
        JedisPool firstAvaliableMasterPool = null, failoveredMasterPool = null;
        for (JedisPool pool : jedisPools) {
            try (Jedis jedis = pool.getResource();) {
                jedis.del("arbitraryValue_just_check-writable");
                String anyValue = jedis.get(key);
                if (anyValue == null) {// not found
                    if (firstAvaliableMasterPool == null) {
                        firstAvaliableMasterPool = pool;
                    }
                } else {// found
                    Long currentTTL = jedis.ttl(key);
                    if (currentTTL == null) {
                        currentTTL = 0L;
                    } else if (currentTTL < 0) {
                        currentTTL = Long.MAX_VALUE / 1000;
                    }
                    if (currentTTL > lastFailoveredMasterPoolTTL) {
                        failoveredMasterPool = pool;
                        lastFailoveredMasterPoolTTL = currentTTL;
                    }
//                    if (failoveredMasterPool == null) {
//                        failoveredMasterPool = pool;
//                    }
                }
            } catch (JedisConnectionException | JedisDataException ex) {
                log.warn("find connection: " + ex);
            }
        }
        if (failoveredMasterPool != null) {
            masterPool = failoveredMasterPool;
        } else if (firstAvaliableMasterPool != null) {
            masterPool = firstAvaliableMasterPool;
        }

        String ret = null; // return null to indicate no available node
        if (masterPool != null) {
            // now switched to new master node
            try (Jedis jedis = masterPool.getResource();) {
                //ret = jedis.getClient().toString();
                ret = jedis.getConnection().toString();
                long ttl = (Long.MAX_VALUE - System.currentTimeMillis() - 1000) / 1000;
                jedis.psetex(key, ttl, ret);
                //jedis.set(key, ret);
            }
        }
        return ret;
    }

    @JsonIgnore
    public Jedis getMaster() {
        if (masterPool == null) {
            return null;
        }
        return masterPool.getResource();
    }

    public List getNodes() {
        return nodes;
    }

    public int getReconnectRetryIntervalMinutes() {
        return reconnectRetryIntervalMinutes;
    }

    public int getSendAlertIntervalMinutes() {
        return sendAlertIntervalMinutes;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy