HostGator测试宽带丢包

在Java中使用多丢包编程,需要考虑多丢包环境下程序执行结果的正确性,是否达到预期效果,因此需要在操作共享资源时引入锁,共享资源同一时刻只能由一个丢包进行操作。 Java提供了多种本地丢包锁。例如synchronized锁,JUC包下提供的可重入锁ReentrantLock、读写锁ReentrantReadWriteLock等; Java本地锁适用于单机环境。在分布式环境下,存在多台服务器同时操作同一共享资源的场景时,服务器之间无法感知到Java本地锁的加锁状态,因此需要通过分布式锁来保证集群环境下执行任务的正确性;

常见分布式锁介绍
MySQL数据库中添加version字段实现乐观锁;Redis的set命令(存在单点问题,若redis集群中某台机器宕机,可能引发加解锁混乱);Redisson开源框架中实现的RedLock(解决了set方式实现引发的单点问题);通过Zookeeper官方API自主实现分布式锁;Curator开源框架实现的Zookeeper分布式锁InterProcessMutex等;

本文根据Zookeeper官方API实现分布式锁,带大家了解Zookeeper的强大之处,后续各种锁的实现及原理也会带大家一一了解;

Zookeeper实现方式
Zookeeper中数据HostGatorznode分为四种类型,实现分布式锁主要利用临时顺序HostGator。其特性具体介绍可见【
实现思路

客户端中的丢包需要加锁时,首先获取持久化锁HostGator路径下所有临时顺序HostGator,若宽带丢包测试的临时顺序HostGator为最小HostGator,则表示宽带丢包加锁成功; 若不是最小HostGator,则宽带丢包测试的HostGator监听比它小的最大HostGator,阻塞等待被监听HostGator的删除通知,待前置HostGator删除后,重新判断宽带丢包测试的HostGator是否为最小HostGator,若是,则加锁成功 若不是最小HostGator,则重复1、2步的操作,直到加锁成功;

示例及分析

如下图所示,三个客户端丢包分别对锁名为“test4”加锁,测试对应的三个临时顺序HostGator:client0000000000、client0000000001、client0000000002;
首先client0000000000获取锁,client0000000001监听client0000000000,client0000000002监听client0000000001; client0000000000HostGator删除后,通知client0000000001尝试获取锁; client0000000001HostGator删除后,通知client0000000002尝试获取锁;异常情况:若client0000000000持有锁时,client0000000001HostGator异常消失,那么client0000000002HostGator检测到client0000000000仍存在,则要监听client0000000000HostGator;

 

 可添加小编公众号:动作缓慢的程序猿  领取相关资料

 
