第四节 zookeeper集群与分布式锁

目录

1. Zookeeper集群操作

1.1 客户端操作zk集群

1.2 模拟集群异常操作

1.3 curate客户端连接zookeeper集群

2. Zookeeper实战案例

2.1 创建项目引入依赖

2.2 获取zk客户端对象

2.3 常用API

2.4 客户端向服务端写入数据流程

2.5 服务器动态上下线、客户端动态监听

2.6 测试

3.Zookeeper分布式锁

3.1 什么是分布式锁

3.2 Zookeeper分布式锁分析

3.3 分布式锁实现


1. Zookeeper集群操作

1.1 客户端操作zk集群

1) 第一步启动集群,启动后查看Zookeeper进程。

image.png

jps命令 作用是显示当前所有java 进程的pid 的命令,QuorumPeerMain是zookeeper集群的启动入口类

2) 客户端连接

  • 连接集群所有客户端

[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183                  

image.png

连接集群单个客户端

# 连接2181
[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2181 
​
# 连接2182
[root@localhost zookeeper-1]# ./bin/zkCli.sh -server 192.168.58.200:2182
​
# 在2181中创建节点
[zk: 192.168.58.200:2181(CONNECTED) 0] create /test2
​
# 在2182中查询,发现数据已同步
[zk: 192.168.58.200:2182(CONNECTED) 0] ls /
[test1, test2, zookeeper]

以上两种方式的区别在于:

  • 如果只连接单个客户端,如果当前连接的服务器挂掉,当前客户端连接也会挂掉,连接失败。

  • 如果是连接所有客户端的形式,则允许集群中半数以下的服务挂掉!当半数以上服务挂掉才会停止服务,可用性更高一点!

3)集群节点信息查看

集群中的节点信息被存放在每一个节点/zookeeper/config/目录下

image.png

1.2 模拟集群异常操作

Leader选举

  • Serverid : 服务器ID

    • 三台服务 编号分别是 1 2 3

    • 编号越大在选择算法中权重越大

  • Zxid: 数据ID

    • 服务器中存放的最大的数据ID, 值越大数据越新

  • 在Leader选举的过程中 如果某台Zookeeper获得了超过半数的选票,就可以当选Leader

(1)首先我们先测试如果是从服务器挂掉,会怎么样

把3号服务器停掉,观察1号和2号,发现状态并没有变化

/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh stop
​
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status

image.png

由此得出结论,3个节点的集群,从服务器挂掉,集群正常

(2)我们再把1号服务器(从服务器)也停掉,查看2号(主服务器)的状态,发现已经停止运行了。

/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh stop
​
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status

image.png

由此得出结论,3个节点的集群,2个从服务器都挂掉,主服务器也无法运行。因为可运行的机器没有超过集群总数量的半数。

(3)我们再次把1号服务器启动起来,发现2号服务器又开始正常工作了。而且依然是领导者。

image.png

(4)我们把3号服务器也启动起来,把2号服务器停掉,停掉后观察1号和3号的状态。

/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh stop
​
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status

image.png

发现新的leader产生了~

由此我们得出结论,当集群中的主服务器挂了,集群中的其他服务器会自动进行选举状态,然后产生新得leader 。

(5)我们再次测试,当我们把2号服务器重新启动起来启动后,会发生什么?2号服务器会再次成为新的领导吗?我们看结果

/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start
​
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status

我们会发现,2号服务器启动后依然是跟随者(从服务器),3号服务器依然是领导者(主服务器),没有撼动3号服务器的领导地位。

由此我们得出结论,当领导者产生后,再次有新服务器加入集群,不会影响到现任领导者。

image.png

1.3 curate客户端连接zookeeper集群

public class CuratorCluster {
​//zookeeper连接private final static String CLUSTER_CONNECT = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";
​//session超时时间private static final int sessionTimeoutMs = 60 * 1000;
​//连接超时时间private static final int connectionTimeoutMs = 5000;
​private static CuratorFramework client;
​public static String getClusterConnect() {return CLUSTER_CONNECT;}
​@Beforepublic void init(){
​// 重试策略RetryPolicy retryPolicy =new ExponentialBackoffRetry(3000,10);
​// zookeeper连接client = CuratorFrameworkFactory.builder().connectString(getClusterConnect()).sessionTimeoutMs(60*1000).connectionTimeoutMs(15*1000).retryPolicy(retryPolicy).namespace("mashibing")  //当前程序创建目录的根目录.build();
​// 添加监听器client.getConnectionStateListenable().addListener(new ConnectionStateListener() {@Overridepublic void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {System.out.println("连接成功!");}});
​client.start();}
​//创建节点public void createIfNeed(String path) throws Exception {Stat stat = client.checkExists().forPath(path);if(stat == null){String s = client.create().forPath(path);System.out.println("创建节点: " + s);}}
​
​//从集群中获取数据@Testpublic void testCluster() throws Exception {createIfNeed("/test");
​//每隔一段时间 获取一次数据while(true){byte[] data = client.getData().forPath("/test");System.out.println(new String(data));
​TimeUnit.SECONDS.sleep(5);}}
}

