Curator基本使用

文章目录

    • 1. 基本操作
      • 1.1 建立连接
      • 1.2 创建结点
      • 1.3 查询结点
        • 查询数据
        • 查询子结点
        • 查看结点信息
      • 1.4 修改结点
        • 普通修改
        • 带乐观锁的修改
      • 1.5 删除
        • 删除单个结点
        • 删除带子结点的结点
        • 必须成功的删除
        • 带回调函数的删除
    • 2. 监听器事件
      • 2.1 NodeCache单一结点连续监听
      • 2.2 PathChildrenCache监听子结点
      • 2.3 TreeCache监听当前结点+子结点
    • 3. Curator 分布式锁
      • 3.1 zookeeper分布式锁原理
      • 3.2 Curator 封装的五种分布式锁
      • 3.3 模拟12306卖票实现可重入锁
      • 3.4 读写锁InterProcessReadWriteLock

Apache Curator官网

Apache Curator官网简介:

Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service. It includes a highlevel API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.

是一个 Java/JVM 客户端库,用于 Apache ZooKeeper 服务,一个分布式协调服务。它包括一个高级的 API 框架和实用工具,使得使用 google Apache ZooKeeper 变得更加容易和可靠。它还包括常见用例和扩展的处方,例如服务发现和 java8异步 DSL。

Curator 就是帮我们封装了zookeeper的常用操作,节省我们的代码量,例如我们只需要写不超过五行代码就可以使用zookeeper实现分布式锁,还有session超时重连,主从选举,分布式计数器,等适用于各种复杂的zookeeper场景的API封装,接下来让我们看看如何使用

