首页 > 其他 > 详细

redisson

时间:2020-03-18 00:19:37      阅读:109      评论:0      收藏:0      [点我收藏+]

  在自己实现redis分布式锁的时候,我问了,为什么有些公司采用redisson去实现锁?!

  其实理由有很多,个人认为最关键的还是锁的控制。

  “拿来主义”,别人已经做好的,拿来用就行了,当然,你要懂原理就更好!

  自己实现的锁有很多问题,比如说,锁的过期时间,在高并发情况下,不能保证每次任务进来的时间长短,势必回导致锁的过期时间设置问题。

  redisson不光有watch dog 而且还把锁信息放在hash中,hash中的key存储了当前线程id!

  下面是模拟并发情况下的,抢占锁资源情况:

  

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.10.6</version>
</dependency>

注意自己加上maven包。
application.yml:



server:
port: 8888
spring:
redis:
port: 6379
host: 192.168.100.108
password: 123456
database: 0
timeout: 2000
redisson:
config: classpath:redisson-single.yml


redisson-single.yml:

# 单节点设置
singleServerConfig:
address: redis://192.168.6.166:6379
database: 0
password: 123456
idleConnectionTimeout: 10000
pingTimeout: 1000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
reconnectionTimeout: 3000
failedAttempts: 3
clientName: null
# 发布和订阅连接的最小空闲连接数 默认1
subscriptionConnectionMinimumIdleSize: 1
# 发布和订阅连接池大小 默认50
subscriptionConnectionPoolSize: 10
# 单个连接最大订阅数量 默认5
subscriptionsPerConnection: 5
# 最小空闲连接数 默认32,现在暂时不需要那么多的线程
connectionMinimumIdleSize: 4
# connectionPoolSize 默认64,现在暂时不需要那么多的线程
connectionPoolSize: 20
# 这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。
threads: 0
# 这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,以及底层客户端所一同共享的线程池里保存的线程数量。
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}
transportMode: NIO


package com.example.demo.redislock;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class DistributedLock {

@Autowired
private StringRedisTemplate redisTemplate;

@Autowired
private RedissonClient redissonClient;

public RLock getLock(String lockId) {
RLock rlock = null;
try{
rlock = redissonClient.getLock(lockId);
rlock.lock(5000, TimeUnit.MILLISECONDS);
//System.out.println("获取锁成功");
}catch (Exception e) {
//System.out.println("获取锁失败!");
unLock(rlock);
}
return rlock;
}

public void unLock(RLock rlock) {
if(rlock != null) {
rlock.unlock();
}
}

public String getValueByKey(String key) {
String value = redisTemplate.opsForValue().get(key);
return value;
}

public void setValueByKey(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}


}

package com.example.demo.controller;

import com.example.demo.redislock.DistributedLock;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/lock")
public class LockController {

private final static String LOCK_ID = "product:";

@Autowired
private DistributedLock distributedLock;

@GetMapping("/{id}")
public void lock(@PathVariable String id) {
//模拟多个资源请求输出库存情况
//goods_num
RLock lock = distributedLock.getLock(LOCK_ID);
if(lock != null) {
System.out.println("-------------线程"+id+"抢到锁------------");
//获取成功
Integer num = Integer
.parseInt(distributedLock.getValueByKey("goods_num"));
if(num > 0) {
distributedLock.setValueByKey("goods_num", Integer.toString(num - 1));
System.out.println("商品数剩余----->" + (num - 1));
} else {
System.out.println("商品销售完----结束了!");
}

distributedLock.unLock(lock);
} else {
//获取失败
System.out.println("没抢到锁");
}
System.out.println("-------------线程"+id+"结束------------");
}
}

以上是部分实现


然后创建多线程模拟并发:
package com.jason.data;


import java.util.concurrent.CountDownLatch;

public class RunnableDemo implements Runnable {

CountDownLatch latch;

int i;
public RunnableDemo(CountDownLatch latch,int i) {
this.latch = latch;
this.i = i;
}

@Override
public void run() {
//http访问get请求
long starTime = 0;
try {
// starTime = System.currentTimeMillis();
System.out.println("开始,第"+i+"个");
latch.await();
// System.out.println("请求开始了");
//JSONObject jsonObject = httpPost("http://192.168.100.43:8080/lua/requestIdfa", "userId=3&bundleName=xxxxxx&country=US&thirdParty=sdfsfs");
// System.out.println("返回的状态码为:"+jsonObject.get("code"));
// System.out.println("返回的状态信息:"+jsonObject.get("desc"));
// System.out.println("返回的内容为:"+jsonObject.get("content"));
HttpClient.doGet("http://localhost:8888/lock/"+i);

} catch (InterruptedException e) {
e.printStackTrace();
}


}
}



package com.jason.data;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class LockDemo {
public static void main(String[] args) {
//Lock
//多线程
ThreadPoolExecutor executor = new ThreadPoolExecutor(2000, 5000,
2, TimeUnit.MINUTES,
new ArrayBlockingQueue<Runnable>(5000));
//模拟2000人并发请求
CountDownLatch latch = new CountDownLatch(1);
for (int i = 0; i < 2000; i++) {
RunnableDemo demo = new RunnableDemo(latch,i);
executor.execute(demo);
}
//计数器減一 所有线程释放 并发访问。
latch.countDown();

}
}


之前通过命令设置 set goods_num 200
库存有200件,我们分别启动,然后截图看效果。
技术分享图片

 

 


技术分享图片

 

 


可以看到,并发情况下,到底谁抢到了锁并不确定,但是利用redis单线程原因,可以保证库存到最后都是正确到,事务性良好。其实秒杀功能也是这个原理,
不过比这复杂,当并发特别巨大当时候,需要做负载均衡,限流等一系列操作!
大家想想,redisson分布式锁有什么缺陷?!

后面有空给大家介绍,rocketmq限流等实现!

redisson

原文:https://www.cnblogs.com/lzphu/p/12514627.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!