【流行框架】Zookeeper

news2024/10/4 19:25:02

🌟个人博客:www.hellocode.top🌟
🌟Java知识导航:Java-Navigate🌟
⭐想获得更好的阅读体验请前往Java-Navigate
🔥本文专栏:《流行框架》
🌞如没有JavaWEB基础,请先前往《Java Web从入门到实战》专栏学习相应知识
⚡如有问题,欢迎指正,一起学习~~


文章目录

    • Zookeeper
      • 概述
      • 命令操作
        • 数据模型
        • 服务端
        • 客户端
      • JavaAPI操作
        • Curator
        • Watch监听
      • 分布式锁
        • 概述
        • 原理
        • 售票案例
      • 集群
        • 搭建
        • 故障测试
        • 集群角色


Zookeeper

概述

  • Zookeeper是Apache Hadoop项目下的一个子项目,是一个树形目录服务
  • Zookeeper翻译过来就是 动物管理员,他是用来管理Hadoop(大象)、Hive(蜜蜂)、Pig(小猪)的管理员。简称zk
  • Zookeeper是一个分布式的、开源的分布式应用程序的协调服务
  • Zookeeper提供的主要功能包括
    • 配置管理
    • 分布式锁
    • 集群管理

安装与启动参看Dubbo注册中心部分

命令操作

数据模型

  • Zookeeper是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构
  • 这里面的每一个节点都被称为:ZNode,每个节点上都会保存自己的数据和节点信息
  • 节点可以拥有子节点,同时也允许少量(1MB)数据存储在该节点之下

节点可以分为四大类

  • PERSISTENT 持久化节点
  • EPHEMERAL 临时节点:-e
  • PERSISTENT_SEQUENTIAL 持久化顺序节点:-s
  • EPHEMERAL_SEQUENTIAL 临时顺序节点:-es

服务端

  • 启动ZooKeeper服务:./zkServer.sh start
  • 查看ZooKeeper服务状态:./zkServer.sh status
  • 停止ZooKeeper服务:./zkServer.sh stop
  • 重启ZooKeeper服务:./zkServer.sh restart

客户端

基本CRUD

  • 连接ZooKeeper服务端:./zkCli.sh -server ip:2181(连接本机的话不需要-server ip:端口
  • 断开连接:quit
  • 查看指定目录下的子节点:ls / ls /dubbo/config
  • 查看命令帮助:help
  • 创建节点:create /节点名 [节点数据]
  • 获取数据:get /节点名
  • 设置数据:set /节点名 数据
  • 删除节点:delete /节点名

当被删节点含有子节点时,不能使用delete删除,需要使用deleteall命令

临时顺序节点

  • 创建临时节点:create -e /节点名
  • 创建顺序节点:create -s /节点名
  • 创建临时顺序节点: create -es /节点名
  • 查看节点详细信息:ls2 /路径(已经被淘汰,替换为:)ls -s /路径
    • czxid:节点被创建的事务id
    • ctime:创建时间
    • mzxid:最后一次被更新的事务id
    • mtime:修改时间
    • pzxid:子节点列表最后一次被更新的事务id
    • cversion:子节点的版本号
    • dataversion:数据版本号
    • aclversion:权限版本号
    • ephemeralOwner:用于临时节点,代表临时节点的事务id,如果为持久节点则为0
    • dataLength:节点存储的数据长度
    • numChildrean:当前节点的子节点个数

JavaAPI操作

Curator

简介

  • Curator 是 Apache ZooKeeper 的Java客户端库

  • 常见的ZooKeeper Java API

    • 原生Java API
    • ZkClient
    • Curator
  • Curator项目的目标是简化ZooKeeper客户端的使用

  • Curator 最初是Netfix 研发的,后来捐献了Apache基金会,目前是Apache的顶级项目

  • 官网:http://curator.apache.org/

常用操作

  • 建立连接
  • 添加节点
  • 删除节点
  • 修改节点
  • 查询节点
  • Watch事件监听
  • 分布式锁实现
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <!--curator-->
    <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>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

建立连接

@Test
public void testConnect(){
    /*
            connectString 连接字符串。zk server 地址和端口(多个用逗号隔开) "192.168.23.129:2181,192.131.34.168:2181"
            sessionTimeoutMs 会话超时时间  单位ms
            connectionTimeoutMs 连接超时时间 单位ms
            retryPolicy 重试策略
         */
    // 第一种方式
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
    // CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.23.129:2181",
                                                                60 * 1000, 15 * 1000, retryPolicy);
    // 开启连接
    // client.start();

    // 第二种方式
    CuratorFramework client = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181")
        .sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("hellocode").build();
    client.start();
}

