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

com.github.javaclub.toolbox.cache.RedisStoreBuilder Maven / Gradle / Ivy

There is a newer version: 2.7.44
Show newest version
/*
 * @(#)RedisStoreBuilder.java	2022-12-8
 *
 * Copyright (c) 2022. All Rights Reserved.
 *
 */

package com.github.javaclub.toolbox.cache;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.github.javaclub.AppBootConstants;
import com.github.javaclub.Constants.Environments;
import com.github.javaclub.Constants.RedisStoreConfig;
import com.github.javaclub.Constants.SpringRedisConfig;
import com.github.javaclub.toolbox.ToolBox.Maps;
import com.github.javaclub.toolbox.ToolBox.Objects;
import com.github.javaclub.toolbox.ToolBox.Reflections;
import com.github.javaclub.toolbox.ToolBox.Strings;
import com.github.javaclub.toolbox.cache.redis.RedisStore;
import com.github.javaclub.toolbox.conf.CompositeAppConfigProperties;
import com.github.javaclub.toolbox.thread.ExecutorServiceInstance;

import redis.clients.jedis.JedisPoolConfig;

/**
 * RedisStoreBuilder
 *
 * @author Gerald Chen
 * @version $Id: RedisStoreBuilder.java 2022-12-8 11:22:11 Exp $
 */
public class RedisStoreBuilder {
	
	static final Logger log = LoggerFactory.getLogger(RedisStoreBuilder.class);
	
	static volatile int warnTimes = 0;
	
	static {
		ExecutorServiceInstance.get().scheduleAtFixedRate(RedisStoreBuilder::doMonitor, 3L, 30L, TimeUnit.SECONDS);
	}

	public static RedisStore autoBuild(boolean configBuild) {
		if (Thread.currentThread().isInterrupted()) {
			log.warn("The currentThread is interrupted");
			return null;
		}
		if (!configBuild) {
			RedisStore store = RedisStore.defaultPublic();
			if (null != store && store.isReady()) {
				return store;
			}
		}
		
		String host = CompositeAppConfigProperties.getInstance().getValue(RedisStoreConfig.HOST);
		String password = Strings.EMPTY;
		int port = -1, db = -1;
		String[] configKeys = null;
		if (Strings.isNotBlank(host)) { // 使用redis.xxx下的配置项
			port = CompositeAppConfigProperties.getInstance().intValue(RedisStoreConfig.PORT, -1);
			password = CompositeAppConfigProperties.getInstance().getValue(RedisStoreConfig.PASSWORD);
			db = CompositeAppConfigProperties.getInstance().intValue(RedisStoreConfig.DB);
			configKeys = RedisStoreConfig.REDIS_STORE_CONFIG;
		} else if (Strings.isNotBlank(CompositeAppConfigProperties.getInstance().getValue(RedisStoreConfig.SPRING_CONFIG[0]))) {
			host = CompositeAppConfigProperties.getInstance().getValue(SpringRedisConfig.HOST);
			port = CompositeAppConfigProperties.getInstance().intValue(SpringRedisConfig.PORT, -1);
			password = CompositeAppConfigProperties.getInstance().getValue(SpringRedisConfig.PASSWORD);
			db = CompositeAppConfigProperties.getInstance().intValue(SpringRedisConfig.DATABASE);
			configKeys = RedisStoreConfig.SPRING_CONFIG;
		} else { // spring配置作为兜底
			host = CompositeAppConfigProperties.getInstance().getValue(SpringRedisConfig.HOST);
			port = CompositeAppConfigProperties.getInstance().intValue(SpringRedisConfig.PORT, -1);
			password = CompositeAppConfigProperties.getInstance().getValue(SpringRedisConfig.PASSWORD);
			db = CompositeAppConfigProperties.getInstance().intValue(SpringRedisConfig.DATABASE);
			configKeys = RedisStoreConfig.SPRING_CONFIG;
		}
		port = (port == -1 ? 6379 : port); // spring redis 未配置端口,会使用默认端口6379
		Object[] params = new Object[] { RedisStoreConfig.STORE_KEY_COMMON, host, port, db };
		if (Strings.isAnyBlank(host, password) || port == -1 || db == -1) {
			warnLog(params);
			return null;
		}
		if (!RedisStore.test(host, port, password, db)) {
			warnLog(params);
			return null;
		}
		return RedisStore.createRedisStore(RedisStoreConfig.STORE_KEY_COMMON, configKeys);
	}
	
