Zookeeper4:Java客户端、应用场景以及实现、第三方客户端curator工具包

news2024/11/15 18:00:50

文章目录

    • Java连接Zookeeper服务端
      • 依赖
      • 代码使用
    • 应用场景
      • 统一命名服务
      • 统一配置管理
      • 统一集群管理
      • 服务器节点动态上下线
        • 理解
        • 实现
          • 模拟服务提供者【客户端代码】-注册服务
          • 模拟服务消费者【客户端代码】-获取服务信息进行请求消费
      • 软负载均衡
      • 分布式锁
        • 理解
        • 实现
      • 生产集群安装N台机器合适
    • 第三方基于zookeeper的包
      • curator
        • 依赖
        • 代码

Java连接Zookeeper服务端

文档: https://zookeeper.apache.org/doc/r3.9.1/javaExample.html

依赖

依赖

  <dependencies>

    <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
    <dependency>
      <groupId>org.apache.zookeeper</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.9.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.dromara.hutool/hutool-all -->
    <dependency>
      <groupId>org.dromara.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>6.0.0-M11</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.10.2</version>
    </dependency>



    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.30</version>
      <scope>provided</scope>
    </dependency>


  </dependencies>

代码使用

Java代码

public class ZkClient {

    @Test
    @SneakyThrows
    public void test1() {
        String connectString = "192.168.19.107:2181"; // zookeeper服务端信息
        int sessionTimeout = 2000; // 连接最大时长(毫秒)
        ZooKeeper zooKeeperClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent event) {
                Console.log("服务端推送给客户端的监听事件信息 == {}", event);
            }
        });

        // 监听节点数据的变化 === 等价于get -w 命令
        try {
            String s = zooKeeperClient.create("/test", "testData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            Console.log("创建节点成功:{}", s);
        } catch (Exception e) {
            Console.log("创建节点失败:{}", e.getMessage());
        }


        // 启动监听增删子节点的变化,然后在前面【Watcher】能收到监听事件 === 等价于ls -w 命令
        List<String> children = zooKeeperClient.getChildren("/test", true);
        Console.log("Zookeeper服务端当前/test所有的子节点名字:{}", children);

        //启动节点的状态信息变化 === 等价于stat -w 命令
        Stat statInfo = zooKeeperClient.exists("/test", true);
        Console.log("Zookeeper服务端当前/test节点的状态信息:{}" , statInfo);



        //程序永远不结束
        while (true) {
            try {
                Thread.sleep(1000); // 暂停1秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

1. 前提背景
在这里插入图片描述

2. 开始执行代码
在这里插入图片描述

3. 命令增加节点
在这里插入图片描述

4. Java客户端监听到的消息
在这里插入图片描述

应用场景

统一命名服务

对应用、服务同意命名便于识别,比如一个对外服务接口的多集群,则需要统一的管理同一服务的所有IP

在这里插入图片描述

统一配置管理

  • 场景:
  1. 一般要求一个集群中,所有节点的配置信息是一致的,比如Kafka集群。
  2. 对配置文件修改后,希望能够快速同步到各个节点上
  3. 实现:配置信息写入到Zookeeper一个节点中,客户端监听这个节点即可

在这里插入图片描述

统一集群管理

  • 场景:
  1. 分布式环境,实时掌握每个节点状态是必要的
  2. 实现: 节点信息写入ZooKeeper_上的一个ZNode。客户端监听这个ZNode可获取它的实时状态变化

在这里插入图片描述

服务器节点动态上下线

理解

特点: 客户端能实时洞察到服务器上下线的变化

在这里插入图片描述

实现

前提: 运行代码前自行在Zookeeper客户端创建/service节点【create /service “service”】,因为zookeeper创建子节点前必须有父节点,否则创建子节点失败

在这里插入图片描述

模拟服务提供者【客户端代码】-注册服务
public class ServiceProviderZkClient {

    private static String connecting = StrUtil.join(StrUtil.COMMA,"192.168.19.107:2181","192.168.19.108:2181","192.168.19.109:2181");
    private static Integer timeout = 2000;

    @SneakyThrows
    public static void main(String[] args) {








        Arrays.asList("application1","application2","application3").stream()
                .parallel()
                .forEach(applicationName -> {

                    serviceRegister(applicationName);

                });


    }

    @SneakyThrows
    public static void serviceRegister(String applicationName) {

        ZooKeeper zooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {
            public void process(WatchedEvent event) {
                Console.log("服务端推送的监听信息:{}", event);
            }
        });

        String zookeeperPath = StrUtil.format("/service/{}", applicationName);
        byte[] zookeeperPathData = Convert.toPrimitiveByteArray(StrUtil.format("{}应用的IP地址等信息", applicationName));


        String newNodePath = zooKeeper.create(zookeeperPath, zookeeperPathData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);


        Console.log("【{}】在线" , applicationName);


        //程序永远不结束
        while (true) {
            try {
                Thread.sleep(1000); // 暂停1秒钟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

模拟服务消费者【客户端代码】-获取服务信息进行请求消费
public class ServiceProviderConsumerZkClient {

    private static String connecting = StrUtil.join(StrUtil.COMMA,"192.168.19.107:2181","192.168.19.108:2181","192.168.19.109:2181");
    private static Integer timeout = 2000;

    private static AtomicReference children = new AtomicReference(ListUtil.of());
    private static ZooKeeper zooKeeper = null;

    @SneakyThrows
    public static void main(String[] args) {



        zooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {
            public void process(WatchedEvent event) {
                Console.log("服务端推送的监听信息:{}", event);

                //每次收到监听通知消息,同步服务在线状态
                getServiceNode();
            }
        });


        //获取在线中的提供提供者
        getServiceNode();


        while (true) {

            String targetServiceName = "application2";

            //在线的服务真实路径
            String targetServiceNodeName = CollUtil.emptyIfNull((List<String>)children.get())
                    .stream()
                    .filter(childrenPath -> StrUtil.contains(childrenPath, targetServiceName))
                    .findFirst()
                    .orElse(null);
            String targetServiceNamePath = StrUtil.format("/service/{}" , targetServiceNodeName);
            boolean targetServiceNameExistFlag = StrUtil.isNotBlank(targetServiceNodeName);

            if(targetServiceNameExistFlag) {

                //获取服务的配置信息进行服务调用 == 节点里面一般包含当前服务提供者http,端口等等信息
                String nodeData = Convert.toStr(zooKeeper.getData(targetServiceNamePath, false, null));
                Console.log("【{}】第三方服务上线,调用接口成功", targetServiceName);
            }else {
                Console.log("【{}】第三方服务未上线,调用接口失败" , targetServiceName);
            }


            ThreadUtil.sleep(5000);
        }


    }

    @SneakyThrows
    public static void getServiceNode() {
        children.set(zooKeeper.getChildren("/service", true));
        Console.log("系统中的服务提供者节点:{}" , children.get());
    }

}

服务提供者进行注册服务时
在这里插入图片描述

服务消费者进行消费时
在这里插入图片描述

软负载均衡

特点: 在Zookeepert中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求

在这里插入图片描述

分布式锁

理解

概念: 分布式系统中能保证多个进程有序地进行访问临界资源的锁,拿到锁的进程才可以访问资源,否则一直排队等待锁

在这里插入图片描述

实现
public class DistributedLock {

    private static String connecting = StrUtil.join(StrUtil.COMMA, "192.168.19.107:2181", "192.168.19.108:2181", "192.168.19.109:2181");
    private static Integer timeout = 2000;
    private static ZooKeeper zooKeeper;
    private static String parentPath = "/DistributedLock";
    private static Map<String, CountDownLatch> threadIdToCountDownLatchMap = MapUtil.newSafeConcurrentHashMap();


    static {
        init();
    }

    @SneakyThrows
    private static void init() {
        zooKeeper = connectZooKeeper();

        // 创建锁父节点
        String realParentPath = zooKeeper.create(parentPath, Convert.toPrimitiveByteArray(parentPath), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // 监听父节点下子节点增删的变化
        // List<String> sonNodeNames = getParentSortedNodes(true);
    }

    @SneakyThrows
    private static ZooKeeper connectZooKeeper() {
        ZooKeeper connectedZooKeeper = new ZooKeeper(connecting, timeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                Console.log("zookeeper收到服务端监听通知消息:{}", event);
                String dealNodePath = event.getPath();
                Event.EventType eventType = event.getType();

                if (eventType == Event.EventType.NodeDeleted) {
                    String nodeBelongThreadId = StrUtil.subBefore(StrUtil.subAfter(dealNodePath, "/", true), "_", false);
                    Console.log("收到删除节点通知,释放线程等待 == {}", nodeBelongThreadId);
                    // 释放锁
                    CountDownLatch countDownLatch = threadIdToCountDownLatchMap.get(nodeBelongThreadId);
                    if (countDownLatch != null) {
                        countDownLatch.countDown();
                        threadIdToCountDownLatchMap.remove(nodeBelongThreadId);
                    }
                }

            }
        });
        return connectedZooKeeper;
    }

    @SneakyThrows
    public static List<String> getParentSortedNodes(Boolean watchFlag) {
        List<String> sonNodeNames = CollUtil.emptyIfNull(zooKeeper.getChildren(parentPath, true))
                .stream()
                .sorted(CompareUtil::compare)
                .collect(Collectors.toList());

        return sonNodeNames;
    }


    /**
     * 获取锁
     */
    @SneakyThrows
    public static void acquireLock() {

        // 当前锁的节点前缀
        String nodeNamePrefix = Thread.currentThread().getId() + "_";
        // 当前锁的节点完整领前缀
        String absolutenodeNamePathPrefix = StrUtil.format("{}/{}", parentPath, nodeNamePrefix);


        // 完整的前缀
        String realAbsolutenodeNamePath = zooKeeper.create(absolutenodeNamePathPrefix, Convert.toPrimitiveByteArray("absolutenodeNamePathPrefix"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);


        // 获取当前父路径下的所有节点
        List<String> sonNodeNames = getParentSortedNodes(false);
        if (CollUtil.size(sonNodeNames) == 1) { // 获取锁
            Console.log("【子元素为1】获取锁【{}】",realAbsolutenodeNamePath);
            return;
        } else {
            String firstNodeName = CollUtil.getFirst(sonNodeNames);

            if (StrUtil.startWith(firstNodeName, nodeNamePrefix)) {
                Console.log("【当前子节点在排序后序列为第一个】获取锁【{}】",realAbsolutenodeNamePath);
                return;
            } else {
                // 一直等待直到有机会获取锁
                CountDownLatch countDownLatch = new CountDownLatch(1);


                // 监听该节点的前一个节点的增删变化
                int currentNodeIndex = CollUtil.indexOf(sonNodeNames, nodeName -> StrUtil.endWith(realAbsolutenodeNamePath, nodeName));
                String previousNodeName = CollUtil.get(sonNodeNames, currentNodeIndex - 1);
                String previousNodePath = StrUtil.format("{}/{}", parentPath, previousNodeName);
                zooKeeper.getData(previousNodePath, true, null);
                threadIdToCountDownLatchMap.put(StrUtil.subBefore(previousNodeName,"_", false), countDownLatch);

                countDownLatch.await();
                Console.log("获取锁【{}】", realAbsolutenodeNamePath);
            }
        }

    }

    /**
     * 释放锁
     */
    @SneakyThrows
    public static void releaseLock() {
        String currentThreadId = Convert.toStr(Thread.currentThread().getId());

        // CountDownLatch countDownLatch = threadIdToCountDownLatchMap.get(currentThreadId);
        // if (ObjUtil.isNull(countDownLatch)) {
        //     Console.log("当前线程并没有等待锁的操作");
        //     return;
        // }

        // 当前锁的节点前缀
        String nodeNamePrefix = currentThreadId + "_";

        String realNodeName = getParentSortedNodes(false).stream()
                .filter(nodeName -> StrUtil.startWith(nodeName, nodeNamePrefix))
                .findFirst()
                .orElse(null);

        if (StrUtil.isBlank(realNodeName)) {
            Console.log("当前线程并未有获取锁的操作");
            return;
        }

        String completeNodePath = StrUtil.format("{}/{}", parentPath, realNodeName);

        zooKeeper.delete(completeNodePath, -1);
        Console.log("释放锁【{}】", completeNodePath);
    }


    public static void main(String[] args) {
        String s = StrUtil.subAfter("fsd/fdsfsdfds", "/", true);
        Console.log(s);
    }
}


public class App2Test {



    @Test
    @SneakyThrows
    public void test3() {

        Bean publicBean = new Bean();

        List<Thread> threadGroup = ListUtil.of();

        for (int i = 0; i < 10; i++) {

            Thread newThread = new Thread(() -> {
                DistributedLock.acquireLock();

                // 随机等待
                try {
                    Thread.sleep(RandomUtil.randomInt(1000, 3000));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                publicBean.num = ++publicBean.num;
                Console.log("【{}】:数+1处理 == {}", Thread.currentThread().getId(), publicBean.num);

                DistributedLock.releaseLock();
            });

            newThread.start();

            threadGroup.add(newThread);
        }


        for (Thread runThread : threadGroup) {
            // 等待线程运行完
            runThread.join();
        }


        Console.log("公共数据最终的结果:{}", publicBean.num);
        Assert.equals(publicBean.num, 10);

    }
}

在这里插入图片描述

生产集群安装N台机器合适

特点: 好处提高可靠性、坏处数据同步有延迟

案例(生产经验)
10台服务器:3个ZK
20台服务器:5个ZK
100台服务器:11个zk
200台服务器:11台zk

第三方基于zookeeper的包

curator

官网: https://curator.apache.org/docs/about

入门教程: https://curator.apache.org/docs/getting-started/

依赖
    <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>5.6.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>5.6.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-client -->
    <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-client</artifactId>
      <version>5.6.0</version>
    </dependency>
代码

发现: 运行代码可见curator的分布式锁的原理跟前面自己实现的逻辑差不多,都是通过增、删子节点,然后监控前一个节点被删释放锁的逻辑原理去做的

public class OtherTest {



    @Test
    @SneakyThrows
    public void test2() {

        String connectString = "192.168.19.107:2181,192.168.19.108:2181,192.168.19.109:2181";
        RetryOneTime retryOneTime = new RetryOneTime(2000);

        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectString, 60 * 1000 * 10, 15 * 1000 * 10, retryOneTime);
        curatorFramework.start();

        //获取某父目录旗下的亲儿子节点名字信息
        List<String> sonNodeNames = curatorFramework.getChildren().forPath("/");
        Console.log(sonNodeNames);

		
		// 分布式锁
        InterProcessMutex interProcessMutexLock = new InterProcessMutex(curatorFramework, "/CuratorLock");

        App2Test.Bean publicBean = new App2Test.Bean();
        List<Thread> threadGroup = ListUtil.of();
        for (int i = 0; i < 5; i++) {

            Thread newThread = new Thread(() -> {
                try {
                    interProcessMutexLock.acquire();

                    // 随机等待
                    try {
                        Thread.sleep(RandomUtil.randomInt(1000, 3000));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    publicBean.num = ++publicBean.num;
                    Console.log("【{}】:数+1处理 == {}", Thread.currentThread().getId(), publicBean.num);

                }catch (Exception e) {

                }finally {
                    try {
                        interProcessMutexLock.release();
                    }catch (Exception e) {

                    }
                }
            });

            newThread.start();

            threadGroup.add(newThread);
        }

        for (Thread runThread : threadGroup) {
            // 等待线程运行完
            runThread.join();
        }


        Console.log("公共数据最终的结果:{}", publicBean.num);
        Assert.equals(publicBean.num, 5);



    }

}

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

事务Transaction简写为tx的原因

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Transaction简写的由来 数据库事务Transaction通常被简写为tx。让人疑惑的是&#xff1a;这个单词本身没有字母x为何又将其简写成了tx呢&#xff1f; 第一种可能 Transac…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:背景设置)

设置组件的背景样式。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 background10 background(builder: CustomBuilder, options?: { align?: Alignment }) 设置组件背景。 系统能力&#xff1a; …

java 正则表达式介绍

Java正则表达式是一种强大的文本处理工具&#xff0c;它允许你进行模式匹配、搜索和文本操作。正则表达式提供了一种简洁、灵活的方式来处理字符串&#xff0c;可以用于各种应用场景&#xff0c;如数据验证、文本解析、搜索和替换等。 正则表达式的基础知识 正则表达式…

PaddleOCR 高精度文字识别:丰富多样的前沿算法 | 开源日报 No.187

PaddlePaddle/PaddleOCR Stars: 34.1k License: Apache-2.0 PaddleOCR 是一个丰富、领先和实用的 OCR 工具库&#xff0c;旨在帮助开发者训练更好的模型并将其应用到实际场景中。该项目具有以下特点和优势&#xff1a; 支持多种 OCR 相关前沿算法提供产业级特色模型 PP-OCR、…

CentOS系统上安装幻兽帕鲁/Palworld服务端的详细步骤是什么?

CentOS系统上安装幻兽帕鲁/Palworld服务端的详细步骤是什么&#xff1f; 首先&#xff0c;需要确认Docker是否已经安装。如果未安装&#xff0c;则需要进行安装。接下来&#xff0c;运行Docker容器。这一步是为了创建一个可以运行幻兽帕鲁服务端的环境。然后&#xff0c;在容器…

顺序表基础

⽬录 1. 课前准备 2. 顺序表概念及结构 3. 顺序表分类 4. 实现动态顺序表 正⽂开始 课前预备 1. 课程⽬标 C语⾔语法基础到数据结构与算法&#xff0c;前⾯已经掌握并具备了扎实的C语⾔基础&#xff0c;为什么要学习数据结构 课程&#xff1f;⸺通讯录项⽬ 2. 需要…

AGI概念与实现

AGI AGI&#xff08;Artificial General Intelligence&#xff09;&#xff0c;中文名为“通用人工智能”或“强人工智能”&#xff0c;是指通过机器学习和数据分析等技术&#xff0c;使计算机具有类似于人类的认知和学习能力的技术. 多模态的大模型 &#xff08;Multimodal…

CSS【详解】居中对齐 (水平居中 vs 垂直居中)

水平居中 内部块级元素的宽度要小于容器(父元素) 方案一&#xff1a;文本居中对齐&#xff08;内联元素&#xff09; 限制条件&#xff1a;仅用于内联元素 display:inline 和 display: inline-block; 给容器添加样式 text-align:center<!DOCTYPE html> <html lang&q…

Linux UnixODBC安装配置

配置 UnixODBC 梦之上关注IP属地: 香港 0.2322020.12.09 13:23:10字数 1,202阅读 5,447 麒麟&达梦适配系列: 1.麒麟服务器上安装 DM8 2.配置 UnixODBC 3.beego-ORM 适配达梦 资源紧张的时候&#xff0c;服务器是大家共用的&#xff0c;上面部署了一堆服务。所以选用doc…

【系统分析师】-系统配置与性能评价

1、性能指标 主频&#xff1a;又称时钟频率&#xff0c;1GHZ表示1秒有1G个时钟周期 1s10^9ns 主频外频 * 倍频 时钟周期 主频的倒数指令周期&#xff1a;取出并执行一条指令的时间 总线周期&#xff1a;一个访存储器或IO操作所用时间平均执行周期数&#xff1a;CPI表示…

电机应用-正点原子直流有刷电机例程笔记

目录 基础驱动实验&#xff1a;调速和换向 初始化工作 电机基础驱动API 电压、电流、温度检测实验 初始化工作 采集工作 编码器测速实验 编码器接口计数原理 初始化工作 编码器测速工作 速度环控制实现 PID相关函数 PID运算 电流环控制实现 PID相关函数 PID运算…

CryoEM - CryoAI: Amortized Inference of Poses 工程源码复现

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/136384544 Paper: CryoAI: Amortized Inference of Poses for Ab Initio Reconstruction of 3D Molecular Volumes from Real Cryo-EM Images CryoAI: 基于摊…

【python】双十一美妆数据分析可视化 [聚类分析/线性回归/支持向量机](代码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

初始Tomcat(Tomcat的基础介绍)

目录 一、Tomcat的基本介绍 1、Tomcat是什么&#xff1f; 2、Tomcat的配置文件详解 3、Tomcat的构成组件 4、Tomcat的顶层架构 5、Tomcat的核心功能 6、Tomcat的请求过程 一、Tomcat的基本介绍 1、Tomcat是什么&#xff1f; Tomcat 服务器是一个免费的开放源代码的Web …

基于CVX凸优化的电动汽车充放电调度matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 CVX凸优化 4.2 电动汽车充放电调度 5.完整程序 1.程序功能描述 基于CVX凸优化的电动汽车充放电调度.仿真输出无电动汽车充电时的负载&#xff0c;电动汽车充电时cvx全局优化求解后的总…

鸿蒙开发-UI-图形-绘制自定义图形

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 鸿蒙开发-UI-页面路由 鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 文章目录 前言 一、使用画布组件绘制自定义图形 1.初…

select,poll和epoll有什么区别

它们都是NIO中多路复用的三种实现机制&#xff0c;是由linux操作系统提供的。 用户空间和内核空间&#xff1a;操作系统为了保证系统安全&#xff0c;将内核分为两个部分&#xff0c;一个是用户空间&#xff0c;一个是内核空间。用户空间不能直接访问底层的硬件设备&#xff0…

SpringCloud-同步异步通讯比较

本文详细探讨了同步通讯和异步通讯在信息传递中的区别&#xff0c;以及它们分别带来的优势和不足。通过对支付流程的案例分析&#xff0c;突显了同步通讯可能面临的阻塞和服务依赖问题&#xff0c;而异步通讯通过引入事件驱动模式和消息代理&#xff08;Broker&#xff09;成功…

HarmonyOS-卡片事件能力说明

卡片事件能力说明 ArkTS卡片中提供了postCardAction()接口用于卡片内部和提供方应用间的交互&#xff0c;当前支持router、message和call三种类型的事件&#xff0c;仅在卡片中可以调用。 接口定义&#xff1a;postCardAction(component: Object, action: Object): void 接口…

QPS 提升 10 倍!滴滴借助 StarRocks 物化视图实现低成本精确去重

作者&#xff1a;滴滴 OLAP 开发工程师 刘雨飞 小编导读&#xff1a; 滴滴于 2022 年引入了 StarRocks。经过一年多的努力&#xff0c;StarRocks 逐渐替代了原有技术栈&#xff0c;成为滴滴内部主要的 OLAP 引擎。截至 2023 年 12 月&#xff0c;滴滴已经成功建立了超过 40 个 …