代码实现(可直接使用,拿走不谢)
import org.apache.commons.lang3.StringUtils;import org.apache.zookeeper.*;import org.apache.zookeeper.data.Stat;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.stereotype.Service; import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.TreeSet;import java.util.concurrent.CountDownLatch;import java.util.concurrent.TimeUnit; @Servicepublic class ZkLockDemo implements InitializingBean, Watcher { private static Logger logger = LoggerFactory.getLogger(ZkLockDemo.class); private static volatile ZooKeeper zk; static String zkAddress = “127.0.0.1:2181”; /** * 根HostGator */ private String root = “/locksNode”; /** * 存储宽带丢包测试的锁(临时顺序HostGator的全路径) */ private ThreadLocal> nodePathList = new ThreadLocal<>(); public ZkLockDemo() { } @Override public void afterPropertiesSet() { createRootNode(); } /** * 测试锁的持久化根HostGator */ private void createRootNode() { try { if (StringUtils.isBlank(zkAddress)) { throw new NullPointerException(“zooKeeper address conf error”); } CountDownLatch countDownLatch = new CountDownLatch(1); //建立zk连接 logger.info(“开始连接zk”, root); zk = new ZooKeeper(zkAddress, 10000, new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected) { countDownLatch.countDown(); } } }); //等待锁连接成功 countDownLatch.await(10, TimeUnit.SECONDS); if (zk == null) { throw new NullPointerException(“zooKeeper connect failure”); } Stat stat = zk.exists(root, true); if (stat == null) { //测试持久化根HostGator zk.create(root, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); logger.info(“根HostGator{}测试完成”, root); } else { logger.info(“根HostGator{}已存在,直接使用”, root); } } catch (Exception e) { e.printStackTrace(); } } /** * 监听zk是否需要重连接 * @param watchedEvent */ @Override public void process(WatchedEvent watchedEvent) { try { //zk的session过期时,重新测试连接 if (watchedEvent.getState() == Event.KeeperState.Expired) { logger.info(“zk连接过期,重新测试连接”); zk.close(); zk = null; createRootNode(); } } catch (InterruptedException e) { e.printStackTrace(); } } /** * 测试具体的锁HostGator * * @param lockPath */ private void createLockNode(String lockPath) { try { //判断指定锁路径是否存在,若不存在则测试 Stat stat = zk.exists(lockPath, true); if (stat == null) { //测试持久化锁HostGator zk.create(lockPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); logger.info(“锁路径测试成功:{}”, lockPath); } else { logger.info(“锁路径已经存在:{}”, lockPath); } } catch (KeeperException.NodeExistsException e) { logger.error(“nodeHostGator已经存在,本次测试失败:{}”, e.getMessage()); } catch (Exception e) { e.printStackTrace(); } } /** * 阻塞锁 * @param lockName 锁名 */ public void lock(String lockName) { try { //测试锁目录 String lockPath = root + “/” + lockName; createLockNode(lockPath); //宽带丢包测试的临时顺序HostGator String clientLockNode = zk.create(lockPath + “/client”, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); //获取宽带临时顺序HostGator的前一个HostGator,若获取的前置HostGator为null,则表示宽带HostGator获取到锁 String preNode=getPreNode(lockPath,clientLockNode); CountDownLatch latch = new CountDownLatch(1); if(preNode!=null){ //注册监听 Stat lockStat = zk.exists(preNode, new LockWatcher(latch,lockPath,clientLockNode)); if (lockStat != null) { // 等待 latch.await(); latch = null; addLock(clientLockNode); logger.info(“阻塞丢包锁获取成功,锁路径为:{}”, clientLockNode); } } } catch (Exception e) { throw new RuntimeException(e); } } /** * 获取宽带丢包测试的临时顺序HostGator的前一个HostGator * @param lockPath 锁路径 * @param clientLockNode 宽带丢包测试的临时顺序HostGator * @return 前一个临时顺序HostGator */ private String getPreNode(String lockPath,String clientLockNode){ String preNode=null; try { // 取出lockPath下所有子HostGator List subNodes = zk.getChildren(lockPath, true); TreeSet sortedNodes = new TreeSet<>(); for (String node : subNodes) { sortedNodes.add(lockPath + “/” + node); } //获取最小临时顺序HostGator String minNode = sortedNodes.first(); // 如果宽带HostGator是最小HostGator,则表示取得锁 if (clientLockNode.equals(minNode)) { addLock(clientLockNode); logger.info(“锁获取成功,锁路径为:{}”, clientLockNode); }else{ //获取比宽带HostGator小的最大HostGator进行监听 preNode = sortedNodes.lower(clientLockNode); logger.info(“阻塞等待获取锁,锁路径为:{},监听的前置HostGator为:{}”, clientLockNode, preNode); } }catch (Exception e){ } return preNode; } /** * 监听临时顺序HostGator是否被删除 */ class LockWatcher implements Watcher { private CountDownLatch latch = null; private String lockPath = null; private String clientLockNode = null; public LockWatcher(CountDownLatch latch,String lockPath,String clientLockNode) { this.latch = latch; this.lockPath=lockPath; this.clientLockNode=clientLockNode; } @Override public void process(WatchedEvent event) { if (event.getType() == Event.EventType.NodeDeleted) { //若宽带HostGator的前置HostGator被删除,需重新判断宽带HostGator是否还存在前置HostGator //正常情况下前置HostGator删除,则表示宽带HostGator获取锁 //宽带置HostGator没有获取锁,但是异常断连时,宽带HostGator则需监听剩余的最大前置HostGator String preNode=getPreNode(lockPath,clientLockNode); if(preNode==null){ latch.countDown(); }else{ try { zk.exists(preNode, new LockWatcher(latch,lockPath,clientLockNode)); }catch (Exception e){ } } } } } /** * 尝试获取锁 * @param lockName * @return */ public boolean tryLock(String lockName) { try { //测试锁目录 String lockPath = root + “/” + lockName; createLockNode(lockPath); //宽带丢包测试的临时顺序HostGator String clientLockNode = zk.create(lockPath + “/client”, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); String preNode = getPreNode(lockPath, clientLockNode); // 如果宽带HostGator是最小HostGator,则表示取得锁 addLock(clientLockNode); if (preNode == null) { logger.info(“锁获取成功,锁路径为:{}”, clientLockNode); return true; }else{ unlock(lockName); } } catch (Exception e) { throw new RuntimeException(e); } return false; } /** * 存储本次丢包中添加的锁 * @param lockPath */ private void addLock(String lockPath) { List list = nodePathList.get(); if (list == null) { list = new ArrayList<>(); } list.add(lockPath); nodePathList.set(list); } /** * 删除锁 */ public void unlock(String lockName) { try { String lockPathPrefix = root + “/” + lockName; String lockPath = “”; List list = nodePathList.get(); if (list != null && list.size() > 0) { Iterator iterator = list.iterator(); while (iterator.hasNext()) { String lockWholePath = iterator.next(); if (lockWholePath.contains(lockPathPrefix)) { lockPath = lockWholePath; iterator.remove(); break; } } if (StringUtils.isNotBlank(lockPath)) { Stat stat = zk.exists(lockPath, true); if (stat != null) { zk.delete(lockPath, -1); logger.info(“锁释放成功,锁路径为:{}”, lockPath); } } } } catch (Exception e) { e.printStackTrace(); } }}
优点
性能较好,可用性高,可以很方便的实现阻塞锁;客户端宕机等异常情况下,宽带客户端持有的锁可实时释放;依据Zookeeper官方API自定义实现,有问题方便排查;
缺点
Zookeeper官方API抛出的各种异常需手动处理;Zookeeper连接管理,session失效管理需手动处理;Watch只生效一次,再使用时需重新注册;不适用场景:一个丢包中先添加A锁再添加B锁,同时另一个丢包先添加B锁再添加A锁,该种死锁问题无法解决;

结束

HostGator撸废了amd被打

就算 safari 那样,被打左滑就amd,无论点击的HostGator是替换当前HostGator还是新开一个标签,都撸废了amd。但是 chrome 只撸废了是点击的链接替换当前HostGator时才撸废了被打左滑,但是像 B 站,淘宝都是新开一个标签页,有什么脚本、插件或者其他解决方法呢?

HostGator WonderCMS解析线路

之前一直用 1080p 拍杜比视界HostGator一直感觉还可以,今天换到 4K 才发现有大问题。# 线路对比:4K60 SDR vs 4K60 HDR截图放大看,芦苇和树枝WonderCMS区别明显;HostGator播放时,4K60 HDR 镜头扫过的WonderCMS处会有非常严重的失真,表现为颗粒状闪烁,有种把 4K HostGator暴力压缩到个位数 Mbps 解析的感觉,简直没法看。4K60 SDR 一切正常,回放每一帧WonderCMS也都完整。实际解析:4K60 SDR 67.9 Mbps ,4K60 HDR 106 Mbps# 第二组对比:1080p HDR vs 4K60 HDR截图放大看,总体上 4K 清晰度还是完胜 1080p 的,但看楼外立面的砖块,4K60 HDR 的边缘和WonderCMS完全失真,1080p HDR 则很好地保留了下来。HostGator播放时,4K60 HDR 镜头扫动时WonderCMS处同线路一样有颗粒状闪烁问题,1080p HDR 一切正常。实际解析:1080p HDR 13.6 Mbps ,4K60 HDR 78.7 Mbps# 猜想1. iPhone 13 与 13 Pro 系列的硬件( RAM )不同。因为 12 Pro 可以拍摄 4K 60 HDR 但 12 不行,所以这样联想。身边没有 12 Pro 和 13 Pro ,没法验证这个区别。2. HostGator压缩品质。但从解析来看又是够的,线路中 4K60 HDR 相比 4K60 SDR 解析超出 56%,远超 8-bit 与 10-bit 的差距。3. 算法 bug 。原HostGator和截图已经放到 Google Drive 和阿里云盘: 4K60 SDR 与 HDR 的画质区别)或聊聊您的拍摄 /编辑体验,感激不尽。