首先我们需要导入pom依赖,Curator依赖的版本只能比zookeeper依赖的版本高

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><lombok.version>1.18.22</lombok.version><slf4j.version>1.7.36</slf4j.version><junit.version>4.7</junit.version><logback.version>1.2.3</logback.version><zookeeper.version>3.7.0</zookeeper.version><curator-framework.version>4.0.1</curator-framework.version><curator-recipes.version>4.0.1</curator-recipes.version>
</properties>
<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>compile</scope></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>${zookeeper.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>${curator-framework.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>${curator-recipes.version}</version></dependency>
</dependencies>

1. 基本操作

1.1 建立连接

public class Test01_connection {private CuratorFramework client;@Beforepublic void setUp() {String url = "zk的ip地址:端口号";/** Create a new client** @param connectString       连接url,集群用逗号分割* @param sessionTimeoutMs    会话超时时间 单位:毫秒,默认60秒* @param connectionTimeoutMs 连接超时时间 单位:毫秒,默认15秒* @param retryPolicy         重试策略* @return client*///重试策略,每隔3秒重试一次,最多重试10次ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000, 10);// 第一种:通过构造器构造
//        CuratorFramework client = CuratorFrameworkFactory.newClient(url,
//                60 * 1000, 15 * 1000, retryPolicy);// 第二种:builder模式链式编程client = CuratorFrameworkFactory.builder().connectString(url).sessionTimeoutMs(60 * 1000)  //会话超时时间.connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy)     //掉线重连策略.namespace("Test01_connection") //默认一个根目录,以后的所有创建都会在此目录下进行.build();client.start();//开启连接}@Afterpublic void after(){if(client != null){client.close();}}
}

1.2 创建结点

创建节点:create 持久 临时 顺序 数据

  1. 基本创建 :create().forPath(“”)
  2. 创建节点 带有数据:create().forPath(“”,data)
  3. 设置节点的类型:create().withMode().forPath(“”,data)
  4. 创建多级节点 /app1/p1 :create().creatingParentsIfNeeded().forPath(“”,data)

普通结点:默认持久无序

@Test
public void testCreate() throws Exception {client.create().forPath("/app1","curator创建的结点".getBytes());
}

在这里插入图片描述

创建临时无序结点

CreateMode是一个枚举类型,用来表示创建结点的类型,包括有序、无序、持久、临时

单词中文含义
PERSISTENT(persistent)持久
EQUENTIAL(ephemeral)临时
SEQUENTIAL(sequential)有序
public enum CreateMode {PERSISTENT(0, false, false, false, false),              //持久PERSISTENT_SEQUENTIAL持久(2, false, true, false, false), //持久且有序EPHEMERAL(1, true, false, false, false),                //临时EPHEMERAL_SEQUENTIAL(3, true, true, false, false),      //临时且有序CONTAINER(4, false, false, true, false),                //容器结点PERSISTENT_WITH_TTL(5, false, false, false, true),      //带TTL的持久结点PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true); //带TTL的持久有序结点
}

测试代码:

@Test
public void testCreate2() throws Exception {//默认类型:持久化client.create().withMode(CreateMode.EPHEMERAL).forPath("/app2", "curator创建的临时结点".getBytes());
}

其他模式的结点同理可创建,这里不再赘述

创建多级结点

在传统的zkClient连接中是无法直接创建多级结点的,curator中可以直接创建,但如果多级结点中的父结点不存在会报错,curator

也为我们提供了相应的方法,这里我们app3本来是没有的,添加creatingParentsIfNeeded就自动创建了

@Test
public void testCreate3() throws Exception {client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/app3/app3_1", "curator递归创建结点".getBytes());
}

设置ACL

@Test
public void testCreate4() throws Exception {client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).withACL(ZooDefs.Ids.CREATOR_ALL_ACL).forPath("/app5", "curator创建的带所有访问权限临时结点".getBytes());
}

可以看到相比原生的API,curator链式调用的方式非常的优雅,并且在idea中还有提示

1.3 查询结点

查询节点:

  1. 查询数据:get: getData().forPath()
  2. 查询子节点: ls: getChildren().forPath()
  3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()

相应的zkCli操作:

get [-s] [-w] path //查看结点存储的值及其结点状态
stat [-w] path //查看结点状态
ls [-s] [-w] [-R] path //查看某一结点下的子结点
查询数据

get /path

@Test
public void testQuery() throws Exception {byte[] data = client.getData().forPath("/app1");System.out.println(new String(data));
}
查询子结点

ls /path

@Test
public void testQuery2() throws Exception {List<String> list = client.getChildren().forPath("/app1");list.forEach(System.out::println);
}
查看结点信息

get -s /path

@Test
public void testQuery3() throws Exception {Stat status = new Stat(); //封装一个stat用来存储信息//查询节点状态信息:ls -sclient.getData().storingStatIn(status).forPath("/");System.out.println(status);
}

1.4 修改结点

修改数据

  1. 基本修改数据:setData().forPath()

  2. 根据版本修改: setData().withVersion().forPath()

    version 是通过查询出来的。目的就是为了让其他客户端或者线程不干扰我。

普通修改
@Test
public void testUpdate() throws Exception {client.setData().forPath("/","修改数据".getBytes());
}
带乐观锁的修改
@Test
public void testUpdateForVersion() throws Exception {//先要查出版本号Stat status = new Stat();client.getData().storingStatIn(status).forPath("/");int version = status.getVersion();//版本号不一样会导致修改失败client.setData().withVersion(version).forPath("/","修改数据".getBytes());
}

1.5 删除

删除节点: delete deleteall

  1. 删除单个节点:delete().forPath(“/app1”);
  2. 删除带有子节点的节点:delete().deletingChildrenIfNeeded().forPath(“/app1”);
  3. 必须成功的删除:为了防止网络抖动。本质就是重试。 client.delete().guaranteed().forPath(“/app2”);
  4. 回调:inBackground
删除单个结点
@Test
public void testDelete() throws Exception {client.delete().forPath("/app1");
}
删除带子结点的结点
@Test
public void testDeleteAll() throws Exception {client.delete().deletingChildrenIfNeeded().forPath("/app1");
}
必须成功的删除

主要是为了防止网络抖动

@Test
public void testDeleteAll() throws Exception {client.delete().guaranteed().forPath("/app1");
}
带回调函数的删除
@Test
public void testDeleteWithCallback() throws Exception {client.delete().guaranteed().inBackground(new BackgroundCallback() {@Overridepublic void processResult(CuratorFramework client, CuratorEvent event) throws Exception {//删除结点后的业务逻辑代码System.out.println("我被删除了~");System.out.println(event);}}).forPath("/app1");
}

2. 监听器事件

我们知道在zookeeper中,监听器是实现其特性的重要保障,而传统的zkClient代码有些繁琐,通过Curator我们可以写出更流畅的代码

Curator引入了Cache来实现对ZooKeeper服务端事件的监听

Zookeeper提供了三种Watcher

  • NodeCache:只是监听某一个特定的节点
  • PathChildrenCache:监控一个ZNode的子节点.
  • TreeCache:可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCache的组合

2.1 NodeCache单一结点连续监听

zkClient中我们知道,监听事件只能绑定一次(这个是zookeeper的性质),我们为了持续监听一个结点,通常做法是在监听事件完后再给结点绑定一次监听,而CuratorNodeCache为我们提供了持续监听的方法

@Test
public void testNodeCache() throws Exception {//countDownLatch为了堵塞主线程,不然主线程执行完了子线程也会结束CountDownLatch countDownLatch = new CountDownLatch(Integer.MAX_VALUE);//1. 创建NodeCache对象NodeCache nodeCache = new NodeCache(client, "/node1");//2. 注册监听nodeCache.getListenable().addListener(new NodeCacheListener() {//能够监听到结点增、删、改,且可以连续监听@Overridepublic void nodeChanged() throws Exception {System.out.println("结点修改了");//获取修改后的结点byte[] data = nodeCache.getCurrentData().getData();System.out.println(new String(data));countDownLatch.countDown();}});//3. 开启监听,如果设置为true,则开启监听时,加载缓存数据nodeCache.start(true);countDownLatch.await();
}

2.2 PathChildrenCache监听子结点

注意PathChildrenCache只会监听子结点,当前结点的变化是监听不到的

@Test
public void testPathChildrenCache() throws Exception {//countDownLatch为了堵塞主线程,不然主线程执行完了子线程也会结束CountDownLatch countDownLatch = new CountDownLatch(Integer.MAX_VALUE);//1.创建监听对象//第三个参数表示缓存每次结点更新后的数据PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/node2", true);//2. 绑定监听器pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {@Overridepublic void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {System.out.println("子结点变化了");System.out.println(event);if (PathChildrenCacheEvent.Type.CHILD_UPDATED == event.getType()) {//更新了子结点System.out.println("子结点更新了");//第一个getData里有很多数据,我们只拿data部分byte[] data = event.getData().getData();System.out.println("更新后的值为:" + new String(data));} else if (PathChildrenCacheEvent.Type.CHILD_ADDED == event.getType()) {//添加了子结点System.out.println("添加了子结点");String path = event.getData().getPath();System.out.println("子结点路径为" + path);} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED == event.getType()) {//删除了子结点System.out.println("删除了子结点");String path = event.getData().getPath();System.out.println("子结点路径为" + path);}countDownLatch.countDown();}});// 3. 开启监听pathChildrenCache.start();// 堵塞主线程countDownLatch.await();
}

2.3 TreeCache监听当前结点+子结点

TreeCache相当于NodeCache(只监听当前结点)+ PathChildrenCache(只监听子结点)的结合版,即监听当前和子结点

@Test
public void testPathTreeCache() throws Exception {//countDownLatch为了堵塞主线程,不然主线程执行完了子线程也会结束CountDownLatch countDownLatch = new CountDownLatch(Integer.MAX_VALUE);//1. 创建监听器TreeCache treeCache = new TreeCache(client, "/node2");//2. 注册监听treeCache.getListenable().addListener(new TreeCacheListener() {@Overridepublic void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {System.out.println("结点变化了");System.out.println(event);if (TreeCacheEvent.Type.NODE_UPDATED == event.getType()) {//更新了子结点System.out.println("结点更新了");//第一个getData里有很多数据,我们只拿data部分byte[] data = event.getData().getData();System.out.println("更新后的值为:" + new String(data));} else if (TreeCacheEvent.Type.NODE_ADDED == event.getType()) {//添加了子结点System.out.println("添加了结点");String path = event.getData().getPath();System.out.println("子结点路径为" + path);} else if (TreeCacheEvent.Type.NODE_REMOVED == event.getType()) {//删除了子结点System.out.println("删除了结点");String path = event.getData().getPath();System.out.println("删除结点路径为" + path);}countDownLatch.countDown();}});//3. 开启监听treeCache.start();countDownLatch.await();
}

3. Curator 分布式锁

分布式锁:

  • 在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下,没有任何问题
  • 但当我们的应用是分布式集群工作的情况下,属于多VM下的工作环境,跨JVM之间已经无法通过多线程的锁解决同步问题
  • 那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题—— 这就是分布式锁

常见的分布式锁实现有:

  • 基于缓存实现(redis、Memcache)
  • 基于zookeeper(Curator )
  • 数据库层面(悲观锁、乐观锁)

基于缓存虽然性能高,但是不可靠,因为redis集群保证的AP,基于数据库层面的性能太低了,不推荐

现在的最优解是基于zookeeper,zookeeper保证的CP,即强一致性,能够保证锁的功能

3.1 zookeeper分布式锁原理

核心思想(临时、顺序、监听):

当客户端要获取锁,则创建结点,使用完锁,则删除结点

  1. 客户端获取锁时,在lock结点下创建临时顺序结点
  2. 然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁使用完锁后,将该节点删除
  3. 如果发现自己创建的结点并非lock所有子结点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个结点,同时对其注册事件监听器,监听删除事件
  4. 如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。

常见面试题:

  • 为什么是临时结点?答:防止某个结点宕机而导致锁一直不释放的问题
  • 为什么是顺序结点? 答:公平锁,为了寻找最小结点从而获取锁

3.2 Curator 封装的五种分布式锁

Curator 为我们封装了五种常用的分布式锁

  1. InterProcessSemaphoneMutex:分布式排他锁(非可重入锁)
  2. InterProcessMutex:分布式可重入排他锁
  3. InterProcessReadWriteLock:分布式读写锁
  4. InterProcessMultiLock:将多个锁作为单个实体管理的容器
  5. InterProcessSemaphoreV2:共享信号量

熟悉JUC的同学看到这些名词应该会感到很熟悉,Curator 只是将单机的锁改成了分布式锁,其作用同样是保证共享变量的安全问题

下面我们结合具体的业务场景来分析一下分布式锁的

3.3 模拟12306卖票实现可重入锁

image-20220519211027529

首先我们知道很多用户都会访问12306的同一份共享资源(票),所以我们的分布式锁肯定是加在共享资源上的

我们先封装一个资源类,用来模拟共享资源

public class Ticket12306 implements Runnable{private int tickets = 10;//数据库里的票数@Overridepublic void run() {while (tickets > 0){System.out.println(Thread.currentThread().getName() + ":" + tickets);tickets--;}}
}

当我们的客户端卖票时如果不加锁肯定是会有票超卖的问题的

public class DistributedLock {public static void main(String[] args) {Ticket12306 ticket12306 = new Ticket12306();//创建客户端Thread t1 = new Thread(ticket12306, "飞猪");Thread t2 = new Thread(ticket12306, "携程");Thread t3 = new Thread(ticket12306, "去哪儿");//开始抢票t1.start();t2.start();t3.start();}
}

运行结果

飞猪:10
飞猪:9
飞猪:8
飞猪:7
飞猪:6
飞猪:5
飞猪:4
飞猪:3
飞猪:2
飞猪:1
去哪儿:10
携程:10

使用Curator封装好的锁非常简单,我们这里先演示一个 InterProcessMutex:分布式可重入排他锁的,对应单机中的SynchronizedReentrantLock

public class Ticket12306 implements Runnable {private int tickets = 10;//数据库里的票数private InterProcessMutex lock; //分布式可重入排他锁public Ticket12306() {String url = "zookeeper的ip地址:端口号";//重试策略,每隔3秒重试一次,最多重试10次ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000, 10);CuratorFramework client = CuratorFrameworkFactory.builder().connectString(url).sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).build();//开启连接zookeeperclient.start();lock = new InterProcessMutex(client,"/lock");}@Overridepublic void run() {//获取锁,三秒钟获取一次,如果没有获取到需要等三秒再获取try {lock.acquire(3, TimeUnit.SECONDS);while (tickets > 0) {System.out.println(Thread.currentThread().getName() + ":" + tickets);tickets--;}} catch (Exception e) {e.printStackTrace();} finally {//释放锁,注意锁无论如何都要释放掉try {lock.release();} catch (Exception e) {e.printStackTrace();}}}
}

每一个线程执行其实就对应一个新的zookeeper连接,这里其实已经模拟出了多客户端争抢锁的情况,我们从结果来看其实问题已经的到了解决

去哪儿:10
去哪儿:9
去哪儿:8
去哪儿:7
去哪儿:6
去哪儿:5
去哪儿:4
去哪儿:3
去哪儿:2
去哪儿:1

但是由于我们设置的是InterProcessMutex:分布式可重入排他锁,所以导致由同一个客户端多次抢到锁的现象发生

3.4 读写锁InterProcessReadWriteLock

和JUC里面的一样,主要核心思想是读锁共享,写锁排他

  • 读锁:大家都可以读,要想上读锁的前提:之前的锁没有写锁
  • 写锁:只有得到写锁的才能写。要想上写锁的前提是,之前没有任何锁

和JUC里一样使用即可,Curator 底层都已经封装好了

private InterProcessReadWriteLock readWriterLock;
readWriterLock.readLock().acquire();
readWriterLock.readLock().release();
readWriterLock.writeLock().acquire();
readWriterLock.writeLock().release();

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

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

相关文章

nginx平滑升级,信号使用,分割日志

信号 kill -l 看信号大全 nginx -h 中可以看到的信号较少 s signal : send signal to a master process: stop, quit, reopen, reload 可以使用man手册来查看详细的信号 如果没安装&#xff0c;去源码包里找到man文件 man 路径/nginx.8 不加路径打不开man帮助 st…

docker小知识:linux环境安装docker

安装必要软件包&#xff0c;执行如下命令 yum install -y yum-utils device-mapper-persistent-data lvm2目的是确保在安装 Docker 之前&#xff0c;系统已经安装了必要的软件包和服务&#xff0c;以支持 Docker 的正常运行。设置yum源&#xff0c;添加Docker官方的CentOS存储…

对Redis锁延期的一些讨论与思考

上一篇文章提到使用针对不同的业务场景如何合理使用Redis分布式锁&#xff0c;并引入了一个新的问题 若定义锁的过期时间是10s&#xff0c;此时A线程获取了锁然后执行业务代码&#xff0c;但是业务代码消耗时间花费了15s。这就会导致A线程还没有执行完业务代码&#xff0c;A线程…

vscode与vue/react环境配置

一、下载并安装VScode 安装VScode 官网下载 二、配置node.js环境 安装node.js 官网下载 会自动配置环境变量和安装npm包(npm的作用就是对Node.js依赖的包进行管理)&#xff0c;此时可以执行 node -v 和 npm -v 分别查看node和npm的版本号&#xff1a; 配置系统变量 因为在执…

机器学习:朴素贝叶斯算法(Python)

一、朴素贝叶斯算法的实现 naive_bayes_classifier.py import numpy as np import collections as cc # 集合的计数功能 from scipy.stats import norm # 极大似然估计样本的均值和标准方差 from data_bin_wrapper import DataBinsWrapperclass NaiveBayesClassifier:"…

Python分支和循环结构及其应用(文末送书)

一、分支结构 应用场景 我们写的Python代码都是一条一条语句顺序执行&#xff0c;这种代码结构通常称之为顺序结构。然而仅有顺序结构并不能解决所有的问题。 if语句的使用 在Python中&#xff0c;要构造分支结构可以使用if、elif和else关键字。所谓关键字就是有特殊含义的…

SpringCloud(17)之SpringCloud Stream

一、Spring Cloud Stream介绍 Spring Cloud Stream是一个框架&#xff0c;用于构建与共享消息系统连接的高度可扩展的事件驱动微服务。该框架提供了一个灵活的编程模型&#xff0c;该模型建立在已经建立和熟悉的Spring习惯用法和最佳实践之上&#xff0c;包括对持久发布/子语义…

腾讯云4核8G服务器优惠价格表(轻量+CVM)

腾讯云4核8G服务器多少钱&#xff1f;轻量应用服务器4核8G12M带宽一年446元、646元15个月&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;标准型SA2服务器1444.8元一年&#xff0c;在txy.wiki可以查询详细配置和精准报价…

ChatGPT带火的HBM是什么?

“ChatGPT是人工智能领域的iPhone时刻&#xff0c;也是计算领域有史以来最伟大的技术之一。” 英伟达创始人兼CEO黄仁勋此前这样盛赞ChatGPT。 ChatGPT突然爆火&#xff0c;对大算力芯片提出了更高更多的要求。近日&#xff0c;据韩国经济日报报道&#xff0c;受惠于ChatGPT&am…

[rust] 10 project, crate, mod, pub, use: 项目目录层级组织, 概念和实战

文章目录 一 项目目录层级组织概念1.1 cargo new 创建同名 的 Project 和 crate1.2 多 crate 的 package1.3 mod 模块1.3.1 创建嵌套 mod1.3.2 mod 树1.3.3 用路径引用 mod1.3.3.1 使用绝对还是相对? 1.3.4 代码可见性1.3.4.1 pub 关键字1.3.4.2 用 super 引用 mod1.3.4.3 用 …

Linux之安装jdk,tomcat,mysql,部署项目

目录 一、操作流程 1.1安装jdk 1.2安装tomcat&#xff08;加创建自启动脚本&#xff09; 1.3 安装mysql 1.4部署项目 一、操作流程 首先把需要用的包放进opt文件下 1.1安装jdk 把jdk解压到/usr/local/java里 在刚刚放解压包的文件夹打开vim /etc/profile编辑器&#xff0c…

普中51单片机学习(8*8LED点阵)

8*8LED点阵 实验代码 #include "reg52.h" #include "intrins.h"typedef unsigned int u16; typedef unsigned char u8; u8 lednum0x80;sbit SHCPP3^6; sbit SERP3^4; sbit STCPP3^5;void HC595SENDBYTE(u8 dat) {u8 a;SHCP1;STCP1;for(a0;a<8;a){SERd…

【GameFramework框架内置模块】4、内置模块之调试器(Debugger)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a;…

MATLAB_ESP32有限脉冲响应FIR无限脉冲响应IIR滤波器

要点 ESP32闪烁LED&#xff0c;计时LEDESP32基础控制&#xff1a;温控输出串口监控&#xff0c;LCD事件计数器&#xff0c;SD卡读写&#xff0c;扫描WiFi网络&#xff0c;手机控制LED&#xff0c;经典蓝牙、数字麦克风捕捉音频、使用放大器和喇叭、播放SD卡和闪存MP3文件、立体…

如何多环境切换?如何在微服务配置多环境?

问题本质: nacos配置中心的配置是如何被项目读取到的&#xff1f;(nacos的配置中心和项目是如何联系的&#xff1f;) 注意&#xff1a;nacos有配置管理和服务管理&#xff0c;别弄混。自动注册的是服务管理&#xff01;&#xff01;&#xff01; 1. 如何注册到nacos服务管理中心…

蓝桥杯备战刷题one(自用)

1.被污染的支票 #include <iostream> #include <vector> #include <map> #include <algorithm> using namespace std; int main() {int n;cin>>n;vector<int>L;map<int,int>mp;bool ok0;int num;for(int i1;i<n;i){cin>>nu…

玩转ChatGPT:参考文献速查

一、写在前面 各位大佬&#xff0c;我又回来了&#xff0c;最近2月太忙啦&#xff08;过年、奶娃、本子、材料、结题&#xff09;&#xff0c;断更了。现水一篇证明我还活着&#xff01;&#xff01;&#xff01; 最近在写国自然本子&#xff0c;遇到一个估计大家都会遇到的问…

Unity将4个纹理图拼接成1个纹理

需要的效果 最终实现的效果大概如下: 4个贴图上去 这里随便放一个切分的图。 Shader代码如下 直接上代码: // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)// Unlit shader. Simplest possible textured shad…

UE5 C++ Widget练习 Button 和 ProgressBar创建血条

一. 1.C创建一个继承Widget类的子类&#xff0c; 命名为MyUserWidget 2.加上Button 和 UserWidget的头文件 #include "CoreMinimal.h" #include "Components/Button.h" #include "Blueprint/UserWidget.h" #include "MyUserWidget.genera…

Python实现自动检测设备连通性并发送告警到企业微信

背景&#xff1a;门禁机器使用的WiFi连接&#xff0c;因为某些原因会不定期自动断开连接&#xff0c;需要人工及时干预&#xff0c;以免影响门禁数据同步&#xff0c;故写此脚本&#xff0c;定时检测门禁网络联通性。 #首次使用要安装tcping模块 pip install tcpingfrom tcpin…