1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application.properties增加以下配置
## redis配置
spring.redis.host=127.0.0.1
spring.redis.database=0
spring.redis.port=6379
spring.redis.password=slo
2.加入配置类
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* <p>redis缓存配置</p>
*/
@Configuration
@EnableCaching
public class RedisCachedConfig extends CachingConfigurerSupport {
//过期时间1天
// private Duration timeToLive = Duration.ofMinutes(1);
private Duration timeToLive = Duration.ofHours(1);
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//默认1
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(this.timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
return redisCacheManager;
}
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
3.编写Lock工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import org.springframework.data.redis.core.RedisTemplate;
import lombok.Data;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Data
@Component
public class RedisLock {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 重试时间
*/
private static final int DEFAULT_ACQUIRY_RETRY_MILLIS = 100;
/**
* 锁的后缀
*/
private static final String LOCK_SUFFIX = "_redis_lock";
/**
* 锁的key
*/
private String lockKey;
/**
* 锁超时时间,防止线程在入锁以后,防止阻塞后面的线程无法获取锁
*/
private int expireMsecs = 2 * 60 * 1000;
/**
* 线程获取锁的等待时间
*/
private int timeoutMsecs = 10 * 1000;
/**
* 是否锁定标志
*/
private volatile boolean locked = false;
/**
* 封装和jedis方法
*
* @param key
* @return
*/
private String get(final String key) {
Object obj = redisTemplate.opsForValue().get(key);
return obj != null ? obj.toString() : null;
}
/**
* 封装和jedis方法
*
* @param key
* @param value
* @return
*/
private boolean setNX(final String key, final String value) {
return redisTemplate.opsForValue().setIfAbsent(key, value);
}
/**
* 封装和jedis方法
*
* @param key
* @param value
* @return
*/
private String getSet(final String key, final String value) {
Object obj = redisTemplate.opsForValue().getAndSet(key, value);
return obj != null ? (String) obj : null;
}
//=================================公共方法区============================//
/**
* 初始化参数
* 默认锁超时时间 2分钟
* 默认获取锁等待时间 10秒
*
* @param lockKey 锁的key
*/
public void init(String lockKey) {
this.lockKey = lockKey + LOCK_SUFFIX;
}
/**
* 初始化参数
*
* @param lockKey 锁的key
* @param timeoutMsecs 获取锁的超时时间 单位毫秒 eg:2 * 60 * 1000
*/
public void init(String lockKey, int timeoutMsecs) {
this.lockKey = lockKey + LOCK_SUFFIX;
this.timeoutMsecs = timeoutMsecs;
}
/**
* 初始化参数
*
* @param lockKey 锁的key
* @param timeoutMsecs 获取锁的超时时间
* @param expireMsecs 锁的有效期 单位毫秒 eg: 10 * 1000
*/
public void init(String lockKey, int timeoutMsecs, int expireMsecs) {
this.lockKey = lockKey + LOCK_SUFFIX;
this.timeoutMsecs = timeoutMsecs;
this.expireMsecs = expireMsecs;
}
public String getLockKey() {
return lockKey;
}
/**
* 获取锁
*
* @return 获取锁成功返回ture,超时返回false
* @throws InterruptedException
*/
public synchronized boolean lock() throws InterruptedException {
int timeout = timeoutMsecs;
logger.info("准备获取锁{},获取锁超时时间为{}",this.getLockKey(),timeout);
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); // 锁到期时间
if (this.setNX(lockKey, expiresStr)) {
locked = true;
logger.info("成功获取锁{},锁超时时间为{}",this.getLockKey(),expires);
return true;
}
// redis里key的时间
String currentValue = this.get(lockKey);
// 判断锁是否已经过期,过期则重新设置并获取
if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
// 设置锁并返回旧值
String oldValue = this.getSet(lockKey, expiresStr);
// 比较锁的时间,如果不一致则可能是其他锁已经修改了值并获取
if (oldValue != null && oldValue.equals(currentValue)) {
logger.info("成功获取锁{},锁超时时间为{}",this.getLockKey(),expires);
locked = true;
return true;
}
}
timeout -= DEFAULT_ACQUIRY_RETRY_MILLIS;
// 延时
Thread.sleep(DEFAULT_ACQUIRY_RETRY_MILLIS);
}
logger.warn("获取锁{}失败了!",this.getLockKey());
return false;
}
/**
* 释放获取到的锁
*/
public void unlock() {
logger.info("准备释放锁{}",this.getLockKey());
if (locked) {
redisTemplate.delete(lockKey);
logger.info("成功释放锁{}",this.getLockKey());
locked = false;
}
}
}
4.redis缓存数据测试
具体类无需关心,只要看注解如何使用即可。
//REDIS缓存调用示例
@Deprecated
@Service
@CacheConfig(cacheNames = "user") //指定cache的名字,这里指定了 caheNames,下面的方法的注解里就可以不用指定 value 属性了
public class UserServiceImpl {
private Map<Long, User> userMap = new HashMap<Long, User>();
public UserServiceImpl() {
User u1=new User();
u1.setId(1L);
u1.setName("1111");
u1.setPassword("11223434");
User u2=new User();
u2.setId(2L);
u2.setName("1111");
u2.setPassword("11223434");
User u3=new User();
u3.setId(3L);
u3.setName("1111");
u3.setPassword("11223434");
userMap.put(1L,u1);
userMap.put(2L,u2);
userMap.put(3L,u3);
}
@Cacheable()
@Override
public List<User> list() {
System.out.println("querying list.....");
User[] users = new User[userMap.size()];
this.userMap.values().toArray(users);
return Arrays.asList(users);
}
@Cacheable(key = "'user:'.concat(#id.toString())")
@Override
public User findUserById(Long id) {
System.out.println("没有调用缓存");
return userMap.get(id);
}
@Cacheable(key = "'user:'.concat(#user.id)")
@Override
public void update(User user) {
System.out.println("没有调用缓存");
userMap.put(user.getId(), user);
}
// 清空cache
@CacheEvict(key = "'user:'.concat(#id.toString())")
@Override
public void remove(Long id) {
System.out.println("没有调用缓存");
userMap.remove(id);
}
@CacheEvict(key = "'user:'.concat(#id.toString())")
@Override
public User upuser(Long id) {
System.out.println("没有调用缓存");
User d = userMap.get(id);
d.setName("0000000000000000000000000000000000000000");
return d;
}
@CachePut(key = "'user:'.concat(#user.id)")
@Override
public User saveUser(User user) {
System.out.println("没有调用缓存");
userMap.put(user.getId(), user);
return user;
}
}
5.Lock嵌入添加示例以及测试
以下无需关注具体业务,只需看lock如何嵌入使用
@Transactional
@Service
public class TestSrvImpl implements TestSrv {
Logger log = LoggerFactory.getLogger(this.getClass());
@Resource
private RedisLock redisLock; //这里自动注入redisLock类
@Resource
private UserInfoRepository userInfoRepository;
@LogAnnotation(moduleName = "测试服务", option = "测试方法")
public ResponseVO testService(RequestVO requestVO) {
//获取支付配置相关信息
ResponseVO response = new ResponseVO(requestVO);
try {
log.info("准备获取锁...");
redisLock.init("Test",20*1000);
if (redisLock.lock()) {
log.info("成功获取锁,入参:{}", JSON.toJSONString(requestVO));
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("no", "123456");
resultMap.put("name", "test");
HandlerStateDTO handlerStateDTO = new HandlerStateDTO();
response.setBizObj(resultMap);
if (!HandlerType.isSuccessful(handlerStateDTO)) {
response.setRetInfo(handlerStateDTO);
}
log.info("开始进入睡眠模式");
Thread.sleep(5000);
log.info("睡眠模式解除");
redisLock.unlock();
log.info("释放锁");
}
} catch (Exception e) {
e.printStackTrace();
//异常释放锁
redisLock.unlock();
response.setRetInfo(HandlerType.UNKNOWN);
log.error("失败了,{}" + e.toString());
}
log.info("结束返回");
return response;
}
本样例SDK模拟多线程测试用例如下,自己根据自己的服务调用方式模拟调用即可。本项目采用sdk请求http接口进行调用:
/**
* 测试
*/
@Test
public void test() {
logger.info("开始请求");
SdkClient sdkClient = new SdkClient(web_pay_url, appId, appSecret, signType, encryptType);
try {
Map<String,Object> param = new HashMap<>();
param.put("name","testUser");
param.put("id","A000001");
// // 发起交易
ResponseVO responseParams = sdkClient.unifyCall("test.service.forfun",version,param);
logger.info("返回的结果为:"+JSON.toJSONString(responseParams));
logger.info("其中业务返回为:"+JSON.toJSONString(responseParams.getBizObj()));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test3() throws InterruptedException {
// 同一时刻最大的并发线程的个数 即并发数
final int concurrentThreadNum = 2;
//总共多少线程
final int countThreadNum = 2;
ExecutorService executorService = Executors.newCachedThreadPool();
CountDownLatch countDownLatch = new CountDownLatch(countThreadNum);
Semaphore semaphore = new Semaphore(concurrentThreadNum);
for (int i = 0; i< countThreadNum; i++) {
executorService.execute(()->{
try {
semaphore.acquire();
test();
semaphore.release();
} catch (InterruptedException e) {
logger.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
logger.info("请求完成");
}
6.基础框架项目
内置spring boot 和接口加解密sdk,redis锁等,下载即可开发具体业务