在集群中的任意服务器节点,为test设置数据

[zk: 192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183(CONNECTED) 2] set /mashibing/test 12345

image.png

2. Zookeeper实战案例

2.1 创建项目引入依赖

<dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version>
</dependency>

2.2 获取zk客户端对象

public class ZkClientTest {private String connectString = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";private int sessionTimeout = 2000;private ZkClient zkClient;/*** 获取zk客户端连接*/@Beforepublic void Before(){/*** 参数1:服务器的IP和端口* 参数2:会话的超时时间* 参数3:会话的连接时间* 参数4:序列化方式*/zkClient = new ZkClient(connectString, sessionTimeout, 1000 * 15, new SerializableSerializer());}@Afterpublic void after(){zkClient.close();}
}

2.3 常用API

  • 创建节点

    /*** 创建节点*/@Testpublic void testCreateNode(){//创建方式 返回创建节点名称String nodeName = zkClient.create("/node1", "lisi", CreateMode.PERSISTENT);System.out.println("路径名称为:" + nodeName);zkClient.create("/node2","wangwu",CreateMode.PERSISTENT_SEQUENTIAL);zkClient.create("/node3","hehe",CreateMode.EPHEMERAL);zkClient.create("/node4","haha",CreateMode.EPHEMERAL_SEQUENTIAL);while(true){}}
  • 删除节点

    /*** 删除节点*/@Testpublic void testDeleteNode(){// 删除没有子节点的节点
//        boolean b1 = zkClient.delete("/node2");
//        System.out.println("删除成功: " + b1);// 递归删除节点信息boolean b2 = zkClient.deleteRecursive("/node2");System.out.println("删除成功: " + b2);}
  • 查看节点的子节点

    /*** 查询节点的子节点*/@Testpublic void testFindNodes(){//返回指定路径的节点信息List<String> ch = zkClient.getChildren("/");for (String c1 : ch) {System.out.println(c1);}}
  • 查看当前节点的数据

    • 注意:如果出现:org.I0Itec.zkclient.exception.ZkMarshallingError: java.io.StreamCorruptedException: invalid stream header: 61616161 异常的原因是: 在shell中的数据序列化方式 和 java代码中使用的序列化方式不一致导致 因此要解决这个问题只需要保证序列化一致即可 都使用相同端操作即可

    /*** 获取节点数据*/@Testpublic void testFindNodeData(){String nodeName = zkClient.create("/node3", "taotao", CreateMode.PERSISTENT);Object data = zkClient.readData("/node3");System.out.println(data);}
  • 查看当前节点的数据并获取状态信息

    /*** 获取数据以及当前节点状态信息*/@Testpublic void testFindNodeDataAndStat(){Stat stat = new Stat();Object data = zkClient.readData("/node20000000004", stat);System.out.println(data);System.out.println(stat);}
  • 修改节点数据

    /*** 修改节点*/@Testpublic void testUpdateNodeData(){zkClient.writeData("/node3","123456");}
  • 监听节点数据的变化

    /*** 监听节点数据*/@Testpublic void testNodeChange(){zkClient.subscribeDataChanges("/node3", new IZkDataListener() {// 当节点的值在修改时,会自动调用这个方法@Overridepublic void handleDataChange(String nodeName, Object result) throws Exception {System.out.println("节点名称: " + nodeName);System.out.println("节点数据: " + result);}// 当节点被删除时,会调用该方法@Overridepublic void handleDataDeleted(String nodeName) throws Exception {System.out.println("节点名称: " + nodeName);}});while(true){}}
  • 监听节点目录的变化

    /*** 监听节点目录的变化*/@Testpublic void testNodesChange(){zkClient.subscribeChildChanges("/node3", new IZkChildListener() {@Overridepublic void handleChildChange(String nodeName, List<String> list) throws Exception {System.out.println("父节点名称: " + nodeName);System.out.println("发生变更后,所有子节点名称: ");for (String name : list) {System.out.println(name);}}});while(true){}}
  • 判断某一个节点是否存在

    //判断节点是否存在@Testpublic void exist(){boolean exists = zkClient.exists("/node3");System.out.println(exists == true ? "节点存在" : "节点不存在");}

2.4 客户端向服务端写入数据流程

  • 写流程之写入请求,直接发送给Leader

image.png

  • 写流程之写入请求,发送给follower节点

image.png

2.5 服务器动态上下线、客户端动态监听

某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。

image.png

1)根节点下,创建servers节点

[zk: 192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183(CONNECTED) 0] 
create /servers "servers"
Created /servers

2)服务端代码

完成服务端向zookeeper注册、动态上下线的代码。

/*** 服务端*/
public class DistributeServer {private ZooKeeper client;// 连接信息private String connectString = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";// 超时时间private int sessionTimeOut = 30000;public static void main(String[] args) throws Exception {DistributeServer server = new DistributeServer();//1.获取zk连接server.getConnect();//2.将服务器注册到zk集群,args参数通过启动 main方法时传入即可server.register(args[0]);//3.启动业务逻辑(线程睡眠)Thread.sleep(Long.MAX_VALUE);}/*** 注册操作* @param hostName 将服务器注册到zk集群时,所需的服务名称*/private void register(String hostName) throws Exception {/*** ZooDefs.Ids.OPEN_ACL_UNSAFE: 此权限表示允许所有人访问该节点(服务器)* CreateMode.EPHEMERAL_SEQUENTIAL: 由于服务器是动态上下线的,上线后存在,下线后不存在,所以是临时节点* 而服务器一般都是有序号的,所以是临时、有序的节点.*/String node = client.create("/servers/" + hostName,hostName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println("已成功创建" + node + "节点");System.out.println(hostName + " 已经上线");}/*** 获取连接*/private void getConnect() throws IOException {client = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {}});}
}

2)客户端代码

服务端代码写好之后,再来完成客户端动态监听zk服务端各个节点的代码。

/*** 客户端*/
public class DistributeClient {private ZooKeeper zk;// 连接信息private String connectString = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";// 超时时间private int sessionTimeOut = 30000;public static void main(String[] args) throws Exception {DistributeClient client = new DistributeClient();//1.获取zk连接client.getConnection();//2.监听 /servers下面所有的子节点变化client.getServerList();//3.业务逻辑Thread.sleep(Long.MAX_VALUE);}/*** 获取连接*/private void getConnection() throws Exception {zk = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {//监听服务器地址的上下线try {getServerList();} catch (Exception e) {e.printStackTrace();}}});}/*** 监听 /servers路径下的所有子节点变化,true表示启动监听器*/private void getServerList() throws Exception {List<String> zkChildren = zk.getChildren("/servers", true);List<String> servers = new ArrayList<>();zkChildren.forEach(node -> {//拼接服务完整信息try {byte[] data = zk.getData("/servers/" + node, false, null);servers.add(new String(data));} catch (Exception e) {e.printStackTrace();}});System.out.println(servers);System.out.println("=======================");}
}

2.6 测试

1)Zookeeper命令行,完成测试

[zk: localhost:2181(CONNECTED) 1] create -e -s /servers/zk01 "192.168.58.200:2181"
Created /servers/zk010000000000

image.png

[zk: localhost:2181(CONNECTED) 2] create -e -s /servers/zk02 "192.168.58.200:2182"
Created /servers/zk020000000001

image.png

[zk: localhost:2181(CONNECTED) 3] create -e -s /servers/zk03 "192.168.58.200:2183"
Created /servers/zk030000000002

image.png

上面的执行结果可以看到,在servers下面依次创建子结点,客户端代码都可以成功监听到。

下面我们删除节点,看看客户端能不能做到动态监听功能(也即删除的节点不会再被监听到)。

[zk: localhost:2181(CONNECTED) 5] delete /servers/zk010000000000[zk: localhost:2181(CONNECTED) 7] delete /servers/zk020000000001[zk: localhost:2181(CONNECTED) 8] delete /servers/zk030000000002 

image.png

使用Java代码来测试

  • 先启动客户端代码

  • 再启动服务端代码

只是在服务端代码中,我们的 register 方法中传参用到了 args ,所以启动之前要传入这个参数。

image.png

传入 192.168.58.200 之后,可以看到服务端代码已经能够实现动态上线了。

image.png

这里我们的 192.168.58.200 动态上线之后,可以看到客户端也正常的监听到它了。

image.png

转到zk命令行中,也可以看到这台服务器的节点信息。

image.png

由于我们之前的服务端代码还启动着,此时我们再传入新的参数 192.168.58.200:2182,那么之前的2181服务器肯定会被挤掉(这里模拟就是main方法,同一个类肯定只能同时启动一次main方法了),那么我们看看客户端能不能动态监听到2181下线、2182上线。

image.png

服务端自然可以正常实现2182这台服务器的动态上线。在客户端代码中可以看到List集合中已经没有2181了(即2181已经下线了),而2182正常上线

image.png

3.Zookeeper分布式锁

3.1 什么是分布式锁

传统单体应用单机部署的情况下,可以使用并发处理相关的功能进行互斥控制,但是原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效。提出分布式锁的概念,是为了解决跨机器的互斥机制来控制共享资源的访问。

image.png

3.2 Zookeeper分布式锁分析

客户端(对zookeeper集群而言)向zookeeper集群进行上线注册,并在一个永久节点下创建有序的临时子节点后,根据编号顺序,最小顺序的子节点获取到锁,其他子节点由小到大监听前一个节点。

image.png

当拿到锁的节点处理完事务后,释放锁,后一个节点监听到前一个节点释放锁后,立刻申请获得锁,以此类推

image.png

3.3 分布式锁实现

1)创建 Distributedlock类, 获取与zookeeper的连接

  • 构造方法中获取连接

  • 添加 CountDownLatch

    CountDownLatch是具有synchronized机制的一个工具,目的是让一个或者多个线程等待,直到其他线程的一系列操作完成。

    CountDownLatch初始化的时候,需要提供一个整形数字,数字代表着线程需要调用countDown()方法的次数,当计数为0时,线程才会继续执行await()方法后的其他内容。

/*** 分布式锁*/
public class DistributedLock {private ZooKeeper client;// 连接信息private String connectString = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";// 超时时间private int sessionTimeOut = 30000;private CountDownLatch countDownLatch = new CountDownLatch(1);//1. 在构造方法中获取连接public DistributedLock() throws Exception {client = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {}});//等待Zookeeper连接成功,连接完成继续往下走countDownLatch.await();//2. 判断节点是否存在}//3.对ZK加锁public void zkLock(){//创建 临时带序号节点//判断 创建的节点是否是最小序号节点,如果是 就获取到锁;如果不是就监听前一个节点}//4.解锁public void unZkLock(){//删除节点}
}

2)对zk加锁

