彩票炸金花博彩平台评测_从Curator好意思满漫衍式锁的源码再到羊群效应
彩票炸金花博彩平台评测_ 一、弁言银河乐章娱乐
Curator是一款由Java编写的,操作Zookeeper的客户端器具,在其里面封装了漫衍式锁、选举等高档功能。
今上帝若是分析其好意思满漫衍式锁的主要旨趣,关系漫衍式锁的一些先容许其他好意思满,有好奇赞佩的同学不错翻阅以下著述:
我用了上万字,走了一遍Redis好意思满漫衍式锁的崎岖之路,从单机到主从再到多实例,本来会发生这样多的问题_阳阳的博客-CSDN博客
Redisson可重入与锁续期源码分析_阳阳的博客-CSDN博客
在使用Curator获取漫衍式锁时,Curator会在指定的path下创建一个有序的临时节点,如果该节点是最小的,则代表获取锁见效。
皇冠信用盘网址接下来,在准备责任中,咱们不错不雅察是否会创建出一个临时节点出来。
二、准备责任率先咱们需要搭建一个zookeeper集群,天然你使用单机也行。
在这篇著述口试官:能给我画个Zookeeper选举的图吗?,先容了一种使用docker-compose形式快速搭建zk集群的形式。
在pom中引入依赖:
<dependency> 澳门巴黎人龙虎斗 <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency>皇冠博彩
Curator客户端的设立项:
收益/** * @author qcy * @create 2022/01/01 22:59:34 */ @Configuration public class CuratorFrameworkConfig { //zk各节点地址 private static final String CONNECT_STRING = "localhost:2181,localhost:2182,localhost:2183"; //贯串超每每期(单元:毫秒) private static final int CONNECTION_TIME_OUT_MS = 10 * 1000; //会话超每每期(单元:毫秒) private static final int SESSION_TIME_OUT_MS = 30 * 1000; //重试的驱动恭候时间(单元:毫秒) private static final int BASE_SLEEP_TIME_MS = 2 * 1000; //最大重试次数 private static final int MAX_RETRIES = 3; @Bean public CuratorFramework getCuratorFramework() { CuratorFramework curatorFramework = CuratorFrameworkFactory.builder() .connectString(CONNECT_STRING) .connectionTimeoutMs(CONNECTION_TIME_OUT_MS) .sessionTimeoutMs(SESSION_TIME_OUT_MS) .retryPolicy(new ExponentialBackoffRetry(BASE_SLEEP_TIME_MS, MAX_RETRIES)) .build(); curatorFramework.start(); return curatorFramework; } }
SESSION_TIME_OUT_MS参数则会保证,在某个客户端获取到锁之后片刻宕机,zk能在该时间内删除现时客户端创建的临时有序节点。
测试代码如下:
//临时节点旅途,qcy是博主名字缩写哈 private static final String LOCK_PATH = "/lockqcy"; @Resource CuratorFramework curatorFramework; public void testCurator() throws Exception { InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, LOCK_PATH); interProcessMutex.acquire(); try { //模拟业务耗时 Thread.sleep(30 * 1000); } catch (Exception e) { e.printStackTrace(); } finally { interProcessMutex.release(); } }
当使用接口调用该状貌时,在Thread.sleep处打上断点,参预到zk容器中不雅察创建出来的节点。
使用 docker exec -it zk容器名 /bin/bash 以交互模式参预容器,接着使用 ./bin/zkCli.sh 贯串到zk的server端。
然后使用 ls path 稽查节点
这三个节点齐是抓久节点,不错使用 get path 稽查节点的数据结构信息
皇冠官方手机版app最新版若一个节点的ephemeralOwner值为0,即该节点的临时领有者的会话id为0,则代表该节点为抓久节点。
当走到断点Thread.sleep时,如实发面前lockqcy下创建出来一个临时节点
博彩平台评测到这里吗,准备责任还是作念罢了,接下来分析interProcessMutex.acquire与release的过程
皇冠客服飞机:@seo3687 三、源码分析Curator相沿多种类型的锁,举例
InterProcessMutex,可重入锁排它锁 InterProcessReadWriteLock,读写锁 InterProcessSemaphoreMutex,不能重入排它锁今上帝若是分析InterProcessMutex的加解锁过程,先看加锁过程
加锁public void acquire() throws Exception { if (!internalLock(-1, null)) { throw new IOException("Lost connection while trying to acquire lock: " + basePath); } }
这里是遏止式获取锁,获取不到锁,就一直进行遏止。是以对于internalLock状貌,超每每期树立为-1,时间单元树立成null。
private boolean internalLock(long time, TimeUnit unit) throws Exception { Thread currentThread = Thread.currentThread(); //通过能否在map中取到该线程的LockData信息,来判断该线程是否还是抓有锁 LockData lockData = threadData.get(currentThread); if (lockData != null) { //进行可重入,平直复返加锁见效 lockData.lockCount.incrementAndGet(); return true; } //进行加锁 String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); if (lockPath != null) { //加锁见效,保存到map中 LockData newLockData = new LockData(currentThread, lockPath); threadData.put(currentThread, newLockData); return true; } return false; }
其中threadData是一个map,key线程对象,value为该线程绑定的锁数据。
LockData中保存了加锁线程owningThread,重入计数lockCount与加锁旅途lockPath,举例
/lockqcy/_c_c46513c3-ace0-405f-aa1e-a531ce28fb47-lock-0000000005
private final ConcurrentMap<Thread, LockData> threadData = Maps.newConcurrentMap(); private static class LockData { final Thread owningThread; final String lockPath; final AtomicInteger lockCount = new AtomicInteger(1); private LockData(Thread owningThread, String lockPath) { this.owningThread = owningThread; this.lockPath = lockPath; } }
参预到internals.attemptLock状貌中
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception { //脱手时间 final long startMillis = System.currentTimeMillis(); //将超每每期和解行化为毫秒单元 final Long millisToWait = (unit != null) ? unit.toMillis(time) : null; //节点数据,这里为null final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes; //重试次数 int retryCount = 0; //锁旅途 String ourPath = null; //是否获取到锁 boolean hasTheLock = false; //是否完成 boolean isDone = false; while (!isDone) { isDone = true; try { //创建一个临时有序节点,并复返节点旅途 //里面调用client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path); ourPath = driver.createsTheLock(client, path, localLockNodeBytes); //依据复返的节点旅途,判断是否抢到了锁 hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath); } catch (KeeperException.NoNodeException e) { //在会话逾期时,可能导致driver找不到临时有序节点,从而抛出NoNodeException //这里就进行重试 if (client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) { isDone = false; } else { throw e; } } } //获取到锁,则复返节点旅途,供调用方记载到map中 if (hasTheLock) { return ourPath; } return null; }
接下来,将会在internalLockLoop中诈欺刚才创建出来的临时有序节点,判断是否获取到了锁。
private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception { //是否获取到锁 boolean haveTheLock = false; boolean doDelete = false; try { if (revocable.get() != null) { //现时不会参预这里 client.getData().usingWatcher(revocableWatcher).forPath(ourPath); } //一直尝试获取锁 while ((client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock) { //复返basePath(这里是lockqcy)下扫数的临时有序节点,何况按照后缀从小到大成列 List<String> children = getSortedChildren(); //取出现时哨程创建出来的临时有序节点的称号,这里便是/_c_c46513c3-ace0-405f-aa1e-a531ce28fb47-lock-0000000005 String sequenceNodeName = ourPath.substring(basePath.length() + 1); //判断现时节点是否处于排序后的首位,如果处于首位,则代表获取到了锁 PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases); if (predicateResults.getsTheLock()) { //获取到锁之后,则断绝轮回 haveTheLock = true; } else { //这里代表莫得获取到锁 //获取比现时节点索引小的前一个节点 String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch(); synchronized (this) { try { //如果前一个节点不存在,则平直抛出NoNodeException,catch中不进行处理,不才一轮中赓续获取锁 //如果前一个节点存在,则给它树立一个监听器,监听它的开释事件 client.getData().usingWatcher(watcher).forPath(previousSequencePath); if (millisToWait != null) { millisToWait -= (System.currentTimeMillis() - startMillis); startMillis = System.currentTimeMillis(); //判断是否超时 if (millisToWait <= 0) { //获取锁超时,删除刚才创建的临时有序节点 doDelete = true; break; } //没超时的话,在millisToWait内进行恭候 wait(millisToWait); } else { //无尽期遏止恭候,监听到前一个节点被删除时,才会触发叫醒操作 wait(); } } catch (KeeperException.NoNodeException e) { //如果前一个节点不存在,则平直抛出NoNodeException,catch中不进行处理,不才一轮中赓续获取锁 } } } } } catch (Exception e) { ThreadUtils.checkInterrupted(e); doDelete = true; throw e; } finally { if (doDelete) { //删除刚才创建出来的临时有序节点 deleteOurPath(ourPath); } } return haveTheLock; }
判断是否获取到锁的中枢逻辑位于getsTheLock中
public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception { //获取现时节点在扫数子节点排序后的索引位置 int ourIndex = children.indexOf(sequenceNodeName); //判断现时节点是否处于子节点中 validateOurIndex(sequenceNodeName, ourIndex); //InterProcessMutex的构造状貌,会将maxLeases驱动化为1 //ourIndex必须为0,智商使得getsTheLock为true,也便是说,现时节点必须是basePath下的最末节点,智商代表获取到了锁 boolean getsTheLock = ourIndex < maxLeases; //如果获取不到锁,则复返上一个节点的称号,用作对其树立监听 String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases); return new PredicateResults(pathToWatch, getsTheLock); } static void validateOurIndex(String sequenceNodeName, int ourIndex) throws KeeperException { if (ourIndex < 0) { //可能会由于贯串丢失导致临时节点被删除,因此这里属于保障步履 throw new KeeperException.NoNodeException("Sequential path not found: " + sequenceNodeName); } }
那什么时候,在internalLockLoop处于wait的线程能被叫醒呢?
在internalLockLoop状貌中,还是使用
client.getData().usingWatcher(watcher).forPath(previousSequencePath);
给前一个节点树立了监听器,当该节点被删除时,将会触发watcher中的回调
private final Watcher watcher = new Watcher() { //回调状貌 @Override public void process(WatchedEvent event) { notifyFromWatcher(); } }; private synchronized void notifyFromWatcher() { //叫醒是以在LockInternals实例上恭候的线程 notifyAll(); }
到这里,基本上还是分析完加锁的过程了,在这里回归下:
率先创建一个临时有序节点
如果该节点是basePath下最末节点,则代表获取到了锁,存入map中,下次平直进行重入。
如果该节点不是最末节点,则对前一个节点树立监听,接着进行wait恭候。现时一个节点被删除时,将会奉告notify该线程。
解锁解锁的逻辑,就比拟简便了,平直参预release状貌中
public void release() throws Exception { Thread currentThread = Thread.currentThread(); LockData lockData = threadData.get(currentThread); if (lockData == null) { throw new IllegalMonitorStateException("You do not own the lock: " + basePath); } int newLockCount = lockData.lockCount.decrementAndGet(); //平直减少一次重入次数 if (newLockCount > 0) { return; } if (newLockCount < 0) { throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath); } //到这里代表重入次数为0 try { //开释锁 internals.releaseLock(lockData.lockPath); } finally { //从map中移除 threadData.remove(currentThread); } } void releaseLock(String lockPath) throws Exception { revocable.set(null); //里面使用guaranteed,会在后台不竭尝试删除节点 deleteOurPath(lockPath); }
重入次数大于0,就减少重入次数。当减为0时,欧博会员平台调用zk去删除节点,这极少和Redisson可重入锁开释时一致。
四、羊群效应在这里谈谈使用Zookeeper好意思满漫衍式锁场景中的羊群效应
什么是羊群效应率先,羊群是一种很狼籍的组织,漫无贪图,衰退不休,一般需要牧羊犬来匡助主东说念主扫尾羊群。
皇冠hg86a
某个时候,当其中一只羊发现前边有愈加好吃的草而动起来,就会导致其余的羊一哄而起,根底不论周围的情况。
是以羊群效应,指的是一个东说念主在进行感性的手脚后,导致其余东说念主平直苦守,产生非感性的从众手脚。
而Zookeeper中的羊群效应,则是指一个znode被改革后,触发了大批本不错被幸免的watch奉告,酿成集群资源的销耗。
获取不到锁时的恭候演化 sleep一段时间如果某个线程在获取锁失败后,总计不错sleep一段时间,再尝试获取锁。
但这样的形式,效果极低。
sleep时间短的话,会不时地进行轮询,销耗资源。
sleep时间长的话,会出现锁被开释但仍然获取不到锁的无语情况。
是以,这里的优化点,在于如何变主动轮询为异步奉告。
watch被锁住的节点扫数的客户端要获取锁时,只去创建一个同名的node。
当znode存在时,这些客户端对其树立监听。当znode被删除后,奉告扫数恭候锁的客户端,接着这些客户端再次尝试获取锁。
天然这里使用watch机制来异步奉告,关联词当客户端的数目荒谬多时,会存在性能低点。
当znode被删除后,在这刹那间,需要给大批的客户端发送奉告。在此时间,其余提交给zk的泛泛请求可能会被蔓延约略遏止。
顶尖工匠如何选拔?过硬本领如何练就?这些新技能、炫酷绝活又如何“扮靓”我们的高品质生活?一问到底↓
屏气凝神,张洪豪立定演示:开缆、理线、熔接、端接……约20秒后,两根纤芯严丝合缝地对接,熔接点恰处于保护设施的正中间。前不久,在天津市电子信息技师学院集训场地内,张洪豪正带领学生备战第二届全国技能大赛信息网络布线项目比赛。
博彩行业有很多玩法,每一种都有其风险和收益,需要投注者根据自己的情况进行选择。这就产生了羊群效应,一个点的变化(znode被删除),酿成了全面的影响(奉告大批的客户端)。
是以,这里的优化点,在于如何减少对一个znode的监听数目,最佳的情况是唯惟一个。
watch前一个有序节点如果先指定一个basePath,念念要获取锁的客户端,平直在该旅途下创建临时有序节点。
当创建的节点是最末节点时,代表获取到了锁。如果不是最小的节点,则只对前一个节点树立监听器,只监听前一个节点的删除手脚。
这样前一个节点被删除时,只会给下一个节点代表的客户端发送奉告,不会给扫数客户端发送奉告,从而幸免了羊群效应。
在幸免羊群效应的同期,使稳妥前锁成为自制锁。即按照请求锁的先后端正得到锁,幸免存在饥饿过度的线程。
五、后语本文从源码角度教学了使用Curator获取漫衍式锁的过程,接着从恭候锁的演化过程角度开拔,分析了Zookeeper在漫衍式锁场景下幸免羊群效应的惩处决议。
这是Zookeeper系列的第二篇,对于其watch旨趣分析、zab左券等著述也在安排的路上了。