	public static void destroyAll() {
		Map stores = RedisStore.presentAll();
		RedisStore commonStore = stores.get(RedisStoreConfig.STORE_KEY_COMMON);
		for (Map.Entry element : stores.entrySet()) {
			if (!Strings.equals(RedisStoreConfig.STORE_KEY_COMMON, element.getKey())) {
				element.getValue().destroy();
			}
		}
		if (null != commonStore) {
			commonStore.destroy();
		}
	}
	
	public static JedisPoolConfig newCustomJedisPoolConfig() {
	    	JedisPoolConfig poolConfig = new JedisPoolConfig();
	    	
	    	// 连接池最大连接数(使用负值表示没有限制)
	    	poolConfig.setMaxTotal(CompositeAppConfigProperties.getInstance().intValue(new String[] {
	    		"redis.pool.maxTotal", "redis.pool.max-total",
	    		"spring.redis.jedis.pool.maxActive", "spring.redis.jedis.pool.max-active",
	    		"spring.redis.lettuce.pool.maxActive", "spring.redis.lettuce.pool.max-active"
	    	}, 15));
	    	// 连接池中的最大空闲连接数
	    poolConfig.setMaxIdle(CompositeAppConfigProperties.getInstance().intValue(new String[] { 
	        	"redis.pool.maxIdle", "redis.pool.max-idle",
	        	"spring.redis.jedis.pool.maxIdle", "spring.redis.jedis.pool.max-idle",
	        	"spring.redis.lettuce.pool.maxIdle", "spring.redis.lettuce.pool.maxIdle",
	    }, 15));
	    // 连接池中的最小空闲连接
	    poolConfig.setMinIdle(CompositeAppConfigProperties.getInstance().intValue(new String[] {
	        	"redis.pool.minIdle", "redis.pool.min-idle",
	        	"spring.redis.jedis.pool.minIdle", "spring.redis.jedis.pool.min-idle",
	        	"spring.redis.lettuce.pool.minIdle", "spring.redis.lettuce.pool.min-idle"
	    }, 1));
	    // 设置获取连接时的最大等待毫秒数(阻塞等待时间,blockWhenExhausted设置为true时才生效)
	    poolConfig.setMaxWaitMillis(CompositeAppConfigProperties.getInstance().longValue(new String[] {
	        	"redis.pool.maxWaitMillis", "redis.pool.maxWait", "redis.pool.max-wait",
	        	"spring.redis.jedis.pool.maxWait", "spring.redis.jedis.pool.max-wait",
	        	"spring.redis.lettuce.pool.maxWait", "spring.redis.lettuce.pool.max-wait"
	    }, 3000L));
        // 空闲资源的检测周期(毫秒)
        poolConfig.setTimeBetweenEvictionRunsMillis(CompositeAppConfigProperties.getInstance().longValue(new String[] { 
	        	"redis.pool.timeBetweenEvictionRunsMillis", "redis.pool.timeBetweenEvictionRuns", "redis.pool.time-between-eviction-runs",
	        	"spring.redis.jedis.pool.timeBetweenEvictionRuns", "spring.redis.jedis.pool.time-between-eviction-runs", 
	        	"spring.redis.lettuce.pool.timeBetweenEvictionRuns", "spring.redis.lettuce.pool.time-between-eviction-runs"
        }, 10*1000L));
        
        // 逐出连接的最小空闲时间毫秒数(15分钟)
        poolConfig.setMinEvictableIdleTimeMillis(CompositeAppConfigProperties.getInstance().longValue("redis.pool.minEvictableIdleTimeMillis", 15*60*1000L)); // 连接最小空闲时间
        // 每次逐出检查时逐出的最大数目,默认3
        poolConfig.setNumTestsPerEvictionRun(CompositeAppConfigProperties.getInstance().intValue("redis.pool.numTestsPerEvictionRun", 3));
        
        // 对拿到的connection进行validateObject校验
        poolConfig.setTestOnBorrow(CompositeAppConfigProperties.getInstance().boolValue("redis.pool.testOnBorrow", true));
        // 在进行returnObject对返回的connection进行validateObject校验
        poolConfig.setTestOnReturn(CompositeAppConfigProperties.getInstance().boolValue("redis.pool.testOnReturn", false));
        // 定时对线程池中空闲的链接进行校验
        poolConfig.setTestWhileIdle(CompositeAppConfigProperties.getInstance().boolValue("redis.pool.testWhileIdle", true));
        // 当资源池用尽后,调用者是否要等待;只有当值为true时,连接池设置的maxWaitMillis才会生效
        poolConfig.setBlockWhenExhausted(CompositeAppConfigProperties.getInstance().boolValue("redis.pool.blockWhenExhausted", true));
    	
        return poolConfig;
    }