/*** 分布式锁*/
public class DistributedLock {private ZooKeeper client;// 连接信息private String connectString = "192.168.58.200:2181,192.168.58.200:2182,192.168.58.200:2183";// 超时时间private int sessionTimeOut = 30000;// 等待zk连接成功private CountDownLatch countDownLatch = new CountDownLatch(1);// 等待节点变化private CountDownLatch waitLatch = new CountDownLatch(1);//当前节点private String currentNode;//前一个节点路径private String waitPath;//1. 在构造方法中获取连接public DistributedLock() throws Exception {client = new ZooKeeper(connectString, sessionTimeOut, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {//countDownLatch 连上ZK,可以释放if(watchedEvent.getState() == Event.KeeperState.SyncConnected){countDownLatch.countDown();}//waitLatch 需要释放 (节点被删除并且删除的是前一个节点)if(watchedEvent.getType() == Event.EventType.NodeDeleted &&watchedEvent.getPath().equals(waitPath)){waitLatch.countDown();}}});//等待Zookeeper连接成功,连接完成继续往下走countDownLatch.await();//2. 判断节点是否存在Stat stat = client.exists("/locks", false);if(stat == null){//创建一下根节点client.create("/locks","locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}}//3.对ZK加锁public void zkLock(){//创建 临时带序号节点try {currentNode = client.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);//判断 创建的节点是否是最小序号节点,如果是 就获取到锁;如果不是就监听前一个节点List<String> children = client.getChildren("/locks", false);//如果创建的节点只有一个值,就直接获取到锁,如果不是,监听它前一个节点if(children.size() == 1){return;}else{//先排序Collections.sort(children);//获取节点名称String nodeName = currentNode.substring("/locks/".length());//通过名称获取该节点在集合的位置int index = children.indexOf(nodeName);//判断if(index == -1){System.out.println("数据异常");}else if(index == 0){//就一个节点,可以获取锁return;}else{//需要监听前一个节点变化waitPath = "/locks/" + children.get(index-1);client.getData(waitPath,true,null);//等待监听执行waitLatch.await();return;}}} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}
}