第二种方式指定namespace可以简化操作,让指定的名称空间作为根目录

添加节点

public class CuratorTest {
    private CuratorFramework client;
    @Before
    public void testConnect(){
        /*
            connectString 连接字符串。zk server 地址和端口(多个用逗号隔开) "192.168.23.129:2181,192.131.34.168:2181"
            sessionTimeoutMs 会话超时时间  单位ms
            connectionTimeoutMs 连接超时时间 单位ms
            retryPolicy 重试策略
         */
        // 第一种方式
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
//        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.23.129:2181",
//                60 * 1000, 15 * 1000, retryPolicy);
//        // 开启连接
//        client.start();

        // 第二种方式
        client = CuratorFrameworkFactory.builder().connectString("192.168.23.129:2181")
                .sessionTimeoutMs(60 * 1000).connectionTimeoutMs(15 * 1000).retryPolicy(retryPolicy).namespace("hellocode").build();
        client.start();
    }

    /**
     * 创建节点:create 持久 临时 顺序
     * 1. 基本创建:create().forPath("")
     * 2. 创建节点 带有数据:create().forPath("",data)
     * 3. 设置节点的类型:create().withMode(节点类型).forPath("",data)
     * 4,创建多级节点:create().withMode(节点类型).creatingParentsIfNeeded().forPath("",data)
     */
    @Test
    public void testCreate() throws Exception {
        // 1. 基本创建:create().forPath("")
        // 如果创建节点没有指定数据,则默认当前客户端的ip作为数据存储
        String path = client.create().forPath("/app1");
        System.out.println(path);
    }
    @Test
    public void testCreate2() throws Exception {
        // 2. 创建节点 带有数据:create().forPath("",data)
        // 如果创建节点没有指定数据,则默认当前客户端的ip作为数据存储
        String path = client.create().forPath("/app2","Hello World".getBytes());
        System.out.println(path);
    }
    @Test
    public void testCreate3() throws Exception {
        // 3. 设置节点的类型:create().withMode(节点类型).forPath("",data)
        // 如果创建节点没有指定数据,则默认当前客户端的ip作为数据存储
        String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
        System.out.println(path);
    }
    @Test
    public void testCreate4() throws Exception {
        // 4,创建多级节点:create().withMode(节点类型).creatingParentsIfNeeded().forPath("",data)
        // 如果创建节点没有指定数据,则默认当前客户端的ip作为数据存储
        String path = client.create().creatingParentsIfNeeded().forPath("/app3/p1");
        System.out.println(path);
    }

    @After
    public void close(){
        if(client != null){
            client.close();
        }
    }
}

查询节点

/**
     * 查询节点:
     * 1. 查询数据:get:getData().forPath()
     * 2. 查询子节点:ls:getChildren().forPath()
     * 3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()
*/
@Test
public void testGet1() throws Exception {
    // 1. 查询数据:get:getData().forPath()
    byte[] bytes = client.getData().forPath("/app2");
    System.out.println(new String(bytes));
}

@Test
public void testGet2() throws Exception {
    // 2. 查询子节点:ls:getChildren().forPath()
    List<String> childrens = client.getChildren().forPath("/");
    System.out.println(childrens);
}

@Test
public void testGet3() throws Exception {
    // 3. 查询节点状态信息:ls -s:getData().storingStatIn(状态对象).forPath()
    Stat stat = new Stat();
    client.getData().storingStatIn(stat).forPath("/app1");
    System.out.println(stat);
}

修改节点

/*
    *  修改数据
    *   1. 修改数据 :setData().forPath()
    *   2. 根据版本修改:setData().withVersion(version).forPath()
    *   version是通过查询出来的。目的是为了让其他客户端或者线程不干扰当前操作
* */
@Test
public void testSet() throws Exception {
    Stat stat = client.setData().forPath("/app1", "hehe".getBytes(StandardCharsets.UTF_8));
    System.out.println(stat);
}

@Test
public void testForVersion() throws Exception {
    Stat stat = new Stat();
    client.getData().storingStatIn(stat).forPath("/app1");
    client.setData().withVersion(stat.getVersion()).forPath("/app1", "haha".getBytes(StandardCharsets.UTF_8));
}

删除节点

/*
     *  删除节点 delete deleteall
     *  1. 删除单个节点
     *  2. 删除带有子节点的节点
     *  3. 必须成功的删除(防止网络抖动)
     *  4. 回调
* */
@Test
public void testDelete() throws Exception {
    // 1. 删除单个节点
    client.delete().forPath("/app1");
}

@Test
public void testDelete2() throws Exception {
    // 2. 删除带有子节点的节点
    client.delete().deletingChildrenIfNeeded().forPath("/app3");
}

@Test
public void testDelete3() throws Exception {
    // 3. 必须成功的删除(防止网络抖动)
    client.delete().guaranteed().forPath("/app2");
}

@Test
public void testDelete4() throws Exception {
    // 4. 回调
    client.delete().guaranteed().inBackground(new BackgroundCallback() {
        @Override
        public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
            System.out.println(curatorEvent);
        }
    }).forPath("/app2");
}

Watch监听

  • ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性
  • ZooKeeper中引入了Watcher机制来实现了发布/订阅功能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态变化时,会通知所有订阅者
  • ZooKeeper原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便,需要开发人员自己反复注册Watcher,比较繁琐
  • Curator引入了Cache来实现对ZooKeeper服务端事件的监听
  • ZooKeeper提供了三种Watcher
    • NodeCache:只是监听某一个特定的节点
    • PathChildrenCache”监控一个ZNode的子节点
    • TreeCache:可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCache的组合

NodeCache

@Test
public void testNodeCache() throws Exception {
    // 1. 创建NodeCache对象
    NodeCache nodeCache = new NodeCache(client,"/app1");
    // 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));
        }
    });
    nodeCache.start();
}

PathChildrenCache

@Test
public void testPathChildrenCache() throws Exception {
    PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);
    pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
        @Override
        public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
            System.out.println("子节点变化了~~~");
            if(pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
                System.out.println("子节点更改了~~~");
                System.out.println(new String(pathChildrenCacheEvent.getData().getData()));
            }
        }
    });
    pathChildrenCache.start();
    while (true){

    }
}

TreeCache

@Test
public void testTreeCache() throws Exception {
    TreeCache treeCache = new TreeCache(client,"/app2");
    treeCache.getListenable().addListener(new TreeCacheListener() {
        @Override
        public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
            System.out.println("节点变化了");
            System.out.println(treeCacheEvent);
        }
    });
    treeCache.start();
    while (true){

    }
}

分布式锁

概述

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

原理

  • 核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点

在这里插入图片描述

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

售票案例

Curator实现分布式锁API

Curator中有五种锁方案:

  • InterProcessSemaphoreMutex:分布式排它锁(非可重入锁)
  • InterProcessMutex:分布式可重入排它锁
  • InterProcessReadWriteLock:分布式读写锁
  • InterProcessMultiLock:将多个锁作为单个实体管理的容器
  • InterProcessSemaphoreV2:共享信号量

模拟12306售票案例

Ticket12306

/**
 * @author HelloCode
 * @site https://www.hellocode.top
 * @date 2022年08月18日 11:18
 */
public class Ticket12306 implements Runnable{

    private int tickets = 10;
    private InterProcessMutex lock;

    public Ticket12306(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
        CuratorFramework client = CuratorFrameworkFactory.builder()
            .connectString("192.168.23.129:2181")
            .connectionTimeoutMs(15 * 1000)
            .sessionTimeoutMs(60 * 1000)
            .retryPolicy(retryPolicy)
            .build();
        client.start();
        lock = new InterProcessMutex(client,"/lock");
    }

    @Override
    public void run() {
        while(true){
            try {
                // 获取锁
                lock.acquire(3, TimeUnit.SECONDS);
                if(tickets > 0){
                    System.out.println(Thread.currentThread() + ":" + tickets--);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                // 释放锁
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

LockTest

/**
 * @author HelloCode
 * @site https://www.hellocode.top
 * @date 2022年08月18日 17:11
 */
public class LockTest {
    public static void main(String[] args) {
        Ticket12306 ticket = new Ticket12306();

        // 创建客户端
        Thread t1 = new Thread(ticket,"携程");
        Thread t2 = new Thread(ticket,"飞猪");

        t1.start();
        t2.start();
    }
}

集群

搭建

要求

真实的集群是需要部署在不同的服务器上的,但是在我们测试时同时启动很多个虚拟机内存会吃不消,所以我们通常会搭建伪集群,也就是把所有的服务都搭建在一台虚拟机上,用端口进行区分

配置集群

  1. 在每个zookeeper的data目录下创建一个myid文件,内容分别是1、2、3。这个文件就是记录每个服务器的id

    echo 1 > /usr/local/zookeeper-cluster/zookeeper-1/data/myid
    echo 2 > /usr/local/zookeeper-cluster/zookeeper-2/data/myid
    echo 3 > /usr/local/zookeeper-cluster/zookeeper-3/data/myid
    
  2. 在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。
    集群服务器IP列表如下

    vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
    vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
    vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfg
    
    server.1=192.168.23.129:2881:3881
    server.2=192.168.23.129:2882:3882
    server.3=192.168.23.129:2883:3883
    

    server.服务器id=服务器IP地址:服务器之间通信端口(默认2881):服务器之间投票选举端口(默认3881)

    真实环境搭建集群时的2881和3881就直接使用默认值即可

启动集群

启动集群就是分别启动每个实例

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

启动后查询一下运行状态

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

故障测试

  • 3个节点的集群,1个从服务器挂掉,集群正常
  • 3个节点的集群,2个从服务器都挂掉,主服务器也无法运行。因为可运行的机器没有超过集群总数量的半数
  • 3个节点的集群,2个从服务器都挂掉,再重启一个从服务器,主服务器恢复运行,集群恢复正常
  • 当主服务器挂掉,将在正常的从服务器中重新进行选举,选举出新的leader

集群角色

在ZooKeeper集群服务中有三个角色:

  • Leader 领导者
    1. 处理事务请求
    2. 集群内部各服务器的调度者
  • Follower 跟随者
    1. 处理客户端非事务请求,转发事务请求给Leader服务器
    2. 参与Leader选举投票
  • Observer 观察者
    1. 处理客户端非事务请求,转发事务请求给Leader服务器

在这里插入图片描述

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

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

相关文章

Linux文件目录结构详解

Linux文件目录结构 Linux文件系统是采用级层式的树状目录结构&#xff0c;在此结构中的最上层是根目录“/”&#xff0c;然后在此目录下再创建其他的目录 Linux系统下一切硬件皆文件 具体的目录结构 /bin ->存放最经常使用的指令/sbin ->存放系统管理员使用的系统管理…

从Redis、HTTP协议,看Nett协议设计,我发现了个惊天大秘密

1. 协议的作用 TCP/IP 中消息传输基于流的方式&#xff0c;没有边界 协议的目的就是划定消息的边界&#xff0c;制定通信双方要共同遵守的通信规则 2. Redis 协议 如果我们要向 Redis 服务器发送一条 set name Nyima 的指令&#xff0c;需要遵守如下协议 // 该指令一共有3…

第一章 R语言介绍

1.为何使用R 与起源于贝尔实验室的S语言类似&#xff0c;R也是一种为统计计算和绘图而生的语言和环境&#xff0c;它是一套开源的数据分析解决方案&#xff0c;由一个庞大且活跃的全球性研究型社区维护。但是&#xff0c;市面上也有许多其他流行的统计和制图软件&#xff0c;如…

NLP自然语言处理NLTK常用英文功能汇总

自然语言处理 (NLP) 是一门研究如何让计算机程序理解人类语言的学科。NLTK (Natural Language Toolkit) 是一个 Python 包,可以用于 NLP 的应用开发。 很多数据都是非结构化的,而且包含可以被人类读懂的文本。在用编程方式分析这些数据之前,我们需要对它们进行预处理。在本…

Allegro174版本新功能介绍之背景颜色设置

Allegro174版本新功能介绍之背景颜色设置 Allegro升级到了174的时候,打开的时候默认是黑色的背景,如下图 选择界面 工作界面 和166以及172版本不一样,174支持切换成白色的背景,具体操作如下 选择setup

Java--基础语法

文章目录一、输出hello world二、示例说明三、基本语法三、标识符规则四、注释一、输出hello world public class Helloworld {/*第一个java程序*输出Hello world!!!*/public static void main(String[] args) {//输出Hello world!!!System.out.println("Hello world!!!&…

如何使用Git同时绑定Github以及Gitee

今天接到一项任务&#xff0c;是需要clone一个github上面的项目&#xff0c;正兴高采烈的git clone的时候&#xff0c;git bash框框报错&#xff0c;突然一想&#xff0c;我貌似一直用的Gitee,绑定的也是Gitee,并没有绑定Github,于是就有了这篇博客记录如何使用Git同时绑定Gite…

CTF压轴题解题思路和过程

前言 压轴题难度极大。我在这里详细的记录一下解题思路和过程 题目初探 拿到题目&#xff0c;为三个文件&#xff0c;其中mem_secret-963a4663.vmem为常见内存镜像文件&#xff0c;另外两个文件格式未知。 使用volatility进行分析无法识别profile。 接着分析分析Encryption.…

探索云原生技术之容器编排引擎-Kubernetes/K8S详解(6)

❤️作者简介&#xff1a;2022新星计划第三季云原生与云计算赛道Top5&#x1f3c5;、华为云享专家&#x1f3c5;、云原生领域潜力新星&#x1f3c5; &#x1f49b;博客首页&#xff1a;C站个人主页&#x1f31e; &#x1f497;作者目的&#xff1a;如有错误请指正&#xff0c;将…

Day 19-Vue3 技术_其它

1.全局API的转移 Vue 2.x 有许多全局 API 和配置。例如&#xff1a;注册全局组件、注册全局指令等。 //注册全局组件 Vue.component(MyButton, {data: () > ({count: 0}),template: <button click"count">Clicked {{ count }} times.</button> })//注…

ESXI6.5.0安装部署

将ESXI6.5.0系统盘放入光驱&#xff0c;插入服务器&#xff0c;启动服务器&#xff1b; 进入服务器BIOS系统&#xff1b; 启动方式选择DVD&#xff1b; 进入ESXI6.5.0安装程序&#xff1b; 等待安装程序载入&#xff1b; 敲回车&#xff1b; 敲回车&#xff1b; …

【token】一.token的作用;二.Express中实现token的方法

目录 一.token的作用&#xff1a; 1.控制表单的重复提交&#xff1a;在表单中加入隐藏的表单控件&#xff0c;在这个隐藏的表单控件中带上token字符串。 2.身份验证&#xff1a;用来验证向服务器发起请求&#xff08;请求服务器的资源&#xff09;的用户是否是合法的用户。经…

如何理解 CRUD 与 REST

全文 2070 字 阅读时间约 6 分钟 本文首发于码匠技术博客​​​​​​​ 目录 什么是 CRUD&#xff1f; CRUD 的发展简史 CRUD 规则 什么是 REST&#xff1f; REST 的发展简史 REST 规则 CRUD VS REST 关于码匠 CRUD 和 REST 是应用开发领域中两个比较常见的概念&…

解决安装Tensorflow2: ERROR annot determine archive format of XXX保存问题

安装命令报错&#xff1a; ERROR: Cannot unpack file C:\Users\lenovo\AppData\Local\Temp\pip-unpack-mdiptqlf\simple.html (downloaded from C:\Users\lenovo\AppData\Local\Temp\pip-req-build-oq32e170, content-type: text/html); cannot detect archive format解决方法…

Barra模型因子的构建及应用(一)

一、摘要 Barra模型可以追溯至1974年&#xff0c;美国学者Barr Rosenberg对投资组合的风险和收益进行分析的多因子风险模型。随后Rosenberg成立了Barra&#xff0c;并针对美国权益市场提出了Barra USE1模型&#xff0c;现在已更新到USE4&#xff1b;而针对中国权益市场提出的B…

微服务架构下的可观测性

微服务架构下的可观测性 一、服务可观测性 传统架构下排查问题传统项目在出现异常或性能问题时&#xff0c;通常都是基于系统日志文件来排查。而在微服务分布式部署架构下&#xff0c;日志文件随微服务分散存储&#xff0c;对于排查问题工作量很大。传统监控告警平台也仅针对平…

痞子衡嵌入式:探讨i.MXRT下FlexSPI driver实现Flash编程时对于中断支持问题

大家好&#xff0c;我是痞子衡&#xff0c;是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT下FlexSPI driver实现Flash编程时对于中断支持问题。 前段时间有客户在官方社区反映 i.MXRT1170 下使用官方 SDK 里 FlexSPI 驱动去擦写 Flash 时不能很好地支持全局中断。客户项目…

内核解读之内存管理(2)内存管理三级架构之内存结点node

文章目录0、概述1、内存节点node0、概述 结合NUMA的架构&#xff0c;Linux抽象出了三级内存管理架构&#xff1a;内存节点node、内存区域zone和物理页框page。 在NUMA模型中&#xff0c;每个CPU都有自己的本地内存节点&#xff08;memory node&#xff09;&#xff0c;而且还…

qq录制视频保存到哪了?如何更改qq录屏存储位置

一、查看qq录制视频保存位置如果有录制视频的需求&#xff0c;相信大部分人都是使用qq自带的录屏功能来录制视频。那qq录屏后的视频在哪里去找&#xff1f;今天就给大家分享如何查看qq录制完的视频保存位置操作方法&#xff1a;第一步&#xff1a;电脑上登录qq&#xff0c;在qq…

Cadence PCB仿真 使用Allegro PCB SI为BRD文件创建通用型IBIS模型的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 1,概述 本文简单介绍使用Allegro PCB SI软件为BRD PCB设计文件中的元器件创建IBIS模型的方法。 2,创建方法 第1步:确定打开PCB文件的软件是 Allegro PCB SI 如果不是Allegro PCB SI,可执行File→Change Editor…更换软…