一,redis如何实现锁的,wms csms两个项目到底用的哪一种方式?
1.1 wms 与springboot无关
wms JedisCluster 所属jar
1.2 csms 属于springboot依赖
csms RedisTemplate 所属jar
compile('org.springframework.boot:spring-boot-starter-redis')
还有跟下面这个依赖有区别吗
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
二,具体如何实现,怎么连接配置信息的,有什么区别
2.1wms
通过RedisProperties加载配置信息,在通过属性配置实例化redis集群客户端JedisCluster,方法由客户端提供,底层实现
而csms中工具类方法,全部是springboot封装类RedisTemplate执行的,底层实现
集群配置
spring:
redis:
clusterNodes:
- 10.108.6.90:6179
- 10.108.6.90:6279
- 10.108.6.90:6379
- 10.108.6.90:6479
- 10.108.6.90:6579
- 10.108.6.90:6679
password: Allways_123
expireSeconds: 120
commandTimeout: 10000 #redis操作的超时时间
pool:
#最大连接数
maxActive: 5000
#最大空闲连接数
maxIdle: 30
#最小空闲连接数
minIdle: 5
#获取连接最大等待时间 ms #default -1
maxWait: 10000
#最大尝试连接数
maxAttempts: 1
属性类
@Component
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int expireSeconds;
private List<String> clusterNodes = new ArrayList<String>();
private String password;
private int commandTimeout;
private Map<String,Integer> pool = new HashMap<>();
public int getExpireSeconds() {
return expireSeconds;
}
public void setExpireSeconds(int expireSeconds) {
this.expireSeconds = expireSeconds;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getCommandTimeout() {
return commandTimeout;
}
public void setCommandTimeout(int commandTimeout) {
this.commandTimeout = commandTimeout;
}
public Map<String, Integer> getPool() {
return pool;
}
public void setPool(Map<String, Integer> pool) {
this.pool = pool;
}
public List<String> getClusterNodes() {
return clusterNodes;
}
public void setClusterNodes(List<String> clusterNodes) {
this.clusterNodes = clusterNodes;
}
}
配置类
@Configuration
public class JedisClusterConfig {
@Autowired
private RedisProperties redisProperties;
/**
* 返回的JedisCluster是单例的,并且可以直接注入到其他类中去使用
* @return
*/
@Bean
public JedisCluster getJedisCluster() {
Set<HostAndPort> nodes = new HashSet<>();
List<String> clusterNodes = redisProperties.getClusterNodes();
for (String ipPort : clusterNodes) {
String[] ipPortPair = ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
}
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(redisProperties.getPool().get("maxActive"));//最大连接数
poolConfig.setMaxIdle(redisProperties.getPool().get("maxIdle"));//最大空闲连接数
poolConfig.setMinIdle(redisProperties.getPool().get("minIdle"));//最小空闲连接数
poolConfig.setMaxWaitMillis(redisProperties.getPool().get("maxWait").longValue());//连接最大等待时间
return new JedisCluster(nodes,redisProperties.getCommandTimeout(),redisProperties.getCommandTimeout(),
redisProperties.getPool().get("maxAttempts"),redisProperties.getPassword() ,poolConfig);//需要密码连接的创建对象方式
}
}
工具类
@Component
public class RedisUtils {
public static String REDIS_RECEIVE = "receive_";
private static String redisCode = "utf-8";
public static String REDIS_OURINV = "outinv_synchronized";
private static final Long RELEASE_SUCCESS = 1L;
public final static Logger logger = LogManager.getLogger(RedisUtils.class);
@Autowired
private JedisCluster jedisCluster;
/**
* 设置缓存
*
* @param key 缓存key
* @param value 缓存value
*/
public void set(String key, String value) {
jedisCluster.set(key, value);
}
/**
* 设置缓存,并且自己指定过期时间
*
* @param key
* @param value
* @param expireTime 过期时间
*/
public void setWithExpireTime(String key, String value, int expireTime) {
jedisCluster.setex(key, expireTime, value);
}
/**
* 设置失效时间
*
* @param key
* @param expireTime
*/
public void expire(String key, int expireTime) {
jedisCluster.setex(key, expireTime, jedisCluster.get(key));
}
/**
* 获取指定key的缓存
*
* @param key
*/
public String get(String key) {
String value = jedisCluster.get(key);
return value;
}
//解决redis异常
public String getThrowRedis(String key) {
try {
get(key);
} catch (Exception e) {
return null;
}
return get(key);
}
/**
* 设置缓存对象
*
* @param key 缓存key
* @param obj 缓存value
* @param expireTime 过期时间
*/
public <T> void setObject(String key, T obj, int expireTime) {
jedisCluster.setex(key, expireTime, JSON.toJSONString(obj));
}
/**
* 获取指定key的缓存
*
* @param key---JSON.parseObject(value, User.class);
*/
public String getObject(String key) {
return jedisCluster.get(key);
}
/**
* 判断当前key值 是否存在
*
* @param key
*/
public boolean hasKey(String key) {
return jedisCluster.exists(key);
}
/**
* 删除指定key的缓存
*
* @param key
*/
public void delete(String key) {
jedisCluster.del(key);
}
/**
* <pre>
* 上锁
* </pre>
*
* @param key
*/
public synchronized void lock(String key) {
if (StringUtils.isBlank(key)) {
return;
}
this.set(key, new Date().toString());
}
/**
* <pre>
* 上锁
* </pre>
*
* @param key
*/
public synchronized void lockWithExpireTime(String key, int seconds) {
if (StringUtils.isBlank(key)) {
return;
}
jedisCluster.setex(key, seconds, new Date().toString());
}
/**
* <pre>
* 判断key是否被锁住了
* </pre>
*
* @param key
* @return
*/
public synchronized Boolean isLock(String key) {
if (StringUtils.isBlank(key)) {
return false;
}
String stringDate = this.get(key);
if (StringUtils.isBlank(stringDate)) {
return false;
}
return true;
}
/**
* <pre>
* 解锁
* </pre>
*
* @param key
*/
public synchronized void unLock(String key) {
if (StringUtils.isBlank(key)) {
return;
}
this.delete(key);
}
/**
* <pre>
* 递增
* </pre>
*
* @param key
* @param by
* @return
*/
public Long incr(String key, Long by) {
//默认原子操作
if (by == null) {
by = 1l;
}
return jedisCluster.incrBy(key, by);
}
/**
* <pre>
*
* </pre>
*
* @param key
* @param value
* @param liveTime
*/
public void set(String key, Object value, Long liveTime) {
try {
jedisCluster.setex(key.getBytes(redisCode), liveTime.intValue(), value.toString().getBytes(redisCode));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* <pre>
* 批量删除keys
* </pre>
*
* @param keys
*/
public void delKeys(String[] keys) {
if(keys != null) {
for (String key : keys) {
Long del = jedisCluster.del(key);
System.out.println(del);
}
}
}
public void unlockValue(String keys, String value) {
String lockValue = jedisCluster.get(keys);
if (lockValue.equals(value)){
jedisCluster.del(keys);
}
}
public String[] keys(String pattern){
TreeSet<String> keys = new TreeSet<>();
Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
for(String k : clusterNodes.keySet()){
logger.debug("Getting keys from: {}", k);
JedisPool jp = clusterNodes.get(k);
Jedis connection = jp.getResource();
try {
keys.addAll(connection.keys(pattern));
} catch(Exception e){
logger.error("Getting keys error: {}", e);
} finally{
logger.debug("Connection closed.");
connection.close();//用完一定要close这个链接!!!
}
}
return keys.toArray(new String[] {});
}
/**
* 设置缓存
* @param key 缓存key
* @param value 缓存value
*/
public Long setnx(String key, String value) {
return jedisCluster.setnx(key, value);
}
/**
* <pre>
* 上锁
* </pre>
*
* @param key
*/
public synchronized boolean locknx(String key){
if(StringUtils.isBlank(key)){
return false;
}
Long result = this.setnx(key, new Date().toString());
if(result.equals(0L)){//如果返回0 则该key已经存在
return false;
}
return true;
}
/**
* <pre>
* 上锁
* </pre>
*
* @param key
*/
public synchronized boolean locknx(String key, String value){
if(StringUtils.isBlank(key)){
return false;
}
Long result = this.setnx(key, value);
if(result.equals(0L)){//如果返回0 则该key已经存在
return false;
}
this.expire(key, 60*3);//设置失效时间3分钟
return true;
}
public synchronized boolean lock(String lockKey,String requestId,int expireTime) {
String result=jedisCluster.set(lockKey, requestId, "NX", "PX", expireTime);
if("OK".equals(result)) {
return true;
}
return false;
}
public synchronized boolean releaseLock(String lockKey,String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedisCluster.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
2.2csms
而csms中工具类方法,全部是springboot封装类RedisTemplate执行的,具体执行方法又是由RedisConnection这个类完成
读取配置文件信息,还需要定义properties属性类吗,不可以从autoconfigtion中定义的直接读取吗,就算从这读取了,读取原理是什么,怎么用的?
单机配置
redis:
database: 0
host: 10.108.2.202
port: 6379
password: allways_123
pool:
#最大连接数
max-active: 5000
#获取连接最大等待时间 ms #default -1
max-wait: 10000
#最大空闲连接数
max-idle: 30
#最小空闲连接数
min-idle: 5
timeout: 0
工具类
package com.anji.allways.base.redis;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.SerializationUtils;
/**
* <pre>
* RedisUtils
* </pre>
*
* @author wanglong
* @version $Id: RedisUtils.java, v 0.1 2018年3月16日 上午9:40:56 wanglong Exp $
*/
@Configuration
public class RedisUtils {
@Resource
private RedisTemplate<String, String> redisTemplate;
private static String redisCode = "utf-8";
/**
* 默认过期时长,单位:秒
*/
public static final long DEFAULT_EXPIRE = 60 * 3;
/**
* @param keys
* k
* @return l
*/
public long del(final String... keys) {
return redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
long result = 0;
for (int i = 0; i < keys.length; i++) {
try {
result += connection.del(keys[i].getBytes(redisCode));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result;
}
});
}
/**
* @since 1.0
* @param key
* k
* @param value
* v
* @param liveTime
*/
public void set(final byte[] key, final byte[] value, final long liveTime) {
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
connection.set(key, value);
if (liveTime > 0) {
connection.expire(key, liveTime);
}
return 1L;
}
});
}
/**
* @since 1.0
* @param key
* k
* @param value
* v
* @param liveTime
*/
public void set(String key, String value, long liveTime) {
try {
this.set(key.getBytes(redisCode), value.getBytes(redisCode), liveTime);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* @param key
* k
* @param value
* v
*/
public void set(String key, String value) {
this.set(key, value, 0L);
}
/**
* @param key
* k
* @param value
* v
*/
public void set(byte[] key, byte[] value) {
this.set(key, value, 0L);
}
/**
* @param key
* l
* @return s
*/
public String get(final String key) {
return redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection connection) throws DataAccessException {
try {
byte[] data = connection.get(key.getBytes(redisCode));
if (data != null) {
return new String(data, redisCode);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
});
}
/**
* @param pattern
* p
* @return set
*/
public Set<String> keys(String pattern) {
return redisTemplate.keys(pattern);
}
/**
* @param key
* k
* @return b
*/
public boolean exists(final String key) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
try {
return connection.exists(key.getBytes(redisCode));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
});
}
/**
* @return s
*/
public String flushDB() {
return flush();
}
public String flush() {
return redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
}
/**
* @return l
*/
public long dbSize() {
return dbs();
}
public long dbs() {
return redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.dbSize();
}
});
}
/**
* @return s
*/
public String ping() {
return pingRedis();
}
public String pingRedis() {
return redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection connection) throws DataAccessException {
return connection.ping();
}
});
}
/**
* @since 1.0
* @param key
* k
* @return
*/
public Object getObject(final String key) {
return SerializationUtils.deserialize(redisTemplate.execute(new RedisCallback<byte[]>() {
public byte[] doInRedis(RedisConnection connection) throws DataAccessException {
try {
return connection.get(key.getBytes(redisCode));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] str = null;
return str;
}
}));
}
/**
* 将 key 的值设为 value ,当且仅当 key 不存在。 <br>
* 若给定的 key 已经存在,则 SETNX 不做任何动作。
* @param key
* @param value
* @return 设置成功,返回 1 。 设置失败,返回 0 。
*/
public boolean setNX(final String key, final String value) {
Object obj = null;
try {
obj = redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisSerializer serializer = new StringRedisSerializer();
Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
connection.close();
return success;
}
});
} catch (Exception e) {
e.printStackTrace();
}
return obj != null ? (Boolean) obj : false;
}
/**
* 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。<br>
* 当 key 存在但不是字符串类型时,返回一个错误。
* @param key
* @param value
* @return 返回给定 key 的旧值。 当 key 没有旧值时,也即是, key 不存在时,返回 null 。
*/
public String getSet(final String key, final String value) {
Object obj = null;
try {
obj = redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
connection.close();
return serializer.deserialize(ret);
}
});
} catch (Exception e) {
e.printStackTrace();
}
return obj != null ? (String) obj : null;
}
/**
* 递增
* @param key
* @param by
* @return value
*/
public Long incr(String key,Long by) {
//默认原子操作
if(by == null) {
by = 1l;
}
return redisTemplate.opsForValue().increment(key, by);
}
/**
* 递增
* @param key
* @param by
* @return value
*/
public Long decr(String key,Long by) {
return redisTemplate.opsForValue().increment(key, -by);
}
/**
* <pre>
* 上锁
* </pre>
*
* @param key
*/
public synchronized void lock(String key){
if(StringUtils.isBlank(key)){
return;
}
this.set(key, new Date().toString());
}
/**
* <pre>
* 判断key是否被锁住了
* </pre>
*
* @param key
* @return
*/
public synchronized Boolean isLock(String key){
if(StringUtils.isBlank(key)){
return false;
}
String stringDate = this.get(key);
if(StringUtils.isBlank(stringDate)){
return false;
}
return true;
}
/**
* <pre>
* 解锁
* </pre>
*
* @param key
*/
public synchronized void unLock(String key){
if(StringUtils.isBlank(key)){
return;
}
this.del(key);
}
/**
* <pre>
* 上锁 NX 加三分钟过期时间
* ,上锁成功返回true,失败false
* </pre>
*
* @param key
*/
public synchronized boolean locknx(String key){
if(StringUtils.isBlank(key)){
return false;
}
boolean setNX = this.setNX(key, new Date().toString());
//设置过期时间,三分钟
this.redisTemplate.expire(key,DEFAULT_EXPIRE,TimeUnit.SECONDS);
return setNX;
}
/**
* 设置缓存,并且自己指定过期时间(废弃)
* @param key
* @param value
* @param expireTime(秒)
*/
public void setWithExpireTime(String key, String value, int expireTime) {
this.setNX(key, value);
this.redisTemplate.expire(key,expireTime,TimeUnit.SECONDS);
}
/**
* 判断当前key值 是否存在
*
* @param key
*/
public boolean hasKey(String key) {
String stringDate = this.get(key);
if(StringUtils.isBlank(stringDate)){
return false;
}
return true;
}
/**
* 刷新缓存时间
* @param key
* @param expireTime
*/
public void expire(String key, int expireTime){
this.setNX(key, this.get(key));
this.redisTemplate.expire(key,expireTime,TimeUnit.SECONDS);
}
}