docker安装redis
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.6.1version>
dependency>
// 创建jedis对象
jedis = new Jedis("192.168.253.129",6379);
// 输入连接密码
jedis.auth("123456");
// 选择数据库
jedis.select(0);
// 插入字符串
jedis.set("name","wangwu");
System.out.println("name="+jedis.get("name"));
@AfterEach
public void after(){
if (jedis!=null){
jedis.close();
}
}
package com.example.dockerfile;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;
@SpringBootTest
class DockerfileApplicationTests {
private Jedis jedis;
@Test
void contextLoads() {
// 创建jedis对象
jedis = new Jedis("192.168.253.129",6379);
// 输入连接密码
jedis.auth("123456");
// 选择数据库
jedis.select(0);
// 插入字符串
jedis.set("name","wangwu");
System.out.println("name="+jedis.get("name"));
}
@AfterEach
public void after(){
if (jedis!=null){
jedis.close();
}
}
}
public class JedisPoolFactory {
private static final JedisPool jdedisppool ;
static {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 设置最大连接数
jedisPoolConfig.setMaxTotal(8);
//设置最大空闲连接数
jedisPoolConfig.setMaxIdle(8);
// 设置最小空闲连接数
jedisPoolConfig.setMinIdle(0);
// 如果长时间空闲,连接池中的对象会被清理
// 设置等待时间
// Duration duration = new Duration(1000);
jedisPoolConfig.setMaxWaitMillis(1000);
jdedisppool=new JedisPool(jedisPoolConfig,"192.168.253.129",6379,1000,"123456");
}
// 获取资源
public static Jedis getjedis(){
return jdedisppool.getResource();
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
spring:
redis:
host: 192.168.253.129
port: 6379
password: 123456
lettuce:
pool:
max-active: 8
min-idle: 0
max-idle: 8
max-wait: 1000
@SpringBootTest
public class SpringRedisTest {
@Resource
private RedisTemplate redisTemplate;
@Test
public void test01(){
redisTemplate.opsForValue().set("name","lmx");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
@Configuration
public class RedisConfigure {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建redistemplate对象
RedisTemplate<String, Object> stringObjectRedisTemplate = new RedisTemplate<>();
// 设置连接工厂
stringObjectRedisTemplate.setConnectionFactory(connectionFactory);
// 设置json序列化工具
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置key的序列化方式
stringObjectRedisTemplate.setKeySerializer(RedisSerializer.string());
stringObjectRedisTemplate.setHashKeySerializer(RedisSerializer.string());
// 设置值的序列化方式
stringObjectRedisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
stringObjectRedisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return stringObjectRedisTemplate;
}
}
@SpringBootTest
public class SpringRedisStringTest {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
public void test01(){
Person person = new Person("李满祥", 18);
String s = JSONObject.toJSONString(person);
stringRedisTemplate.opsForValue().set("person",s);
String person1 = stringRedisTemplate.opsForValue().get("person");
System.out.println(person1);
Person person2 = JSONObject.parseObject(person1, Person.class);
System.out.println(person2);
}
}
@Override
public Result SendPhone(String phone, HttpSession session) {
// 校验手机验证码
if (RegexUtils.isPhoneInvalid(phone)) {
return Result.fail("手机号格式错误");
}
//生成验证码
String s = RandomUtil.randomNumbers(4);
log.info("生成的验证码是:" + s);
// 验证码保存在session中
session.setAttribute(SavePattern.PHONECODE, s);
return Result.ok();
}
@Override
public Result LoginService(LoginFormDTO loginForm, HttpSession session) {
// 校验手机号
if (RegexUtils.isPhoneInvalid(loginForm.getPhone())){
return Result.fail("手机号格式错误");
}
// 校验验证码
Object sessioncode = session.getAttribute(SavePattern.PHONECODE);
if (loginForm.getCode()==null || !loginForm.getCode().equals(sessioncode)){
return Result.fail("验证码错误");
}
// 数据库中查询用户
User user = query().eq("phone", loginForm.getPhone()).one();
// 如果没有该用户,插入数据库
if (user==null){
user=new User();
user.setPhone(loginForm.getPhone());
// 随机生成昵称
String s = RandomUtil.randomString(10);
user.setNickName("user_"+s);
save(user);
}
// 用户信息保存到session中
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setNickName(user.getNickName());
session.setAttribute(SavePattern.LOGINUSER,userDTO);
log.info("登录用户的信息已存入session中");
return Result.ok();
}
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// return HandlerInterceptor.super.preHandle(request, response, handler);
HttpSession session = request.getSession();
UserDTO attribute = (UserDTO) session.getAttribute(SavePattern.LOGINUSER);
// 将用户的信息存入thradlocal中
if (attribute==null){
response.setStatus(401);
return false;
}
// 不放行;
// 身份不通过
UserHolder.saveUser(attribute);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
// HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
package com.hmdp.config;
import com.hmdp.controller.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
@Configuration
public class Webconfigure implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
ArrayList<String> patterns = new ArrayList<>();
patterns.add("/user/code");
patterns.add("/user/login");
patterns.add("/user/logout");
// patterns.add("/user/me");
patterns.add("/shop/**");
patterns.add("/shop-type/**");
patterns.add("/upload/**");
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns(patterns);
}
}
@Override
public Result queryByid(Long id) {
String o = "shop:" + id;
String s = stringRedisTemplate.opsForValue().get(o);
if (s!=null){
// 缓存有商铺信息
Shop shop = JSONObject.parseObject(s, Shop.class);
return Result.ok(shop);
}
// 如果不存在,数据库中查询
Shop shop = query().eq("id", id).one();
if (shop==null){
return Result.fail("无此商铺信息");
}
String shopstring = JSONObject.toJSONString(shop);
stringRedisTemplate.opsForValue().set(o,shopstring);
return Result.ok(shop);
}
@Override
@Transactional
public Result updateByid(Shop shop) {
// 先做校验
if (shop == null && shop.getId() == 0L) {
return Result.fail("商铺信息或商铺id不能为空");
}
// 更新数据库操作
updateByid(shop);
// 删除缓存
stringRedisTemplate.delete(pre+shop.getId());
return Result.ok();
}
@Override
public Result queryByid(Long id) {
String o = pre + id;
String s = stringRedisTemplate.opsForValue().get(o);
// if (s!=null && ){
// return Result.fail("d店铺不存在");
// }
if (s != null) {
// 解决缓存穿透的问题
if ("".equals(s)){
return Result.fail("店铺不存在");
}
// 缓存有商铺信息
Shop shop = JSONObject.parseObject(s, Shop.class);
return Result.ok(shop);
}
// 如果不存在,数据库中查询
Shop shop = query().eq("id", id).one();
if (shop == null) {
// 将空对象写入缓存中,解决缓存穿透的问题
stringRedisTemplate.opsForValue().set(o,"",2L,TimeUnit.MINUTES);// 有效时间是两分钟
return Result.fail("无此商铺信息");
}
String shopstring = JSONObject.toJSONString(shop);
stringRedisTemplate.opsForValue().set(o, shopstring);
stringRedisTemplate.expire(o, 30L, TimeUnit.MINUTES);//设置过期时间为30 分钟
return Result.ok(shop);
}
@Override
public Result queryByid(Long id) {
// 使用缓存穿透的方法解决
// Shop shop=getshopWithCatchThrouw(id);
// 解决缓存击穿的问题,使用互斥锁
String o = pre + id;
String s = stringRedisTemplate.opsForValue().get(o);
// if (s!=null && ){
// return Result.fail("d店铺不存在");
// }
if (s != null) {
// 解决缓存穿透的问题
if ("".equals(s)) {
return Result.fail("店铺不存在");
}
// 缓存有商铺信息
Shop shop = JSONObject.parseObject(s, Shop.class);
return Result.ok(shop);
}
Shop shop = null;
try {
boolean getlock = getlock();
if (!getlock) {
// 进行等待
Thread.sleep(50);
// 进行重试
queryByid(id);
}
shop = query().eq("id", id).one();
if (shop == null) {
// 将空对象写入缓存中,解决缓存穿透的问题
stringRedisTemplate.opsForValue().set(o, "", 2L, TimeUnit.MINUTES);// 有效时间是两分钟
return Result.fail("店铺不存在");
}
String shopstring = JSONObject.toJSONString(shop);
stringRedisTemplate.opsForValue().set(o, shopstring);
stringRedisTemplate.expire(o, 30L, TimeUnit.MINUTES);//设置过期时间为30 分钟
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}finally {
unlock();
}
return Result.ok(shop);
}
// 得到锁,使用redis的sentnx方法,如果redis中有key,则不会创建,返回false
public boolean getlock() {
Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("lock:shop", "1", 10, TimeUnit.SECONDS);
return aBoolean;
}
// 释放锁
public void unlock() {
stringRedisTemplate.delete("lock:shop");
}
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
// 解决缓存击穿的问题,使用逻辑过期的方法
private Shop getshopWithLogicLock(Long id) {
// 查询缓存中是否有数据
// 如果有数据,则判断是否过期,如果没有过期,直接返回
// 如果已过期,进行缓存重建,返回旧数据
String o = pre + id;
String s = stringRedisTemplate.opsForValue().get(o);
// 如果未命中,返回空
if (s == null) {
return null;
}
RedisData redisData = JSONObject.parseObject(s, RedisData.class);
LocalDateTime ecpiretime = redisData.getEcpiretime();//过期时间
JSONObject data = (JSONObject) redisData.getData();
Shop shop = JSON.toJavaObject(data, Shop.class);
// 过期时间在现在时间之前,说明过期
if (ecpiretime.isBefore(LocalDateTime.now())) {
// 如果获取到锁进行缓存重建,如果没有则,将其他进程在进行缓存重建,直接放回旧数据
boolean getlock = getlock();
if (getlock) {
executorService.submit(() -> {
try {
saveshopwithlogic(id, 2L);// 设置过期时间是2秒
} catch (InterruptedException e) {
throw new RuntimeException(e.getMessage());
} finally {
unlock();
}
});
}
}
return shop;
}
注:java中的线程池方法:
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.submit(() -> {
try {
saveshopwithlogic(id, 2L);// 设置过期时间是2秒
} catch (InterruptedException e) {
throw new RuntimeException(e.getMessage());
} finally {
unlock();
}
});
@Component
public class RedisOneID {
// 生成全局唯一id,生成的id 符号位+时间戳+序列号
private final static long starttime = 1286064000L;
@Resource
private StringRedisTemplate stringRedisTemplate;
public long getLongId(String pre) {
// 获取当前的时间戳
long l = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
// long l1 = LocalDateTime.of(2010, 10, 3, 0, 0, 0).toEpochSecond(ZoneOffset.UTC);
// System.out.println(l1);
//
long time = l - starttime;
time=time<<32; //时间戳向左移32=位,空出位置
String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
Long increment = stringRedisTemplate.opsForValue().increment("ice" + pre + format);// 拼接的字符串
long l1 = time | increment;
return l1;
}
}
package com.hmdp.service.impl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.SeckillVoucherMapper;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisOneID;
import com.hmdp.utils.UserHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
*
* 秒杀优惠券表,与优惠券是一对一关系 服务实现类
*
*
* @author 虎哥
* @since 2022-01-04
*/
@Service
public class SeckillVoucherServiceImpl extends ServiceImpl<SeckillVoucherMapper, SeckillVoucher> implements ISeckillVoucherService {
@Resource
private VoucherOrderMapper voucherOrderMapper;
@Resource
private RedisOneID redisOneID;
@Override
public Result getseckill(Long voucherId) {
// 查询优惠券
SeckillVoucher seckillVoucher = query().eq("voucher_id", voucherId).one();
// 判断时间是否开始
boolean after = seckillVoucher.getBeginTime().isAfter(LocalDateTime.now());
if (after) {
return Result.fail("抢购时间未开始");
}
// 查询库存是否够
if (seckillVoucher.getStock() < 1) {
return Result.fail("库存不足");
}
// 更新库存数据 ,判断向前查到的库存与是否发生改变
boolean voucher_id = update().setSql("stock=stock-1").eq("voucher_id", voucherId).eq("stock",seckillVoucher.getStock())
.update();
if (!voucher_id) {
return Result.fail("库存扣减失败");
}
// 插入订单数据
VoucherOrder voucherOrder = new VoucherOrder();
long order = redisOneID.getLongId("order");
voucherOrder.setId(order);
voucherOrder.setVoucherId(voucherId);
voucherOrder.setUserId(UserHolder.getUser().getId());
// 插入订单
voucherOrderMapper.insert(voucherOrder);
return Result.ok(order);
}
}
// intern可以获取string常量池中的对象,确保synchronized中锁定的同一个用户
synchronized (UserHolder.getUser().getId().toString().intern()) {
ISeckillVoucherService o = (ISeckillVoucherService)AopContext.currentProxy();// @Transactional 对象的原理其实是通过aop代理实现的,
// 所以在调用方法时需要获取带当前对象的代理对象
return o.createorder(voucherId,seckillVoucher);
}
~~~java
@Service
public class SeckillVoucherServiceImpl extends ServiceImpl implements ISeckillVoucherService {
@Resource
private VoucherOrderMapper voucherOrderMapper;
@Resource
private RedisOneID redisOneID;
@Override
@Transactional
public Result getseckill(Long voucherId) {
// 查询优惠券
SeckillVoucher seckillVoucher = query().eq("voucher_id", voucherId).one();
// 判断时间是否开始
boolean after = seckillVoucher.getBeginTime().isAfter(LocalDateTime.now());
if (after) {
return Result.fail("抢购时间未开始");
}
// 查询库存是否够
if (seckillVoucher.getStock() < 1) {
return Result.fail("库存不足");
}
// intern可以获取string常量池中的对象,确保synchronized中锁定的同一个用户
synchronized (UserHolder.getUser().getId().toString().intern()) {
ISeckillVoucherService o = (ISeckillVoucherService)AopContext.currentProxy();// @Transactional 对象的原理其实是通过aop代理实现的,
// 所以在调用方法时需要获取带当前对象的代理对象
return o.createorder(voucherId,seckillVoucher);
}
}
@Transactional
// 给该方法添加事务,事务的提交时机是在方法结束之后才提交,
// 所以需要将synchronized控制块添加当方法的外围,确保在锁中的代码事务提交之后在释放锁
public Result createorder(Long voucherId,SeckillVoucher seckillVoucher){
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(VoucherOrder::getUserId, UserHolder.getUser().getId()).eq(VoucherOrder::getVoucherId, voucherId);
List voucherOrders = voucherOrderMapper.selectList(queryWrapper);
if (voucherOrders == null) {
return Result.fail("请勿重复抢购");
}
// 更新库存数据 ,判断向前查到的库存与是否发生改变
boolean voucher_id = update().setSql("stock=stock-1").eq("voucher_id", voucherId).eq("stock", seckillVoucher.getStock())
.update();
if (!voucher_id) {
return Result.fail("库存扣减失败");
}
// 插入订单数据
VoucherOrder voucherOrder = new VoucherOrder();
long order = redisOneID.getLongId("order");
voucherOrder.setId(order);
voucherOrder.setVoucherId(voucherId);
voucherOrder.setUserId(UserHolder.getUser().getId());
// voucherOrder.setUserId(1011L);
// 插入订单
voucherOrderMapper.insert(voucherOrder);
return Result.ok(order);
}
}
public class SimpleRedisLock implements Ilock {
private String name; // 给哪个对象加锁,的名字
private StringRedisTemplate redisTemplate;
private final String KEY_PRE = "lock:";
private final String ID_PRE= UUID.randomUUID().toString().replace("-","")+"-";
public SimpleRedisLock(String name, StringRedisTemplate redisTemplate) {
this.name = name;
this.redisTemplate = redisTemplate;
}
/*
*
* @Pgrm 尝试获取锁
* */
@Override
public boolean trylocak(Long timeoustSecond) {
long id = Thread.currentThread().getId(); // 获取当前线程的id值,充当sentnx的vlaue
String ids=ID_PRE+id;
Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(KEY_PRE + name, ids + "", timeoustSecond, TimeUnit.SECONDS);
// 在进行自动拆箱的过程中,可能会返回null对象,导致发生异常
return Boolean.TRUE.equals(aBoolean);
}
@Override
public void unlock() {
// 判断当前的id值,是否与缓存内存取的id值相等
long id = Thread.currentThread().getId();
String ids= ID_PRE+id;
String s = redisTemplate.opsForValue().get(KEY_PRE + name);
if (ids.equals(s)){
redisTemplate.delete(KEY_PRE + name);
}
}
}
if (ids.equals(s)){
redisTemplate.delete(KEY_PRE + name);
}
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>3.16.8version>
dependency>