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

net.wicp.tams.common.redis.pool.AbsPool Maven / Gradle / Ivy

The newest version!
/*
 * **********************************************************************
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称:common
 * 项目描述:公共的工具集
 * 版权说明:本软件属andy.zhou([email protected])所有。
 * ***********************************************************************
 */
package net.wicp.tams.common.redis.pool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.pool2.impl.AbandonedConfig;

import com.fasterxml.jackson.databind.ObjectMapper;

import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.ReflectAssist;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectExceptionRuntime;
import net.wicp.tams.common.redis.RedisAssit;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.StreamEntryID;
import redis.clients.jedis.params.SetParams;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadGroupParams;
import redis.clients.jedis.resps.StreamEntry;
import redis.clients.jedis.util.Pool;

@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class AbsPool {
	/*****
	 * 把对象做为Map存放到Redis
	 * 
	 * @param jedis  连接的引用,外部传入
	 * @param key    要放入的key值
	 * @param obj    要放入的对象
	 * @param     对象的类型
	 * @param expire 超时时间,单位(秒)
	 */
	public static  void putObjByMap(Jedis jedis, String key, T obj, Integer expire) {
		Map inpumap = ReflectAssist.convertMapFromBean(obj);
		jedis.hmset(key, inpumap);
		if (expire != null) {
			jedis.expire(key, expire);
		}
	}

	/***
	 * ###############sadd动作用于汇总所有相关的key值 把对象做为Map存放到Redis,没有超时时间,永久性放入
	 * 
	 * @param jedis 连接的引用,外部传入
	 * @param key   要放入的key值
	 * @param    对象的类型
	 * @param obj   要放入的对象
	 */
	public static  void putObjByMap(Jedis jedis, String key, T obj) {
		putObjByMap(jedis, key, obj, null);
	}

	/***
	 * 把对象做为Map存放到Redis,并把key存到 groupkey中
	 * 
	 * @param 
	 * @param jedis
	 * @param key
	 * @param groupkey
	 * @param obj
	 */
	public static  void putObjByMap(Jedis jedis, String key, String groupkey, T obj) {
		jedis.sadd(groupkey, key);
		putObjByMap(jedis, key, obj);
	}

	/***
	 * 把对象做为Json存放到Redis
	 * 
	 * @param jedis  连接的引用,外部传入
	 * @param obj    要放入的对象
	 * @param key    要放入的key值
	 * @param     对象的类型
	 * @param expire 超时时间,单位(秒)
	 */
	public final static  void putObjByJson(Jedis jedis, T obj, String key, Integer expire) {
		ObjectMapper mapper = new ObjectMapper();
		try {
			String json = mapper.writeValueAsString(obj);
			jedis.set(key, json);
			if (expire != null) {
				jedis.expire(key, expire);
			}
		} catch (Exception e) {
			throw new ProjectExceptionRuntime(ExceptAll.Project_default, e);
		}

	}

	/****
	 * 把对象做为Json存放到Redis
	 * 
	 * @param jedis 连接的引用,外部传入
	 * @param obj   要放入的对象
	 * @param    对象的类型
	 * @param key   要放入的key值
	 */
	public final static  void putObjByJson(Jedis jedis, T obj, String key) {
		putObjByJson(jedis, obj, key, null);
	}

	/***
	 * 枚举类型的map放到缓存
	 * 
	 * @param jedis    连接的引用,外部传入
	 * @param key      要放入的key值
	 * @param       对象的类型
	 * @param inputMap 要存放的map,它的key是一个枚举值
	 */

	public final static  void putEnumMap(Jedis jedis, String key, Map inputMap) {
		if (jedis == null || MapUtils.isEmpty(inputMap) || StringUtil.isNull(key)) {
			return;
		}
		Map input = new HashMap<>();
		for (Enum ele : inputMap.keySet()) {
			input.put(ele.name(), inputMap.get(ele));
		}
		jedis.hmset(key, input);
	}

	/****
	 * 得到枚举类的缓存对象
	 * 
	 * @param jedis 连接的引用,外部传入
	 * @param key   要放入的key值
	 * @param clazz 枚举所对应的类
	 * @return 返回的枚举
	 */

	public final static  Map getEnumMap(Jedis jedis, String key, Class clazz) {
		Object[] objs = clazz.getEnumConstants();
		String[] fields = new String[objs.length];
		for (int i = 0; i < fields.length; i++) {
			Enum tempobj = (Enum) objs[i];
			fields[i] = tempobj.name();
		}
		List rets = jedis.hmget(key, fields);
		Map retobj = new HashMap<>();
		for (int i = 0; i < fields.length; i++) {
			Enum curobj = null;
			for (Object object : objs) {
				Enum tempobj = (Enum) object;
				if (fields[i].equals(tempobj.name())) {
					curobj = tempobj;
					break;
				}
			}
			retobj.put((T) curobj, rets.get(i));
		}
		return retobj;
	}

	/***
	 * 取指定列的值
	 * 
	 * @param jedis  连接的引用,外部传入
	 * @param key    要放入的key值
	 * @param fields 要取的列名
	 * @return
	 */
	public static Map getMapByField(Jedis jedis, String key, String... fields) {
		Map retobj = new HashMap();
		if (jedis == null || StringUtil.isNull(key) || ArrayUtils.isEmpty(fields) || !jedis.exists(key)) {
			return retobj;
		}
		List values = jedis.hmget(key, fields);
		if (ArrayUtils.isNotEmpty(fields) && CollectionUtils.isNotEmpty(values)) {
			for (int i = 0; i < fields.length; i++) {
				retobj.put(fields[i], values.get(i));
			}
		}
		return retobj;
	}

	/***
	 * Redis上的值为Map,取对象的值,没有指定字段就取全部
	 * 
	 * @param clazz  要返回的对象的类
	 * @param jedis  连接的引用,外部传入
	 * @param key    要放入的key值
	 * @param     对象的类型
	 * @param fields 要取的列名
	 * @return 要返回的对象
	 */
	public static  T getObjByMapValue(Class clazz, Jedis jedis, String key, String... fields) {
		if (ArrayUtils.isEmpty(fields)) {
			List classfields = ReflectAssist.findGetField(clazz);
			fields = classfields.toArray(new String[classfields.size()]);
		}
		Map retmap = getMapByField(jedis, key, fields);
		T rett = (T) ReflectAssist.convertMapToBean(clazz, retmap);
		return rett;
	}

	public static  T getObjByMapValue(Class clazz, Jedis jedis, String key) {
		List list = ReflectAssist.findGetField(clazz);
		String[] array = list.toArray(new String[list.size()]);
		return getObjByMapValue(clazz, jedis, key, array);
	}

	/***
	 * Redis上的值为Json,取对象的值
	 * 
	 * @param clazz 要返回的对象的类
	 * @param jedis 连接的引用,外部传入
	 * @param key   要放入的key值
	 * @param    对象的类型
	 * @return
	 */
	public static  T getObjByJsonValue(Class clazz, Jedis jedis, String key) {
		String jsonstr = jedis.get(String.valueOf(key));
		ObjectMapper objmap = new ObjectMapper();
		try {
			T value = objmap.readValue(jsonstr, clazz);
			return value;
		} catch (Exception e) {
			throw new ProjectExceptionRuntime(ExceptAll.Project_default, e);
		}

	}

//	/***
//	 * 释放资源
//	 *
//	 * @param jedis 要释放的连接
//	 */
//	public static void returnResource(Jedis jedis) {
//		if (jedis != null) {
//			try {
//				jedis.close();
//			} catch (Exception e) {
//
//			}
//		}
//	}

	public abstract Jedis getResource();

	public abstract void returnResource(Jedis jedis);

	public abstract void returnBrokenResource(Jedis jedis);

	/***
	 * 池废弃
	 */
	public abstract void destroy();

	public abstract boolean isInit();

	protected void doLeak(String serverName, Pool jedisPool) {
		Map data = Conf
				.getAllConfigValues(String.format("%s.%s", "common.redis.redisserver", serverName), true);
		if (RedisAssit.getConfigInt(data, "leak.second") > 0) {
			AbandonedConfig abandonedConfig = new AbandonedConfig();
			abandonedConfig.setRemoveAbandonedOnMaintenance(true); // 在Maintenance的时候检查是否有泄漏
			abandonedConfig.setRemoveAbandonedOnBorrow(true); // borrow 的时候检查泄漏
			abandonedConfig.setRemoveAbandonedTimeout(RedisAssit.getConfigInt(data, "leak.second")); // 如果一个对象borrow之后默认10秒还没有返还给pool,认为是泄漏的对象
			// jedis2.9时需要通过反射搞定。4.1不需要了
//			@SuppressWarnings("unchecked")
//			GenericObjectPool poolInner = (GenericObjectPool) ReflectAssist.getPrivateField(jedisPool,
//					"internalPool");
//			poolInner.setAbandonedConfig(abandonedConfig);
			jedisPool.setAbandonedConfig(abandonedConfig);
		}
	}

	///////////////////////////////////// 一些工具//////////////////////////////////////////////////////////////////
	public  T findByRedis(Class calssz, String key) {
		Jedis jedis = getResource();
		try {
			T t = AbsPool.getObjByMapValue(calssz, jedis, key);
			return t;
		} finally {
			returnResource(jedis);
		}
	}

	public  void putRedis(T saveobj, String key) {
		Jedis jedis = getResource();
		try {
			AbsPool.putObjByMap(jedis, key, saveobj);
		} finally {
			returnResource(jedis);
		}
	}

	/**
	 * 使用redis进行分布式加锁操作
	 * 
	 * @param key        锁的key
	 * @param value      加锁的值
	 * @param expireTime 获取锁的单位毫秒
	 * @return
	 */
	public boolean tryDistributedLock(Jedis jedis, String key, String value, int expireTime) {
		SetParams params = new SetParams();
		String result = jedis.set(key.getBytes(), value.getBytes(), params.nx().px(expireTime));
		if ("OK".equals(result)) {
			return true;
		} else {
			return false;
		}
	}

	/***
	 * 将数据传入stream,这个stream最大允许存max条记录
	 * 
	 * @param jedis     连接
	 * @param streamkey 队列名
	 * @param max       队列最大值
	 * @param content   传入的内容
	 * @return
	 */
	public StreamEntryID putStream(Jedis jedis, String streamkey, long max, Map content) {
		// new StreamEntryID().NEW_ENTR = * ,代表id由redis生成
		// 也可以指定stream最多存储多少条记录。--自己查api
		XAddParams params = XAddParams.xAddParams();
		params.maxLen(max);
		params.id(StreamEntryID.NEW_ENTRY);
		return jedis.xadd(streamkey, params, content);
	}

	/***
	 * 传入二进制数据
	 * 
	 * @param jedis
	 * @param streamkey
	 * @param max
	 * @param content
	 * @return
	 */
	public byte[] putStreamByte(Jedis jedis, String streamkey, long max, Map content) {
		// new StreamEntryID().NEW_ENTR = * ,代表id由redis生成
		// 也可以指定stream最多存储多少条记录。--自己查api
		XAddParams params = XAddParams.xAddParams();
		params.maxLen(max);
		params.id(StreamEntryID.NEW_ENTRY);
		return jedis.xadd(streamkey.getBytes(), params, content);
	}

	public StreamEntryID putStream(Jedis jedis, String streamkey, Map content) {
		return putStream(jedis, streamkey, Conf.getLong("common.redis.client.stream.max"), content);
	}

	public byte[] putStreamByte(Jedis jedis, String streamkey, Map content) {
		return putStreamByte(jedis, streamkey, Conf.getLong("common.redis.client.stream.max"), content);
	}

	/***
	 * 阻塞获取stream的最后一条记录,由于是长链接,可能会Broken,所以自己来拿连接并自己管理,
	 * 
	 * @param streamkey 队列key
	 * @param groupname 队列group名
	 * @return 惟一的那条数据
	 */
	public Map getStreamBlockOne(String streamkey, String groupname) {
		Jedis jedis = this.getResource();
		try {
			String xgroupCreate = jedis.xgroupCreate(streamkey, groupname, StreamEntryID.LAST_ENTRY, true);
			if (!"OK".equals(xgroupCreate)) {// 创建失败?
				System.out.println(xgroupCreate);
			}
		} catch (Throwable e) {
			if (e.getMessage().contains("Group name already exists")) {
				// 一般来说是group已创建异常
				// e.printStackTrace();
			} else {
				returnResource(jedis);
				throw new ProjectExceptionRuntime(ExceptAll.Project_default, "创建stream组失败", e);
			}
		}
		try {
			XReadGroupParams params = XReadGroupParams.xReadGroupParams();
			params.block(Conf.getInt("common.redis.client.stream.blocktimes"));// 永久阻塞,1000表示阻塞1S
			params.count(Conf.getInt("common.redis.client.stream.batchPreNum"));// 拿1条消息
			Map streams = new HashMap();
			// > 号表示从当前消费组的 last_delivered_id 后面开始读
			streams.put(streamkey, StreamEntryID.UNRECEIVED_ENTRY);
			// 元素组成: key:streamkey value:streamkey对应的值,
			List>> xreadGroup = jedis.xreadGroup(groupname,
					Conf.get("common.redis.client.stream.consumer"), params, streams);
			StreamEntry streamEntry = xreadGroup.get(0).getValue().get(0);
			Map retmap = streamEntry.getFields();
			// 到此有可能此链接已超时被费掉了。
//			if(jedis.isBroken()) {
//				jedis.close();
//			}
			return retmap;
		} finally {
			returnResource(jedis);
		}

	}

	// 从消息里拿到duckula消息
	public Pair getStreamBlockOneDuckulaEvent(String streamkey, String groupname) {
		Jedis jedis = this.getResource();
		try {
			// 从头开始消费
			String xgroupCreate = jedis.xgroupCreate(streamkey, groupname, null, true);
			if (!"OK".equals(xgroupCreate)) {// 创建失败?
				System.out.println(xgroupCreate);
			}
		} catch (Throwable e) {
			if (e.getMessage().contains("Group name already exists")) {
				// 一般来说是group已创建异常
				// e.printStackTrace();
			} else {
				returnResource(jedis);
				throw new ProjectExceptionRuntime(ExceptAll.Project_default, "创建stream组失败", e);
			}
		}
		try {
			XReadGroupParams params = XReadGroupParams.xReadGroupParams();
			params.block(Conf.getInt("common.redis.client.stream.blocktimes"));// 永久阻塞,1000表示阻塞1S
			params.count(Conf.getInt("common.redis.client.stream.batchPreNum"));// 拿1条消息
//			params.noAck();//不需要ack,表示不会进入到待处理列表
			Map streams = new HashMap();
			// > 号表示从当前消费组的 last_delivered_id 后面开始读
			streams.put(streamkey.getBytes(), StreamEntryID.UNRECEIVED_ENTRY.toString().getBytes());
			// 元素组成: key:streamkey value:streamkey对应的值,
			List xreadGroup = jedis.xreadGroup(groupname.getBytes(),
					Conf.get("common.redis.client.stream.consumer").getBytes(), params,
					streams.entrySet().iterator().next());
			if (CollectionUtils.isEmpty(xreadGroup)) {// 出错了:Unknown reply: 1
				return null;
			}

			List all = (List) xreadGroup.get(0);

			String key = new String((byte[]) all.get(0));// 例如:dim:demo:user_info:control:stream
			List ele = ((List) all.get(1));

			List dataHasIdList = ((List) ele.get(0));
			// 队列的key
			String idValue = new String((byte[]) dataHasIdList.get(0));// 例如:1655808258188-0
			List datalist = ((List) dataHasIdList.get(1));
			// data或time
			String key1 = new String((byte[]) datalist.get(0));
//		    String key2= new String((byte[])datalist.get(2));
			jedis.xack(streamkey.getBytes(), groupname.getBytes(), idValue.getBytes());// 手动ack
			if ("data".equals(key1)) {
				byte[] dataValue = (byte[]) datalist.get(1);
				String time = new String((byte[]) datalist.get(3));
				return Pair.of(new Long(time), dataValue);
			} else {
				byte[] dataValue = (byte[]) datalist.get(3);
				String time = new String((byte[]) datalist.get(1));
				return Pair.of(new Long(time), dataValue);
			}

		} catch (Throwable e) {
			return null;
		} finally {
			returnResource(jedis);
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy