zookeeper

目录

1.软件架构的发展

2.了解zookeeper

2.1概述

2.2zookeeper的应用场景

2.3安装zookeeper

2.4zookeeper客户端命令

3.zookeeper简单操作

3.1zookeeper的数据结构

3.2节点的分类

3.3java代码操作zookeeper节点

3.4zookeeper的watch机制

3.4.1介绍

3.4.2NodeCache

3.4.3PathChildrenCache

3.4.4TreeCache

4.集群

4.1了解zookeeper集群

4.2架构图

4.3搭建集群

4.4leader选举


1.软件架构的发展

        软件架构的发展经历了由单体架构、垂直架构、分布式架构到流动计算架构的演进过程,如下图:

单一应用架构

        当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

垂直应用架构

        当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

分布式服务架构

        当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构

        当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

什么是SOA

        SOA全称为Service-OrientedArchitecture,即面向服务的架构。它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。一个服务通常以独立的形式存在于操作系统进程中。

        站在功能的角度,把业务逻辑抽象成可复用、可组装的服务,通过服务的编排实现业务的快速再生,目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。

        通过上面的描述可以发现SOA有如下几个特点:分布式、可重用、扩展灵活、松耦合

原来的单体项目如何改为SOA架构?

        原来的单体工程项目大多分为三层:表现层(Controller)、业务层(Service)、持久层(Dao),要改为SOA架构,其实就是将业务层提取为服务并且独立部署即可,表现层通过网络和业务层进行通信,如下图:

2.了解zookeeper

2.1概述

        ZooKeeper从字面意思理解,【Zoo - 动物园,Keeper - 管理员】动物园中有很多种动物,这里的动物就可以比作分布式环境下多种多样的服务,而ZooKeeper做的就是管理这些服务。 Apache ZooKeeper的系统为分布式协调是构建分布式应用的高性能服务。 ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。 ZooKeeper 适用于存储和协同相关的关键数据,不适合用于大数据量存储。

2.2zookeeper的应用场景

注册中心

        分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。通过调用Zookeeper提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。 阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。

配置中心

        数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态获取数据的目的。

ZooKeeper 采用的是推拉结合的方式。

1、推push: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知。

2、拉pull: 客户端获得通知后,然后主动到服务端拉取最新的数据。

分布式锁

        分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

分布式队列

        在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。

负载均衡

        负载均衡是通过负载均衡算法,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。

上图中左侧为ZooKeeper集群,右侧上方为工作服务器,下面为客户端。每台工作服务器在启动时都会去ZooKeeper的servers节点下注册临时节点,每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接。

2.3安装zookeeper

下载地址:http://zookeeper.apache.org

1.安装完成后在zookeeper路径下创建一个data文件夹,用于存放数据。

2.conf路径中复制一份zoo_sample.cfg,改名为 zoo.cfg

3.zoo.cfg配置文件中指定保存数据的目录:data目录

4.启动zookeeper,打开bin目录,双击zkServer.cmd

5.启动客户端,bin目录下,双击zkCli.cmd

2.4zookeeper客户端命令

输入help命令即可查看:

3.zookeeper简单操作

3.1zookeeper的数据结构

        ZooKeeper 的数据模型是层次模型。层次模型常见于文件系统。例如:我的电脑可以分为多个盘符(例如C、D、E等),每个盘符下可以创建多个目录,每个目录下面可以创建文件,也可以创建子目录,最终构成了一个树型结构。通过这种树型结构的目录,我们可以将文件分门别类的进行存放,方便我们后期查找。而且磁盘上的每个文件都有一个唯一的访问路径。

        层次模型和key-value 模型是两种主流的数据模型。ZooKeeper 使用文件系统模型主要基于以下两 点考虑:

  1. 文件系统的树形结构便于表达数据之间的层次关系。

  2. 文件系统的树形结构便于为不同的应用分配独立的命名空间(namespace)。

        ZooKeeper 的层次模型称作data tree。Datatree 的每个节点叫作znode(Zookeeper node)。不同于文件系统,每个节点都可以保存数据。每个节点都有一个版本(version)。版本从0 开始计数。

        如图所示,data tree中有两个子树,用于应用1( /app1)和应用2(/app2)。每个客户端进程pi 创建一个znode节点 p_i 在 /app1下, /app1/p_1就代表一个客户端在运行。

3.2节点的分类

        持久节点和临时节点:一个znode可以是持久性的,也可以是临时性的。

  1. 持久性的znode[PERSISTENT],这个znode一旦创建不会丢失,无论是zookeeper宕机,还是client宕机。

  2. 临时性的znode[EPHEMERAL],如果zookeeper宕机了,或者client在指定的timeout时间(5s)内没有连接server,都会被认为丢失。

        顺序节点:znode也可以是顺序性的,每一个顺序性的znode关联一个唯一的单调递增整数。这个单调递增整数是znode名字的后缀。

  1. 持久顺序性的znode(PERSISTENT_SEQUENTIAL):znode 处理具备持久性的znode的特点之外,znode的名称具备顺序性

  2. 临时顺序性的znode(EPHEMERAL_SEQUENTIAL):znode处理具备临时性的znode特点,znode的名称具备顺序性。

3.3java代码操作zookeeper节点

导入依赖

 <!--zookeeper的依赖-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.7</version>
</dependency>
<!--zookeeper CuratorFramework 
是Netflix公司开发一款连接zookeeper服务的框架,
通过封装的一套高级API 简化了ZooKeeper的操作,
提供了比较全面的功能,除了基础的节点的操作,
节点的监听,还有集群的连接以及重试。-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator­framework</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator­recipes</artifactId>
    <version>4.0.0</version>
</dependency>
<!--测试-->
<dependency>
   <groupId>junit</groupId>  
   <artifactId>junit</artifactId>  
   <version>4.12</version>  
</dependency>

重试策略

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
//RetryPolicy:失败的重试策略公共接口.
//参数1:初始化的sleep时间,用于计算之后的每次重试的sleep时间.
//参数2:最大的重试次数.

创建客户端

CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181",3000,1000,retryPolicy);
//参数1:连接zookeeper服务器的IP端口.
//参数2:会话超时毫秒数.
//参数3:连接超时毫秒数.
//参数4:重试策略.

开启客户端

client.start();
//会阻塞到会话连接成功为止.

创建节点

//1.创建一个空节点(a)(只能创建一层节点)
client.create().forPath("/a"); // 此节点默认会有一个ip值

//2.创建一个有内容的b节点(只能创建一层节点)
client.create().forPath("/b", "hello zookeeper".getBytes());

//3.创建多层节点
client.create().creatingParentsIfNeeded().  //是否需要递归创建节点
       withMode(CreateMode.PERSISTENT).  //创建持久性节点
       forPath("/c1/c2/c3");

//4.创建带有的序号的节点
client.create().creatingParentsIfNeeded().  //是否需要递归创建节点
       withMode(CreateMode.PERSISTENT_SEQUENTIAL). //创建持久并带有序号的节点
       forPath("/d");

//5.创建临时节点(客户端关闭,节点消失;客户端不关闭,默认5秒后节点消失)
client.create().creatingParentsIfNeeded().
       withMode(CreateMode.EPHEMERAL).    //创建临时节点
       forPath("/e");

//6.创建临时带序号节点(客户端关闭,节点消失;客户端不关闭,默认5秒后节点消失)
client.create().creatingParentsIfNeeded().
       withMode(CreateMode.EPHEMERAL_SEQUENTIAL).  //创建临时带序号节点
       forPath("/f");

修改节点内容

client.setData().forPath("/a", "hello zookeeper".getBytes());

获取节点内容

byte[] bytes = client.getData().forPath("/a");

删除节点

//删除单层节点 若c1节点下有其他节点,则会产生异常
client.delete().forPath("/c1");

//删除节点并递归删除其子节点 若c1节点下有其他节点,则会一并删除
client.delete().deletingChildrenIfNeeded().forPath("/c1");

关闭客户端

client.close();

3.4zookeeper的watch机制

3.4.1介绍

        zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。所谓订阅发布功能,其实说白了就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。

        zookeeper的订阅发布也就是watch机制,是一个轻量级的设计。因为它采用了一种推拉结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的“推”部分。然后,收到变更通知的客户端需要自己去拉变更的数据,这就是“拉”部分。watche机制分为添加数据和监听节点。

        Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。

        Curator中的cache共有三种

  • NodeCache 只监测当前节点

  • PathChildrenCache 只检测自己的子节点(不包含孙子节点)

  • TreeCache 监测当前节点和所有的子节点(包含孙子节点)

3.4.2NodeCache

        //创建重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);

        //创建客户端
        CuratorFramework client =CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000,retryPolicy);

        //开启客户端
        client.start();
        System.out.println("连接成功");

        //创建节点数据监听对象
        final NodeCache nodeCache = new NodeCache(client, "/hello");

        //开始缓存
        /**
         * 参数为true:可以直接获取监听的节点
         * 参数为false:不可以获取监听的节点
         */
        nodeCache.start(true);
        System.out.println(nodeCache.getCurrentData());

        //添加监听对象
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            //如果节点数据有变化,会回调该方法
            public void nodeChanged() throws Exception {
                String data = new String(nodeCache.getCurrentData().getData());
                System.out.println("数据Watcher:路径=" + nodeCache.getCurrentData().getPath() + ":data=" + data);
            }
        });

3.4.3PathChildrenCache

        //创建重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
        
        //创建客户端
        CuratorFramework client =CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000,retryPolicy);
        
        //开启客户端
        client.start();
        
        //监听指定节点的子节点变化情况包括hello新增子节点 子节点数据变更 和子节点删除
        PathChildrenCache childrenCache = new PathChildrenCache(client,"/hello",true);
        
        /**
          * NORMAL:  普通启动方式, 在启动时缓存子节点数据
          * POST_INITIALIZED_EVENT:在启动时缓存子节点数据,提示初始化
          * BUILD_INITIAL_CACHE: 在启动时什么都不会输出
          */
        childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        
        //添加监听
        childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                if(event.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED){
                    System.out.println(event.getData().getPath()+"子节点添加:"+event.getData().getData());
                 }else if (event.getType() ==PathChildrenCacheEvent.Type.CHILD_REMOVED){
                    System.out.println(event.getData().getPath()+"子节点移除:"+event.getData().getData());
                }else if(event.getType() ==PathChildrenCacheEvent.Type.CHILD_UPDATED){
                    System.out.println(event.getData().getPath()+"子节点修改:"+event.getData().getData());
                }else if(event.getType() == PathChildrenCacheEvent.Type.INITIALIZED){
                    System.out.println("初始化完成");
                }else if(event.getType()==PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED){
                    System.out.println("连接过时");
                }else if(event.getType()==PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED){
                    System.out.println("重新连接");
                }else if(event.getType()==PathChildrenCacheEvent.Type.CONNECTION_LOST){
                    System.out.println("连接过时一段时间");
                }
            }
        });

3.4.4TreeCache

        //创建重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,1);
        
        //创建客户端
        CuratorFramework client =CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000,retryPolicy);
        
        //开启客户端
        client.start();
        
        //监听指定节点的所有子节点变化情况
        TreeCache treeCache = new TreeCache(client,"/hello");
        treeCache.start();
        System.out.println(treeCache.getCurrentData("/hello"));
        
        //配置监听器
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                if(event.getType() == TreeCacheEvent.Type.NODE_ADDED){
                    System.out.println(event.getData().getPath() + "节点添加");
                }else if (event.getType() == TreeCacheEvent.Type.NODE_REMOVED){
                    System.out.println(event.getData().getPath() + "节点移除");
                }else if(event.getType() == TreeCacheEvent.Type.NODE_UPDATED){
                    System.out.println(event.getData().getPath() + "节点修改");
                }else if(event.getType() == TreeCacheEvent.Type.INITIALIZED){
                    System.out.println("初始化完成");
                }else if(event.getType()==TreeCacheEvent.Type.CONNECTION_SUSPENDED){
                    System.out.println("连接过时");
                }else if(event.getType()==TreeCacheEvent.Type.CONNECTION_RECONNECTED){
                    System.out.println("重新连接");
                }else if(event.getType() ==TreeCacheEvent.Type.CONNECTION_LOST){
                    System.out.println("连接过时一段时间");
                }
            }
        });

4.集群

4.1了解zookeeper集群

集群介绍

        Zookeeper 集群搭建指的是 ZooKeeper 分布式模式安装。 通常由 2n+1台 servers 组成。 这是因为为了保证 Leader 选举(基于 Paxos 算法的实现) 能过得到多数的支持,所以 ZooKeeper 集群的数量一般为奇数。

集群模式

ZooKeeper集群搭建有两种方式:

  • 伪分布式集群搭建:

    所谓伪分布式集群搭建ZooKeeper集群其实就是在一台机器上启动多个ZooKeeper,在启动每个ZooKeeper时分别使用不同的配置文件zoo.cfg来启动,每个配置文件使用不同的配置参数(clientPort端口号、dataDir数据目录.在zoo.cfg中配置多个server.id,其中ip都是当前机器,而端口各不相同,启动时就是伪集群模式了。这种模式和单机模式产生的问题是一样的。也是用在测试环境中使用。

  • 完全分布式集群搭建:

    多台机器各自配置zoo.cfg文件,将各自互相加入服务器列表,每个节点占用一台机器

4.2架构图

Leader:Zookeeper(领导者): 集群工作的核心 事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性; 集群内部各个服务器的调度者。 对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

Follower(跟随者)处理客户端非事务(读操作) 请求,转发事务请求给 Leader;参与集群 Leader 选举投票 2n-1台可以做集群投票。

Observer:观察者角色 针对访问量比较大的 zookeeper 集群, 还可新增观察者角色。观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。 不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

4.3搭建集群

1.准备3台虚拟机

        虚拟机上需要安装jdk环境。

        虚拟机的网络连接方式使用桥接模式,因为三台服务器之间需要进行通信。

        关闭防火墙。

2.安装zookeeper

        命令:cd usr/local (进入local路径)
        命令:tar -zxvf apache-zookeeper-3.5.5.bin.tar.gz -C /usr/local(解压)
        命令:mv apache-zookeeper-3.5.5.bin zookeeper (重命名文件夹)

3.配置zookeeper

        (1)创建data目录。

        命令:cd zookeeper (进入zookeeper目录)
        命令:mkdir data

        (2)设置conf/zoo.cfg

        命令:cd conf (进入conf目录)
        命令:cp zoo_sample.cfg zoo.cfg(复制zoo_sample.cfg,文件名为zoo.cfg)

        (3)修改zoo.cfg

#zookeeper保存数据的位置

dataDir=/usr/local/zookeeper/data

#当前zookeeper端口号

clientPort=2181

#集群机器配置
server.1=192.168.174.128:2182:3182
server.2=192.168.174.129:2182:3182
server.3=192.168.174.130:2182:3182

2182:端口号是自己定义的,用来选举领导。

3182:端口号是自己定义的,用来服务器之间通信。

        (4) 在data目录创建文件myid

        命令:cd .. (退出conf目录)
        命令: cd data (进入data目录)
        命令:vim myid(修改myid文件,分别设置为1,2,3)或使用 cat 1 >> myid 命令

4.启动zookeeper

        命令:cd .. (退出data目录)
        命令:cd bin(进入bin路径)
        命令: ./zkServer.sh start

5.查看zookeeper状态

        命令:./zkServer.sh status

4.4leader选举

        在领导者选举的过程中,如果某台ZooKeeper获得了超过半数的选票,则此ZooKeeper就可以成为Leader了。

(1)服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。

(2)服务器2启动,给自己投票,同时与之前启动的服务器1交换结果, 每个Server发出一个投票由于是初始情况,1和2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时1的投票为(1,0),2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。 接受来自各个服务器的投票集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器

处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下

  • 优先检查ZXID。ZXID比较大的服务器优先作为Leader。

  • 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器

由于服务器2的编号大,更新自己的投票为(2, 1),然后重新投票,对于2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可,此时集群节点状态为LOOKING。 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息

(3)服务器3启动,进行统计后,判断是否已经有过半机器接受到相同的投票信息,对于1、2、3而言,已统计出集群中已经有3台机器接受了(3, 0)的投票信息,此时投票数正好大于半数,便认为已经选出了Leader,

改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING所以服务器3成为领导者,服务器1,2成为从节点。

ZXID: 即zookeeper事务id号。ZooKeeper状态的每一次改变, 都对应着一个递增的Transactionid, 该id称为zxid

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

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

相关文章

ERD Online 4.0.11 在线数据库建模、元数据协作平台(免费、私有部署)

ERD Online 是全球第一个开源、免费在线数据建模、元数据管理平台。提供简单易用的元数据设计、关系图设计、SQL查询等功能&#xff0c;辅以版本、导入、导出、数据源、SQL解析、审计、团队协作等功能、方便我们快速、安全的管理数据库中的元数据。 4.0.11 ❝ :memo: fix(erd):…

5亿融资与重磅新品双发布,杉数以智能决策技术变革中国产业运营模式

2023年3月30日&#xff0c;由杉数科技举办的“智能决策重塑增长”2023杉数科技智能决策前沿峰会在北京举行。会上发布了杉数新一轮融资消息&#xff0c;同时&#xff0c;面向零售快消的决策优化产品计划宇宙&#xff08;Planiverse&#xff09;与面向工业制造的决策优化产品数弈…

Flink (四) --------- Flink 运行时架构

目录一、系统架构1. 整体构成2. 作业管理器&#xff08;JobManager&#xff09;3. 任务管理器&#xff08;TaskManager&#xff09;二、作业提交流程1. 高层级抽象视角2. 独立模式&#xff08;Standalone&#xff09;3. YARN 集群三、 一些重要概念1. 数据流图&#xff08;Data…

C的实用笔记36——几种常用的字符串处理API(一)

0、const关键字 1、知识点&#xff1a;const是与存储相关的关键字&#xff0c;用作常量声明&#xff0c;修饰普通变量和指针变量&#xff0c;表示只读。const修饰普通变量&#xff1a;&#xff0c;修饰后变量从可修改的左值变成不可修改的左值 const修饰指针变量&#xff1a;分…

redis源码解析(四)——ziplist

版本&#xff1a;redis - 5.0.4 参考资料&#xff1a;redis设计与实现 文件&#xff1a;src下的ziplist.c ziplist.h 一、基础知识1、压缩列表的各个组成部分及详细说明2、列表节点3、encoding二、连锁更新三、ziplist.hquickList一、基础知识 压缩列表是Redis为了节约内存而开…

陌生人社交软件如何破冰?

据艾媒咨询的数据显示&#xff0c;2020年中国移动社交用户规模已达9.24亿人&#xff0c;预计2022年中国移动社交用户整体突破10亿人。而早在2020年&#xff0c;我国陌生人社交用户规模已经达到了6.49亿人&#xff0c;虽然增速有所放缓&#xff0c;但整体规模还是较为庞大。 艾媒…

操作系统笔记——进程管理

操作系统笔记——进程管理2. 进程管理2.1 进程与线程2.1.1 进程的引入前趋图程序的顺序执行程序的并发执行2.1.2 进程的定义及描述进程的定义进程的特征进程和程序的关系进程与作业的区别进程的组成2.1.3 进程的状态与转换进程的5种基本状态进程的状态的相互转换2.1.4 进程的控…

java常见锁策略分享(包括cas和synchronized的优化)

前言 锁策略学习思维导图: 1.常见锁策略 ① 乐观锁和悲观锁 ● 它们是根据锁冲突的预测,如果预测锁冲突比较小,那就是乐观锁,反之,就是悲观锁. ● 举个例子:高考前夕,我总觉得高考题会很难,然后拼命做各种科目的题,全副武装的去应对高考,而我妈则觉得高考只是人生的一个阶段而…

PCB模块化设计04——USB-Type-C PCB布局布线设计规范

目录PCB模块化设计04——USB-Type-C PCB布局布线设计规范USB Type-C功能介绍信号图示Type-C接口引脚定义USB 2.0差分对电源和接地引脚RX和TX引脚CC1和CC2针脚VCONN引脚SBU1和SBU2针脚USB供电PCB设计布线要求PCB模块化设计04——USB-Type-C PCB布局布线设计规范 USB Type-C US…

STC的官网,是我永远忘不掉的炼丹炉

搞电子的&#xff0c;应该都搞过8051搞8051的&#xff0c;那应该都搞过STC在国内&#xff0c;STC已经成为了8051的代名词http://www.stcmcudata.com/如果你刚开始搞嵌入式&#xff0c;应该学单片机&#xff0c;你学习单片机&#xff0c;就应该学习下8051&#xff0c;学习8051&a…

Python+Pygame实现简单的单词小游戏

语言是一种艺术&#xff0c;但是作为语言的基础——词汇&#xff0c;却不像艺术那样赏心悦目。不断的记忆与复习&#xff0c;让词汇成为很多孩子在学习英语时&#xff0c;最难完全攻克的关卡。本文就来用Python制作一个简单的英语单词游戏吧 前言 语言是一种艺术&#xff0c;但…

【ArcGIS Pro二次开发】(17):打开GDB、SHP、CAD等各种数据

一、打开GDB数据库 // 输入一个数据库路径string gdbPath "C:\Users\Administrator\Documents\ArcGIS\Projects\Test\Test.gdb";await QueuedTask.Run(() >{// 如果文件夹存在并且包含有效的地理数据库&#xff0c;则会打开地理数据库。using (Geodatabase geoda…

【单片机/普中A2】学习笔记1-配置环境与STC-ISP烧录

目录前言连接到开发板micro-usb 测试安装串口驱动烧写准备源码烧录前言 目前我们的开发需求很简单&#xff0c;仅需三个软件&#xff1a; keli5 编写代码proteus8 professional 描绘电路板STC-ISP 串口烧录 具体教程在 CSDN 等博客平台上已经有很多&#xff0c;这里就不再赘述…

(排序2)希尔排序

写希尔排序注意&#xff1a; 写新元素融入有序数组的过程(end&tmp)将这个过程给多次类比到需要排序的一串数据中 (for&while)排完一组不够&#xff0c;需要排gap组 (再来for)敲定gap下标关系&#xff1a; 希尔排序与直接插入排序的区别与联系 希尔排序的话也叫做缩小…

刷题笔记【3】| 快速刷完67道剑指offer(Java版)

本文已收录于专栏&#x1f33b;《刷题笔记》文章目录前言&#x1f3a8; 1、斐波那契数列题目描述思路一&#xff08;递归&#xff09;思路二&#xff08;循环&#xff09;&#x1f3a8; 2、跳台阶题目描述思路一&#xff08;递归&#xff09;思路二&#xff08;循环&#xff09…

03-03 周五 镜像安装sshd和jupyter以及修改密码

03-03 周五 镜像安装sshd和jupyter以及修改密码时间版本修改人描述2023年3月3日15:34:49V0.1宋全恒新建文档 简介 由于在镜像中需要进行jupyter和sshd的安装&#xff0c;并且需要进行密码的修改&#xff0c;因此在该文档中记录了这两个交互方式的工程设计。 在线加密 在线加密…

Pycharm创建自定义代码片段

简介 PyCharm允许您创建自定义代码片段&#xff0c;也称为代码模板&#xff0c;以提高您的开发效率 实现步骤 1.添加代码模板 打开PyCharm并导航到File->Settings&#xff0c;或者按快捷键ctrl alt s 打开设置 ​ 按照如下序号步骤进行点击&#xff0c;点击“”按钮以…

基于canvas画布的实用类Fabric.js的使用Part.3

目录一、基于canvas画布的实用类Fabric.js的使用Part.1Fabric.js简介 开始方法事件canvas常用属性对象属性图层层级操作复制和粘贴二、基于canvas画布的实用类Fabric.js的使用Part.2锁定拖拽和缩放画布分组动画图像滤镜渐变右键菜单删除三、基于canvas画布的实用类Fabric.js的使…

gcc在Linux下如何运行一个C/C++程序

安装gcc&#xff1a;sudo apt-get install gcc&#xff08;之后输入密码即可&#xff09; 绝对路径的方式进入usr目录&#xff1a; cd /home /home/&#xff1a;是普通用户的主目录&#xff0c;在创建用户时&#xff0c;每个用户要有一个默认登录和保存自己数据的位置&#x…

【数据结构刷题集】链表经典习题

&#x1f63d;PREFACE&#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐ 评论&#x1f4dd;&#x1f4e2;系列专栏&#xff1a;数据结构刷题集&#x1f50a;本专栏涉及到题目是数据结构专栏的补充与应用&#xff0c;只更新相关题目&#xff0c;旨在帮助提高代码熟练度&#x…