3)zk删除锁

    //4.解锁public void unZkLock() throws KeeperException, InterruptedException {//删除节点client.delete(currentNode,-1);}

4)测试

public class DistributedLockTest {
​public static void main(String[] args) throws Exception {
​final DistributedLock lock1 = new DistributedLock();final DistributedLock lock2 = new DistributedLock();
​new Thread(new Runnable() {@Overridepublic void run() {
​try {lock1.zkLock();System.out.println("线程1 启动 获取到锁");
​Thread.sleep(5 * 1000);lock1.unZkLock();System.out.println("线程1 释放锁");} catch (InterruptedException | KeeperException e) {e.printStackTrace();}}}).start();
​new Thread(new Runnable() {@Overridepublic void run() {
​try {lock2.zkLock();System.out.println("线程2 启动 获取到锁");
​Thread.sleep(5 * 1000);lock2.unZkLock();System.out.println("线程2 释放锁");} catch (InterruptedException | KeeperException e) {e.printStackTrace();}}}).start();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/679995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JUC-并发面试题

一、基础 1、为什么要并发编程 充分利用多核CPU的资源2、并发编程存在的问题 上下文切换:PU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。任务从保…

一个三极管引脚识别的小技巧,再也不用对照手册啦

三极管是一个非常常用的器件&#xff0c;时不时的就需要用到他们&#xff0c;有些时候当我们拿到一颗三极管时 &#xff0c;对于常用的友来说&#xff0c;三极管的引脚可能早已烂熟于心&#xff0c;而对于不常用或者初学者来说&#xff0c;三极管的引脚可以说是今天记下明天忘&…

Pytorch卷积层原理和示例 nn.Conv1d卷积 nn.Conv2d卷积

内容列表 一&#xff0c;前提 二&#xff0c;卷积层原理 1.概念 2.作用 3. 卷积过程 三&#xff0c;nn.conv1d 1&#xff0c;函数定义&#xff1a; 2, 参数说明: 3,代码: 4, 分析计算过程 四&#xff0c;nn.conv2d 1, 函数定义 2, 参数&#xff1a; 3, 代码 4, 分析计算过程 …

面了滴滴的数据分析师(实习),几道面试题都是原题啊。。。

年前&#xff0c;技术群组织了一场数据类的技术&面试讨论会&#xff0c;邀请了一些同学分享他们的面试经历&#xff0c;讨论会会定期召开&#xff0c;如果你想加入我们的讨论群或者希望要更详细的资料&#xff0c;文末加入。 喜欢本文记得收藏、关注、点赞 。技术交流文末…

C++之继承

一&#xff0c;概念及用法 1&#xff09;概念 首先我们来了解一下官方的概念&#xff1a;继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&a…

leetcode:131.分割回文串

树形结构&#xff1a; 切割到字符串的尾部&#xff0c;就是叶子节点。 回溯算法三部曲&#xff1a; 1.递归的参数和返回值&#xff1a; 参数字符串s和startIndex切割线 2.确定终止条件&#xff1a; 当分割线到字符串末尾时到叶子节点&#xff0c;一种方案出现 3.单层搜索…

使用R语言建立回归模型并分割训练集和测试集

通过简单的回归实例&#xff0c;可以说明数据分割为训练集和测试集的必要性。以下先建立示例数据: set.seed(123) #设置随机种子 x <- rnorm(100, 2, 1) # 生成100个正态分布的随机数&#xff0c;均值为2&#xff0c;标准差为1 y exp(x) rnorm(5, 0, 2) # 生成一个新的变…

【C语言】assert断言:保护程序的利器

在软件开发过程中&#xff0c;我们经常会遇到一些假设条件或者预期行为。例如&#xff0c;我们可能假设一个函数的输入参数必须在某个范围内&#xff0c;或者某个变量的值应该满足特定的条件。当这些假设或预期行为被打破时&#xff0c;程序可能会出现异常行为&#xff0c;甚至…

Java+SpringBoot:高校竞赛管理新篇章

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

2024年Midjourney 付费订阅流程 | Midjourney 各版本介绍,使用虚拟信用卡支付买Midjourney流程指南

1.Midjourney介绍 Midjourney 是一款备受欢迎的人工智能生成图像工具&#xff0c;它可以通过输入文字描述&#xff0c;自动生成精美的图像。与许多其他图像生成工具不同&#xff0c;Midjourney 不需要安装任何软件&#xff0c;也不受个人电脑性能的限制&#xff0c;因为它运行…

73. 矩阵置零(Java)

目录 题目描述&#xff1a;输入&#xff1a;输出&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 输入&#xff1a; matrix [[1,1,1],[1,0,…

(五)elasticsearch 源码之查询流程分析

https://www.cnblogs.com/darcy-yuan/p/17039526.html 1.概述 上文我们讨论了es&#xff08;elasticsearch&#xff0c;下同&#xff09;索引流程&#xff0c;本文讨论es查询流程&#xff0c;以下是基本流程图 2.查询流程 为了方便调试代码&#xff0c;笔者在电脑上启动了了…

S32 Design Studio PE工具配置GPIO

首先我们来讲最简单的GPIO配置 代码生成 按照下图步骤就能配置一个基本的GPIO口&#xff0c;在组件里面选择pin_mux&#xff0c;选中就能配置使能和方向&#xff0c;no pin routed就是没有配置的。GPIO口分ABCDE组&#xff0c;每组从0到最大的序号。 然后在functional prope…

多机多卡运行nccl-tests和channel获取

nccl-tests 环境1. 安装nccl2. 安装openmpi3. 单机测试4. 多机测试mpirun多机多进程多节点运行nccl-testschannel获取 环境 Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-91-generic x86_64)cuda 11.8 cudnn 8nccl 2.15.1NVIDIA GeForce RTX 4090 *2 1. 安装nccl #查看cuda版本 nv…

npm config set registry https://registry.npm.taobao.org 这个设置了默认的镜像源之后如何恢复默认的镜像源

要恢复npm默认的镜像源&#xff0c;你可以使用以下命令将registry设置回npm的官方源&#xff1a; npm config set registry https://registry.npmjs.org/这个命令会修改你的全局npm配置&#xff0c;将包的下载源改回npm官方的源。这样做之后&#xff0c;任何后续的npm install…

逐鹿比特币生态,Elastos 携新作 BeL2「重出江湖」

撰文&#xff1a;Babywhale&#xff0c;Techub News 文章来源Techub News&#xff0c;搜Tehub News下载查看更多Web3资讯。 刚刚过去的 2023 年&#xff0c;「比特币生态」成为了市场的绝对焦点之一。从铭文开始&#xff0c;到重新走进大众视野的 Stacks 与比特币闪电网络&am…

算法竞赛进阶指南——基本算法(倍增)

ST表 可以求区间最大、最小、gcd、lcm&#xff0c;符合 f(a, a) a都可以 求区间最值&#xff0c;一个区间划分成两段 f[i][j]: 从i开始&#xff0c;长度为2^j的区间最值 #include<iostream> #include<cmath> using namespace std; const int N 1e6 10; int n,…

mxxWechatBot流程与原理

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 免责声明&#xff1a;该工具仅供学习使用&#xff0c;禁止使用该工具从事违法活动&#xff0c;否则永久拉黑封禁账号&#xff01;&#xff01;&#xff01;本人不对任何工具的使用负责&am…

【FPGA Verilog】各种加法器Verilog

1bit半加器adder设计实例 module adder(cout,sum,a,b); output cout; output sum; input a,b; wire cout,sum; assign {cout,sum}ab; endmodule 解释说明 &#xff08;1&#xff09;assign {cout,sum}ab 是连续性赋值 对于线网wire进行赋值&#xff0c;必须以assign或者dea…