	public static void doMonitor() {
		Map storesMap = RedisStore.presentAll();
		if (Maps.isEmpty(storesMap)) {
			return;
		}
		for (Map.Entry it : storesMap.entrySet()) {
			RedisStore instance = it.getValue();
			if (null == instance || Objects.isAnyNull(instance.getJedisPoolConfig(), instance.getJedisPool())) {
				continue;
			}
			if (AppBootConstants.isLoggerEnabled("logger.switch.jedis") && log.isInfoEnabled()) {
				String format = "RedisStore instance={} {}JedisPoolConfig: {} {}JedisPoolStats: {}{}";
				Map configMap = buildPoolConfigMap(instance.getJedisPoolConfig());
				GenericObjectPool internalPool = (GenericObjectPool) Reflections.getFieldValue(instance.getJedisPool(), "internalPool");
				Map statsMap = buildPoolStatsMap(internalPool);
				log.info(format, instance.getName(), Environments.LINE_SEPARATER, 
						JSON.toJSONString(configMap), Environments.LINE_SEPARATER, 
						JSON.toJSONString(statsMap), Environments.LINE_SEPARATER);
			}
		}
	}

	static Map buildPoolConfigMap(JedisPoolConfig jedisPoolConfig) {
		if (null == jedisPoolConfig || Strings.isBlank(jedisPoolConfig.toString())) {
			return Maps.newHashMap();
		}
		String txt = jedisPoolConfig.toString();
		if (txt.endsWith("]") && txt.startsWith("[")) {
			txt = txt.substring(1, txt.length() - 1).trim();
		}
		txt = Strings.substringsBetween(txt, "[", "]")[0];
		return Maps.parseMap(txt, ",");
	}

	static Map buildPoolStatsMap(GenericObjectPool pool) {
		return Maps.createMap(
			"isClosed", pool.isClosed(), 
			"activeNum", pool.getNumActive(),
			"idleNum", pool.getNumIdle(),
			"waitersNum", pool.getNumWaiters(),
			"createdCount", pool.getCreatedCount(),
			"borrowedCount", pool.getBorrowedCount(),
			"returnedCount", pool.getReturnedCount(),
			"destroyedCount", pool.getDestroyedCount(),
			"destroyedByEvictorCount", pool.getDestroyedByEvictorCount(),
			"destroyedByBorrowValidationCount", pool.getDestroyedByBorrowValidationCount()
		);
	}
	
	static void warnLog(Object[] params) {
		warnTimes++;
		if (warnTimes <= 10) {
			log.warn("!!! No suitable redisConfig for {}: [host={}, port={}, db={}]", params);
		}
		if (warnTimes > (Integer.MAX_VALUE - 50000)) {
			warnTimes = 20000;
		}
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy