- 浏览: 60571 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (117)
- RPC相关 (4)
- mvc_controller (3)
- mvc_model (3)
- maven (4)
- mvc_view (5)
- IO (2)
- 业务相关 (2)
- MQ (7)
- 搜索引擎 (3)
- zookeeper (2)
- 工具相关 (4)
- 编辑错误 (1)
- tomcat (1)
- 单元测试 (1)
- 负载均衡 (1)
- ubuntu (1)
- nginx (1)
- dubbo (2)
- 网络站点分发 (1)
- 电商-支付相关 (10)
- 电商订单业务相关 (3)
- Core java1 (3)
- Core Java (12)
- 多线程高并发(并发包/线程/锁) (10)
- 数据库+缓存 (17)
- springcloud (2)
- jvm (5)
- 日志相关 (1)
- 算法 (3)
- spring (2)
- 分布式一致性算法 (1)
最新评论
先理了解本地锁Lcok:
http://572327713.iteye.com/blog/2407789
1.基于数据库实现分布式锁
性能较差,容易出现单点故障
锁没有失效时间,容易死锁
非阻塞式的
不可重入
2.基于缓存实现分布式锁
性能好
锁失效时间难设置,容易死锁
非阻塞式的(使用线程等待解决)
不可重入
3.基于zookeeper实现分布式锁
实现相对简单
可靠性高
性能较好
可重入
数据库:
性能较差,容易出现单点故障:
mysql并发性能瓶颈300-700,一个线程访问时插入一条数据,处理后删除此数据
很容易出现单点故障,连接有瓶劲性能差
锁没有失效时间,容易死锁:
一个线程突然宕机,因为数据没有删除,其他线程一直等待--死锁
非阻塞式的:
拿锁失败后立刻返回结果,线程做其他事情
不可重入:
线程加锁,其他线程不能加锁了
redis:
性能好:
轻轻松松响应并发10万
锁失效时间难设置,容易死锁:
非阻塞式的(使用线程等待解决):
不可重入:
线程加锁,其他线程不能加锁了
加解锁正确的姿势:(来自redis作者antirez的总结归纳)
1.加锁:
1.1生成唯一的随机值(uuid,时间搓),向1.2 redis写入随机值完成加锁并1.3设置失效时间。
必须使用sentnx? SET if Not exists(如果不存在,则SET)
SET resource_name my_random_value NX PX 30000
2.解锁:
2.1根据生成随机值,2.2进行比对(线程与redis里如果一致),2.3删除redis上的数据:
执行如下lua脚本
if redis.call("get".KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1]);
else
return 0;
end
注意!:
1.分布式锁必须要设置一个过期时间(好比unlock一定放在finally一个道理)
2.设置一个随机字符串my_random_value是很有必要的
3.加锁和设置失效时间必须是原子操作
4.释放锁保证三步原子性(get,比较del值,del)可用基于lua脚本实现
原因:a线程解锁比较redis值相等时恰好到了过期时间,此时redis写入了b线程的值,a线程会继续删除redis,导致b线程失效,所以必须保证释放锁三步原子性,使用lua脚本,不会受到b线程任何影响
图中2:a线程特意暂停3秒,但是已经超了超时时间。
图中3:a线程过了超时时间,redis清空,b线程加入了锁。
图中4:当a线程解锁时,发现此redis是b线程的时间戳,默默的离去,这就是设置随机时间搓的原因。
图中第二步: 比较redis值与本地值是否一致,删除redis进行解锁
此时本地值用的是threadlocal修饰变量。
redis的lock具体实现:
pom.xml
RedisLock.java
unlock.lua
基于redis分布式锁方案问题:
1.锁失效时间难把握,一般为单线程处理时长两到三倍
超时时间不能长也不能短,为什么设置了100ms,一般为单线程处理线程的两到三倍。
2.可能出现锁失效情况
a线程如果超时,a线程还一直以为拿着锁,出现了共享资源竞争的问题。
3.此分布式锁不能在redis集群中使用,集群环境中可用redLock。
用于单节点redis,如果再集群下不太合适,可以使用redLock复杂很多。
所以建议使用zookeeper的分布式锁。
思考:有人会说redis单点可以有主从啊,但是主从会有问题:
比如服务1获取主机锁,主机宕机,这是还没来得及同步到从机,此时丛机变主机,这时服务2获取新主机锁,这样会有两个服务获取到锁。
https://blog.csdn.net/hh1sdfsf56456/article/details/79474434
zookeeper:
1.基于内存
2.实现简单
liux
持久节点create /temp(路径) temp(值)
临时节点 create -e /temp(路径) temp(值)
顺序节点 create -s
临时顺序 create -s -e
zk应用场景:
数据发布订阅(配置中心)
命名服务
Master选举
集群管理
分布式队列
分布式锁
羊群效应在分布式集群规模比较大的环境中危害是严重的:
1.巨大的服务器性能损耗:我去发事件,我要序列化的事件啊
客户端无端接受了很多与自己无关的通知事件。
2.网络冲击,每个节点网络发事件,网络带宽消耗很大
3.当羊群效应频繁的发生,整个节点都挂了,节点可能造成宕机
如果以后集群环境2个以上节点时。
集群节点有10个,只有1个会抢占锁
存在死锁的可能性。
生产的订单号服务宕机
临时顺序节点:
ps aux|grep java
zkServer.sh start
linux服务器中访问zk服务:
在zk服务查看znode节点:
如果线程抢占一个资源,也可以使用队列抢占解决。
http://572327713.iteye.com/blog/2407789
1.基于数据库实现分布式锁
性能较差,容易出现单点故障
锁没有失效时间,容易死锁
非阻塞式的
不可重入
2.基于缓存实现分布式锁
性能好
锁失效时间难设置,容易死锁
非阻塞式的(使用线程等待解决)
不可重入
3.基于zookeeper实现分布式锁
实现相对简单
可靠性高
性能较好
可重入
数据库:
性能较差,容易出现单点故障:
mysql并发性能瓶颈300-700,一个线程访问时插入一条数据,处理后删除此数据
很容易出现单点故障,连接有瓶劲性能差
锁没有失效时间,容易死锁:
一个线程突然宕机,因为数据没有删除,其他线程一直等待--死锁
非阻塞式的:
拿锁失败后立刻返回结果,线程做其他事情
不可重入:
线程加锁,其他线程不能加锁了
redis:
性能好:
轻轻松松响应并发10万
锁失效时间难设置,容易死锁:
非阻塞式的(使用线程等待解决):
不可重入:
线程加锁,其他线程不能加锁了
加解锁正确的姿势:(来自redis作者antirez的总结归纳)
1.加锁:
1.1生成唯一的随机值(uuid,时间搓),向1.2 redis写入随机值完成加锁并1.3设置失效时间。
必须使用sentnx? SET if Not exists(如果不存在,则SET)
SET resource_name my_random_value NX PX 30000
2.解锁:
2.1根据生成随机值,2.2进行比对(线程与redis里如果一致),2.3删除redis上的数据:
执行如下lua脚本
if redis.call("get".KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1]);
else
return 0;
end
注意!:
1.分布式锁必须要设置一个过期时间(好比unlock一定放在finally一个道理)
2.设置一个随机字符串my_random_value是很有必要的
3.加锁和设置失效时间必须是原子操作
4.释放锁保证三步原子性(get,比较del值,del)可用基于lua脚本实现
原因:a线程解锁比较redis值相等时恰好到了过期时间,此时redis写入了b线程的值,a线程会继续删除redis,导致b线程失效,所以必须保证释放锁三步原子性,使用lua脚本,不会受到b线程任何影响
图中2:a线程特意暂停3秒,但是已经超了超时时间。
图中3:a线程过了超时时间,redis清空,b线程加入了锁。
图中4:当a线程解锁时,发现此redis是b线程的时间戳,默默的离去,这就是设置随机时间搓的原因。
图中第二步: 比较redis值与本地值是否一致,删除redis进行解锁
此时本地值用的是threadlocal修饰变量。
redis的lock具体实现:
pom.xml
<!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
RedisLock.java
package com.hailong.yu.dongnaoxuexi.lock; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import redis.clients.jedis.Jedis; public class RedisLock implements Lock{ private static final String LOCK_KEY = "lock"; // 线程上下文,在这个线程执行过程中,保存的变量放这里,变量传递 private ThreadLocal<String> local = new ThreadLocal<String>(); /** * 阻塞锁(synchonied是阻塞的) */ public void lock() { if(tryLock()) { } else { //reids做阻塞不太灵活,用现成阻塞 try { Thread.sleep(200); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } lock(); } } /** * 非阻塞式锁 */ public boolean tryLock() { String uuid = UUID.randomUUID().toString(); Jedis redis = new Jedis("localhost"); // key // value // @param nxxx NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the key // if it already exist. 一定要没有值才设置成功/一定要有值才能设置成功 // expx EX|PX, expire time units: EX = seconds; PX = milliseconds // 100ms有效期 String ret = redis.set(LOCK_KEY, uuid, "NX", "PX", 100); if (ret !=null && ret.equals("OK")) { local.set(uuid); return true; } return false; } /** * 解锁 */ public void unlock() { // FileUtils.readFileByLines("E:/workspaces/.../unlock.lua"); String script = ""; // 执行脚本命令 Jedis redis = new Jedis("localhost"); List<String> keys = new ArrayList<String>(); keys.add(LOCK_KEY); List<String> locals = new ArrayList<String>(); locals.add(local.get()); redis.eval(script, keys, locals); } public void lockInterruptibly() throws InterruptedException { // TODO Auto-generated method stub } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { // TODO Auto-generated method stub return false; } public Condition newCondition() { // TODO Auto-generated method stub return null; } }
unlock.lua
if redis.call("get".KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]); else return 0; end
基于redis分布式锁方案问题:
1.锁失效时间难把握,一般为单线程处理时长两到三倍
超时时间不能长也不能短,为什么设置了100ms,一般为单线程处理线程的两到三倍。
2.可能出现锁失效情况
a线程如果超时,a线程还一直以为拿着锁,出现了共享资源竞争的问题。
3.此分布式锁不能在redis集群中使用,集群环境中可用redLock。
用于单节点redis,如果再集群下不太合适,可以使用redLock复杂很多。
所以建议使用zookeeper的分布式锁。
思考:有人会说redis单点可以有主从啊,但是主从会有问题:
比如服务1获取主机锁,主机宕机,这是还没来得及同步到从机,此时丛机变主机,这时服务2获取新主机锁,这样会有两个服务获取到锁。
https://blog.csdn.net/hh1sdfsf56456/article/details/79474434
zookeeper:
1.基于内存
2.实现简单
liux
持久节点create /temp(路径) temp(值)
临时节点 create -e /temp(路径) temp(值)
顺序节点 create -s
临时顺序 create -s -e
zk应用场景:
数据发布订阅(配置中心)
命名服务
Master选举
集群管理
分布式队列
分布式锁
羊群效应在分布式集群规模比较大的环境中危害是严重的:
1.巨大的服务器性能损耗:我去发事件,我要序列化的事件啊
客户端无端接受了很多与自己无关的通知事件。
2.网络冲击,每个节点网络发事件,网络带宽消耗很大
3.当羊群效应频繁的发生,整个节点都挂了,节点可能造成宕机
如果以后集群环境2个以上节点时。
集群节点有10个,只有1个会抢占锁
存在死锁的可能性。
生产的订单号服务宕机
临时顺序节点:
package com.baozun.util.locks; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import org.I0Itec.zkclient.IZkDataListener; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.serialize.SerializableSerializer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class ZookeeperLock implements Lock{ private static String LOCK_PATH = "/LOCK"; // @Value("${dubbo.registry.address}") // private static String ZK_IP_PORT; private static String ZK_IP_PORT = "123.206.*.*:2181"; private static final Log logger = LogFactory.getLog(ZookeeperLock.class); // private ZkClient client = new ZkClient(ZK_IP_PORT, 1000, 10, new SerializableSerializer()); private ZkClient client = new ZkClient(ZK_IP_PORT); private CountDownLatch cdl; // 之前节点 private String beforePath; // 当前请求的节点 private String currentPath; public ZookeeperLock() { if(!client.exists(LOCK_PATH)){ client.createPersistent(LOCK_PATH); } } /** * 非阻塞式锁 */ @Override public boolean tryLock() { // 如果currentPath为空则为第一次尝试加锁,第一次加锁赋值currentPath if(currentPath == null || currentPath.length() <=0) { // 创建临时顺序节点 currentPath = client.createEphemeralSequential(LOCK_PATH + '/', "lock"); } // 获取所有临时节点并排序,临时节点名称为自增长的字符串:0000000400 List<String> childrens = client.getChildren(LOCK_PATH); Collections.sort(childrens); // 如果当前节点在所有节点中排名第一则获取锁成功 if(currentPath.equals(LOCK_PATH + '/' + childrens.get(0))) { return true; // 如果当前节点在所有节点中排名中不是第一,则获取前面的节点名称,并赋值给beforePath } else { int wz = Collections.binarySearch(childrens, currentPath.substring(6)); beforePath = LOCK_PATH + '/' + childrens.get(wz-1); } return false; } @Override public void unlock() { // TODO Auto-generated method stub client.delete(currentPath); } /** * 阻塞式锁 */ @Override public void lock() { if(tryLock()) { waitForLock(); lock(); } else { logger.info(Thread.currentThread().getName()+" 获得分布式锁!"); } } /** * 等待锁 * @return */ public void waitForLock() { // 监听器 IZkDataListener iZkDataListener = new IZkDataListener() { @Override public void handleDataDeleted(String arg0) throws Exception { // 节点数据被删除 if(cdl!=null) { cdl.countDown(); } } @Override public void handleDataChange(String arg0, Object arg1) throws Exception { // TODO Auto-generated method stub } }; // 给排在前面的节点增加数据删除的wetcher client.subscribeDataChanges(beforePath, iZkDataListener); if(client.exists(beforePath)) { cdl = new CountDownLatch(1); try { cdl.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } client.unsubscribeDataChanges(beforePath, iZkDataListener); } // =========================暂不实现=========================== @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { // TODO Auto-generated method stub return false; } /** * 中断机制(可中断锁) */ @Override public void lockInterruptibly() throws InterruptedException { // TODO Auto-generated method stub } /** * 设置条件加锁或解锁 * 多个条件变量 */ @Override public Condition newCondition() { // TODO Auto-generated method stub return null; } }
import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; import com.baozun.util.locks.ZookeeperLock; /** * @author hailong.yu1 * @date 2018年1月25日 上午10:36:57 */ public class SecurityProcessorTest implements Runnable { public static final int NUM = 10; public static CountDownLatch countDownLatch = new CountDownLatch(NUM); public static OrderCodeGenerator orderCodeGenerator = new OrderCodeGenerator(); public ZookeeperLock lock = new ZookeeperLock(); @Override public void run() { try { countDownLatch.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } createOrder(); } /** * @param args */ public static void main(String[] args) { for(int i=0; i<NUM; i++) { new Thread(new SecurityProcessorTest()).start(); countDownLatch.countDown(); } } public void createOrder() { String orderNum = null; try { lock.lock(); orderNum = orderCodeGenerator.getOrderCode(); } catch (Exception e) { // TODO: handle exception } finally { lock.unlock(); } System.out.println(Thread.currentThread().getName()+"===="+orderNum); } }
ps aux|grep java
zkServer.sh start
linux服务器中访问zk服务:
在zk服务查看znode节点:
如果线程抢占一个资源,也可以使用队列抢占解决。
发表评论
-
Synchronized实现原理和底层优化
2019-04-24 11:04 318Java并发编程:Synchronized及其实现原理 htt ... -
分布式锁Redlock
2019-03-08 10:01 403好文章:https://blog.csdn.n ... -
volatile指令重排序理解
2019-02-01 21:38 434volatile优点:可见性,防止指令重排序,那如何理解指令重 ... -
CyclicBarrier和CountDownLatch区别
2018-12-06 20:20 389这个链接图片总结到位 https://blog.csdn.ne ... -
线程池简单实现
2018-08-01 17:51 344以下实现注意,并没有用到最大线程数参数max import j ... -
线程相关好文章
2018-06-27 15:41 377notify和notifyAll有什么区别: https:// ... -
线程本地锁
2018-01-15 16:01 461static关键字的四种用法 https://www.cnbl ... -
多线程并发Future模式应用以及jdk源码分析
2018-01-07 17:03 456多线程三种方式: http://blog.csdn.net/a ... -
volitile与原子类atomic区别
2018-01-02 11:41 506volatile的内存模型: http://blog.csdn ...
相关推荐
`SETNX`命令在键不存在时设置键值,但如果存在则返回失败,这可以用来简单地实现锁。然而,单实例的Redis分布式锁存在一定的风险,如主节点故障可能导致锁无法释放。 RedLock算法是Redis创始人Antirez提出的一种...
计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习参考资料计算机技术、IT咨询、人工智能AI理论介绍,学习...
下面将详细介绍基于Redis、Zookeeper以及etcd的分布式锁实现方式。 #### 二、基于Redis的分布式锁实现 Redis作为一款高性能的键值存储系统,非常适合用来实现分布式锁。以下是一个简单的示例代码: - **获取锁**...
5. 如何结合Netty、Redis和ZooKeeper来设计和实现一个高并发的微服务架构。 6. 性能调优,包括Netty的线程模型调整、Redis的内存管理和ZooKeeper的配置优化。 7. 实战项目中可能遇到的问题及解决策略,例如网络抖动...
在单机环境中,我们可以通过Java等编程语言内置的并发控制手段,如synchronized关键字或ReentrantLock等实现锁。然而,在分布式环境中,由于多台服务器可能同时访问同一资源,这时就需要借助于分布式锁来确保数据的...
Redis是一个内存数据结构存储系统,其速度非常快,适合实现锁。使用Redis实现分布式锁通常有两种方法:`SETNX`命令和`lua`脚本。`SETNX`命令在键不存在时设置键值,实现互斥锁。为了防止锁不能被释放(例如,客户端...
在分布式锁的实现中,Zookeeper可以借助其强一致性、顺序一致性和事件通知机制,通过创建临时节点实现锁的获取与释放。当一个客户端创建了一个临时节点,其他客户端则会监视这个节点,当节点消失(即锁被释放),...
一旦键过期,Redis会自动删除该键值对,从而实现锁的自动释放,避免死锁。 3. **`DEL` 命令**:用于删除Redis中的键,释放锁。 ##### 实现步骤 1. **获取锁**: - 使用`SETNX`命令尝试获取锁,并使用`EXPIRE`命令...
### Windows 下分布式部署 Mysql、Redis、Zookeeper、Nginx、FastDFS 集合 #### MySQL 安装与配置 在 Windows 环境下安装 MySQL 的过程较为直观,但需要注意细节。 1. **下载 MySQL 安装程序:** - 从 MySQL ...
2. 基于Redis实现:Redis提供了丰富的数据结构和操作命令,如`SETNX`(设置并返回值为新)用于原子性地设置键,配合`EXPIRE`设置过期时间,实现锁的自动释放。Redisson是基于Redis的Java客户端,提供了一套完整的...
如何操作Redis和zookeeper实现分布式锁 在分布式场景下,有很多种情况都需要实现最终一致性。在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和...
本项目“springboot redis zookeeperlock rabbit实现的分布式锁”结合了Spring Boot、Redis、Zookeeper以及RabbitMQ这四款强大的工具,旨在构建一个健壮的分布式锁系统。以下是关于这些技术及其在分布式锁中的应用的...
分布式锁是一种在分布式系统中实现锁机制的技术,用于在多节点之间协调访问共享资源,确保在高并发环境下数据的一致性和完整性。本压缩包“zk:redis分布式锁.zip”提供了基于Zookeeper(zk)和Redis两种分布式锁实现...
分布式锁的实现机制和 Redis、ZooKeeper 的差异 分布式锁是指在分布式系统中,多个进程或线程之间为了访问共享资源而需要获取的锁。实现分布式锁需要考虑三个阶段:1. 进程请求获取锁;2. 获取锁的进程持有锁并执行...
* 需要客户端实现:ZooKeeper分布式锁需要客户端实现锁机制,增加了客户端的复杂性。 其他分布式锁实现 除了ZooKeeper分布式锁外,还有其他分布式锁实现方案,如Redis分布式锁、MySQL分布式锁等。这些分布式锁实现...
Java基于SOA架构的分布式电商购物商城 前后端分离 前台商城:Vue全家桶 后台管理系统:Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper等。 Java基于SOA架构的分布式电商购物商城 前后端分离 前台商城...
redis和zk两种不同方式实现分布式锁,互联网开发小伙伴必备技能!
3. 使用Curator的InterProcessMutex实现锁: ```java InterProcessMutex lock = new InterProcessMutex(client, "/path/to/lock"); try (LockHandle handle = lock.acquire()) { // 执行关键操作 } catch ...
分布式锁的实现通常需要依赖一个可靠的外部协调系统,这样的系统可以是数据库、ZooKeeper、Redis等。 Redis作为一个开源的高性能键值存储系统,被广泛应用于实现分布式锁。Redis提供的命令操作简单且执行速度快,...
分布式锁有三种实现方式:数据库乐观锁、基于Redis的分布式锁和基于ZooKeeper的分布式锁。本篇博客将详细介绍第二种方式,基于Redis实现分布式锁。 可靠性是分布式锁的重要特性。为了确保分布式锁的可靠性,至少...