测试redis分布式锁
作者:互联网
1、基础知识:
通过HttpServletRequest获取对方请求的IP地址
request.getRemoteAddr()直接请求到服务器,获取对方的IP地址
request.getHeader("")通过nginx负载均衡访问的服务器,获取对方的IP地址。
@RequestMapping("{skuId}.html")
public String item(@PathVariable String skuId, ModelMap modelMap, HttpServletRequest request){
String IP = request.getRemoteAddr(); //直接请求到服务器,获取对方的IP地址
//request.getHeader(""); 通过nginx负载均衡访问的服务器,获取对方的IP地址
2、设置分布式锁防止redis缓存击穿
(1)第1位同学先请求页面,就有第1条线程先进来。先查询缓存但是没查到,就去申请设置锁,并且拿到了锁,有权在10秒的过期时间内访问数据库。
为了看到设置锁的效果,这里设置第1位同学的线程等待5秒再去查询数据库和把结果存入redis,再归还删除锁
(2)第2名同学紧接着也请求页面,查询缓存但没查到。就申请设置锁。因为第1名同学还在等待5秒钟,还没有归还删除锁。
所以第2名同学就拿不到锁,也线程等待3秒,本方法自旋1次(第2名同学照样拿不到锁),线程继续等待3秒,本方法自旋第2次(正常从缓存查到数据)
(3)5秒过去了,第1名同学的线程开始执行,查询数据库和把结果存入redis,再归还删除锁。
(4)这样第2名同学的线程第2次自旋本方法,查询缓存,查到了,成功访问到前端页面!!!
3、实现代码如下(好好理解里面的注释):
基础知识:
Thread.sleep(1000*5) //线程睡5秒
Thread.currentThread().getName() //获取线程的名称
@Override
//用户购买商品页面:查看sku商品,sku图片集合
public PmsSkuInfo getSkuById(String skuId , String IP) {
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"正准备进入商品详情页面");
PmsSkuInfo pmsSkuInfo = new PmsSkuInfo();
//连接缓存
Jedis jedis = redisUtil.getJedis();
//查询缓存
String skuKey = "sku:"+skuId+":info";
String skuJson = jedis.get(skuKey); //根据skuId到redis缓存查询得到一个json的字符串
if(StringUtils.isNotBlank(skuJson)) { //如果不等于空,即是可以在缓存查到
pmsSkuInfo = JSON.parseObject(skuJson,PmsSkuInfo.class); //把查询到的json字符串 转为 java对象
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"成功从缓存获取商品详情页面QWQ");
}else {
//如果缓存没有,就查询mysql数据库
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"发现缓存没有,申请获取锁");
//设置分布式锁,防止缓存击穿。让用户有秩序的拿锁访问数据库,而不是一窝蜂进到数据库
String ok = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 10*1000);
if(StringUtils.isNotBlank(ok)&&ok.equals("OK")){ //设置锁返回ok成功,允许这位用户访问数据库,10秒后过期时间,再允许下一位访问
//注意返回来的字符串OK是大写!
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"拿到了锁"+"sku:" + skuId + ":lock"+",有权在10秒过期时间内访问数据库");
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"让该线程等待5秒。。。");
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
pmsSkuInfo = getSkuByIdFromDb(skuId); //建立redis缓存服务器,就是为了保护这行代码:查询数据库的操作
if(pmsSkuInfo!=null) {
//mysql查询结果存入redis
jedis.set("sku:"+skuId+":info", JSON.toJSONString(pmsSkuInfo)); //把java对象 转为 json字符串
}else { //mysql查询结果为null,防止缓存穿透!!!!
//把这个不存在的skuId设为:空值json字符串null,并存到redis服务器中。
jedis.setex("sku:"+skuId+":info",60*3, JSON.toJSONString(""));
//seconds是秒。3分钟内再有请求这skuId,就直接返回一个空的对象给前端(没有任何信息)。不再执行查询数据库操作
}
jedis.del("sku:" + skuId + ":lock"); //访问数据库结束后,删除锁。下一个用户继续拿锁访问!
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"访问数据库完毕,归还并删除锁:"+"sku:" + skuId + ":lock");
}else { //设置锁不成功,自旋(设置该线程睡眠几秒,再重新尝试访问本方法)
System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"没拿到锁,开始自旋!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getSkuById(skuId, IP); //这里要return返回这个方法重新访问,同一条线程!!
//不加return,则创建一条新的线程,原来的线程还是访问不到,即原来的线程成了一条孤儿线程!
}
}
jedis.close(); //关闭缓存连接
return pmsSkuInfo;
}
标签:同学,skuId,缓存,Thread,IP,redis,线程,测试,分布式 来源: https://blog.csdn.net/Hiber12/article/details/111156457