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;
    @Before
    public 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();//开启连接
    }

    @After
    public 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 -s
    client.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() {
                @Override
                public 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() {
                //能够监听到结点增、删、改,且可以连续监听
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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;//数据库里的票数

    @Override
    public 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();
        //开启连接zookeeper
        client.start();
        lock = new InterProcessMutex(client,"/lock");
    }

    @Override
    public 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();

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GEE入门篇|遥感专业术语(实践操作2):空间分辨率(Spatial Resolution)

目录 空间分辨率&#xff08;Spatial Resolution&#xff09; 1.MODIS&#xff08;搭载在Aqua 和 Terra 卫星上&#xff09; 2. TM&#xff08;搭载在早期LandSat卫星上&#xff09; 3.MSI&#xff08;搭载在在Sentinel-2 卫星上&#xff09; 4.NAIP 空间分辨率&#xff0…

基于qt的图书管理系统----03核心界面设计

参考b站&#xff1a;视频连接 源码github&#xff1a;github 目录 1 添加软件图标2 打包程序3 三个管理界面设计4 代码编写4.1 加载界面4.2 点击按钮切换界面4.3 组团添加样式4.4 搭建表头4.5 表格相关操作 从别人那里下载的项目会有这个文件&#xff0c;里边是别人配置的路径…

ETL:数据转换与集成的关键过程

ETL&#xff1a;数据转换与集成的关键过程 在现代数据驱动的世界中&#xff0c;有效地管理和处理数据对于企业的成功至关重要。ETL&#xff08;提取、转换、加载&#xff09;是一种关键的数据处理过程&#xff0c;有助于将数据从源系统提取、清洗、转换并加载到目标系统中&…

大蟒蛇(Python)笔记(总结,摘要,概括)——第9章 类

目录 9.1 创建和使用类 9.1.1 创建Dog类 9.1.2 根据类创建实例 9.2 使用类和实例 9.2.1 Car类 9.2.2 给属性指定默认值 9.2.3 修改属性的值 9.3 继承 9.3.1 子类的_init_()方法 9.3.2 给子类定义属性和方法 9.3.3 重写父类中的方法 9.3.4 将实例用作属性 9.3.5 模拟实物 9.…

Maven setting.xml 配置

目的&#xff1a;可以把我们书写的jar包发布到maven私有仓库&#xff0c;简称私仓 1. 打开云效 2.点击 非生产库-snapshot mave release仓库与snapshot仓库区别&#xff1f; 在软件开发中&#xff0c;"Maven release 仓库"和"Maven snapshot 仓库"是两种…

google浏览器chrome无法访问localhost等本地虚拟域名的解决方法

场景一&#xff1a; 谷歌浏览器访问出现&#xff1a;forbbiden 403 问题&#xff0c;或者直接跳转到正式域名(非本地虚拟域名) 访问本地的虚拟域名http://www.hd.com/phpinfo.php?p1发生了302 条状 火狐浏览器正常访问; 解决方法&#xff1a; 方法1&#xff1a;在谷歌浏览器…

8 buuctf解题

[BJDCTF2020]just_a_rar 1 下载&#xff0c;得到 发现有加密 使用ARCHPR设置四位数掩码爆破 得到口令2016&#xff0c;解压得到图片&#xff0c;flag在图片exif中 在备注里面看见了flag [HBNIS2018]excel破解 1 下载下来是attachment.xls 修改后缀为rar 使用010 Editor打开&a…

5G网络建设 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N&#xff0c; 接下来需要各个基站之间使用光纤进行连接以确保基…

基于Docker和Springboot两种方式安装与部署Camunda流程引擎

文章目录 前言1、Docker安装1.1、拉取Camunda BPM镜像1.2、编写docker启动camunda容器脚本1.3、docker启动脚本1.4、访问验证 2、SpringBoot启动2.1、下载地址2.2、创建SpringBoot项目并配置基础信息2.3、下载SpringBoot项目并在idea中打开2.4、pom修改2.5、application.yml配置…

P1927 防护伞

题目传送门&#xff1a;P1927 防护伞 作业出了这道题&#xff0c;写一篇题解纪念一下。 这道题可以简化为“先枚举所有点&#xff0c;然后把这些点到另外点距离的最大距离和其他点比较&#xff0c;求出最小距离”。 这样说可能也听不懂&#xff0c;还可以再简化&#xff1a; …

深度学习环境配置常见指令

首先打开anaconda prompt&#xff0c;激活对应虚拟环境。 导入torch并获取对应版本 import torch torch.__version__导入torchvision并获取对应版本 import torchvision torchvision.__version__ 检查cuda是否可用 torch.cuda.is_available() 获取CUDA设备数 torch.cuda.…

85、字符串操作的优化

上一节介绍了在模型的推理优化过程中,动态内存申请会带来额外的性能损失。 Python 语言在性能上之所以没有c++高效,有一部分原因就在于Python语言将内存的动态管理过程给封装起来了,我们作为 Python 语言的使用者是看不到这个过程的。 这一点有点类似于 c++ 标准库中的一些…

CAN——创建一个数据库DBC文件

一、创建一个工程 file——new——can 500kbaud1ch 得到一个工程文件.cfg 二、实现两个节点通讯 can networks 三、创建数据库DBC tool——candbeditor——file——creatdatabase——cantemplate.dbc 1.建数值表 view——value tables——空白处右击add—— definition 定…

shell脚本编写基础实战

1.判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。 第一步&#xff1a;配置邮件服务 yum install mailx -y ------安装邮件服务 设置邮箱服务相关配置 vim /etc/mail.rc 第…

【线程池项目(二)】线程池FIXED模式的实现

在上一篇【线程池项目&#xff08;一&#xff09;】项目介绍和代码展示 中&#xff0c;我们展示了线程池的两个版本实现&#xff0c;它们的代码在具体的实现细节上是优化过了的。下文提供的代码并非完整&#xff0c;也有很多地方尚需改善&#xff0c;但这些差异对理解整个项目而…

深度学习(17)--DataLoader自定义数据集制作

目录 DataLoader自定义数据集制作 1.从标注文件(txt文件)中读取数据和标签 2.分别把数据和标签存在两个list中 3.设置完整的图像数据路径 4.根据任务整合出一个数据处理类 5.数据预处理 6.使用定义好的类来实例化DataLoader 7.检查数据和标签是否对应 8.使用创建好的D…

【行业会议】优积科技应邀参加住建部模块建筑企业2023年工作座谈会

2023年3月2日&#xff0c;优积建筑科技发展&#xff08;上海&#xff09;有限公司&#xff08;以下简称“优积科技”&#xff09;应邀参加由住房和城乡建设部科技与产业化发展中心&#xff08;以下简称“住建部科技与产业化中心”&#xff09;组织召开的模块建筑企业2023年工作…

OpenCV 4基础篇| OpenCV图像基本操作

目录 1. 图像读取1.1 cv2.imread() 不能读取中文路径和中文名称1.2 cv2.imdecode() 可以读取中文路径和中文名称 2. 图像的显示2.1 openCV显示图像 cv2.imshow()2.2 matplotlib显示图像 plt.imshow() 3. 图像的保存 cv2.imwrite()4. 图像的复制4.1 img.copy()4.2 np.copy()4.3 …

基于java springboot的图书管理系统设计和实现

基于java springboot的图书管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源码联…

Ansible 简介及部署 基础模块学习 ansible部署rsync 及时监控远程同步

Ansible介绍&#xff1a; Ansible 是一个配置管理系统&#xff0c;当下最流行的批量自动化运维工具之一&#xff0c;它是一款开源的自动化工具&#xff0c;基于Python开发的配置管理和应用部署的工具。 Ansible 是基于模块工作的&#xff0c;它只是提供了一种运行框架&#xff…