package com.jstarcraft.ai.data.attribute; import java.util.Arrays; import org.redisson.Redisson; import org.redisson.api.RAtomicLong; import org.redisson.api.RScript; import org.redisson.api.RScript.Mode; import org.redisson.api.RScript.ReturnType; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Builder; /** * 基于Redis的离散属性 * * @author Birdy * */ public class RedisQualityAttribute<T extends Comparable<T>> implements QualityAttribute<T> { private final static String indexLua = "local index = redis.call('hget', KEYS[1], ARGV[1]); if (not index) then index = redis.call('incr', KEYS[2]) - 1; redis.call('hset', KEYS[1], ARGV[1], index); end; return index;"; private final static String indexSuffix = "_indexes"; private final static String sizeSuffix = "_size"; /** 属性名称 */ private String name; /** 属性类型 */ private Class<T> type; private String indexKey; private String sizeKey; private ConcurrentLinkedHashMap<T, Integer> indexCache; private RScript script; private String indexSignature; private RAtomicLong sizeAtomic; public RedisQualityAttribute(String name, Class<T> type, int minimunSize, int maximunSize, Redisson redisson) { this.name = name; this.type = type; this.indexKey = name + indexSuffix; this.sizeKey = name + sizeSuffix; Builder<T, Integer> builder = new Builder<>(); builder.initialCapacity(minimunSize); builder.maximumWeightedCapacity(maximunSize); this.indexCache = builder.build(); this.script = redisson.getScript(); this.indexSignature = script.scriptLoad(indexLua); this.sizeAtomic = redisson.getAtomicLong(sizeKey); } @Override public String getName() { return name; } @Override public Class<T> getType() { return type; } @Override public int convertData(T data) { Integer index = indexCache.get(data); if (index == null) { Number number = script.evalSha(Mode.READ_WRITE, indexSignature, ReturnType.INTEGER, Arrays.asList(indexKey, sizeKey), data); index = number.intValue(); indexCache.put(data, index); } return index; } @Override public int getSize() { return (int) sizeAtomic.get(); } }