zookeeper学习笔记

news2024/11/18 20:23:11

zookeeper学习笔记

  • 1.Zookeeper概念
  • 2.Zookeeper命令操作
    • 2.1数据模型
      • 2.1.1数据结构
      • 2.1.2节点类型
    • 2.2服务端命令
    • 2.3客户端命令-基本命令
    • 2.4客户端命令-高级点命令
  • 3.Zookeeper JavaAPI操作
    • 3.1Cutor介绍
    • 3.2Cutor API常用操作-增删改查
      • 3.2.1建立连接
      • 3.2.2创建节点
      • 3.2.3删除节点
      • 3.2.4修改节点
      • 3.2.5查询节点
    • 3.3Cutor API常用操作-Watch事件监听
      • 3.3.1概念
      • 3.3.2Watch 机制特点
    • 3.4分布式锁
    • 3.5模拟12306售票案例
  • 4.Zookeeper集群搭建
    • 4.1上传zookeeper并解压
    • 4.2移动解压的文件,并重新命名
    • 4.3创建data目录,并将conf下zoo_sample.cfg改名为zoo.cfg
    • 4.4配置每一个Zookeeper的dataDir和clientPort
    • 4.5配置集群
      • 4.5.1在每个zookeeper的data目录下创建一个myid文件,内容分别为1,2,3这个文件 就是记录每个服务器的id
      • 4.5.2在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表
    • 4.6启动集群
    • 4.7集群角色

1.Zookeeper概念

Zookeeper 是一个分布式协调服务的开源框架。 主要用来解决分布式集群中应用系统的一致性问题,例如怎样避免同时操作同一数据造成脏读的问题。
ZooKeeper 本质上是一个分布式的小文件存储系统。 提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。

诸如:

  • 统一命名服务
  • 分布式配置管理
  • 分布式消息队列
  • 分布式锁(编码实现可以)
  • 分布式协调等功能

2.Zookeeper命令操作

2.1数据模型

2.1.1数据结构

在这里插入图片描述

  1. Znode 兼具文件和目录两种特点。既像文件一样维护着数据、元信息、 ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子 Znode。用户对 Znode 具有增、删、改、查等操作(权限允许的情况下)。
  2. Znode 具有原子性操作, 读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的
    操作。
  3. Znode 存储数据大小有限制。 ZooKeeper 虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据, 通常以 KB 为大小单位。 ZooKeeper 的服务器和客户端都被设计为严格检查并限制每个 Znode 的数据大小至多 1M,当时常规使用中应该远小于此值。
  4. Znode 通过路径引用, 如同 Unix 中的文件路径。 路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper 中,路径由Unicode 字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。

2.1.2节点类型

在这里插入图片描述

Znode 有两种,分别为临时节点永久节点
节点的类型在创建时即被确定,并且不能改变。

  • 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。 临时节点不允许拥有子节点。

  • 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

  • Znode 还有一个序列化的特性,如果创建的时候指定的话,该 Znode 的名字后面会自动追加一个不断增加的序列号。 序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序。 它的格式为“ %10d” (10 位数字,没有数值的数位用 0 补充,例如“ 0000000001” )。

2.2服务端命令

在这里插入图片描述

2.3客户端命令-基本命令

在这里插入图片描述

服务端启动
在这里插入图片描述
客户端启动

./zkCli.sh -server localhost:2181

在这里插入图片描述
退出

quit

查看

ls /

在这里插入图片描述创建

create /appl cj

在这里插入图片描述
获取数据

get /appl

设置数据

set /appl 55

在这里插入图片描述
删除

delete /appl/55

删除全部

deleteall
help

2.4客户端命令-高级点命令

创建临时节点

create -e /app1

创建顺序节点

create -s /app1

创建临时顺序节点

create -es /app1

查看节点信息

ls -s /

3.Zookeeper JavaAPI操作

3.1Cutor介绍

在这里插入图片描述
官网

3.2Cutor API常用操作-增删改查

在这里插入图片描述

3.2.1建立连接

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jq</groupId>
    <artifactId>curator-zk</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</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.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
package com.jq.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Test;

public class CuratorTest {
    /**
     * 建立连接
     */
    @Test
    public void testConnect(){
        /**
         * 第一种方式
         * @param connectString 连接字符串,zk server地址和端口
         * @param sessionTimeoutMs 会话超时时间
         * @param connectionTimeoutMs  连接超时时间
         * @param retryPolicy 重试策略

        */
//        //重试策略
//        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
//        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
//        client.start();
//        //开启连接

        /**
         * 第二种方式
         * */
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.56.10:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy).namespace("cj").build();
        client.start();
    }
}

3.2.2创建节点

package com.jq.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.nio.charset.StandardCharsets;

public class CuratorTest {
    private CuratorFramework client;
    /**
     * 建立连接
     */
    @Before
    public void testConnect(){
        /**
         * 第一种方式
         * @param connectString 连接字符串,zk server地址和端口
         * @param sessionTimeoutMs 会话超时时间
         * @param connectionTimeoutMs  连接超时时间
         * @param retryPolicy 重试策略

        */
//        //重试策略
//        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
//        CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
//        client.start();
//        //开启连接

        /**
         * 第二种方式
         * */
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
        client = CuratorFrameworkFactory.builder()
                .connectString("192.168.56.10:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy).namespace("cj").build();
        client.start();
    }

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

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

}

3.2.3删除节点

    /**
     * 删除节点: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("/app4");
    }
    @Test
    public void testDelete3() throws Exception {
        //3.必须成功删除
        client.delete().guaranteed().forPath("/app2");
    }
    @Test
    public void testDelete4() throws Exception {
        //4.回调
        BackgroundCallback backgroundCallback = new BackgroundCallback() {
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("我被删除了");
                System.out.println(curatorEvent);
            }
        };
        client.delete().guaranteed().inBackground(backgroundCallback).forPath("/dubbo");
    }v

3.2.4修改节点

 /**
     * 修改节点:
     * 1. 修改数据
     * 2.根据版本修改
     */
    @Test
    public void testSet() throws Exception {
        client.setData().forPath("/app1","jeee".getBytes());
    }
    @Test
    public void testSetForVersion() throws Exception {
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");

        int version = stat.getVersion();
        System.out.println(version);
        client.setData().withVersion(version).forPath("/app1","jeee2".getBytes());
    }

3.2.5查询节点

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

    }
    @Test
    public void testGet2() throws Exception {
        //2.查询子节点: ls
        List<String> path = client.getChildren().forPath("/app4");
        System.out.println(path);
    }
    @Test
    public void testGet3() throws Exception {
        //3. 查询节点的状态信息: ls-s
        Stat stat = new Stat();
        client.getData().storingStatIn(stat).forPath("/app1");
        System.out.println(stat);

    }

3.3Cutor API常用操作-Watch事件监听

在这里插入图片描述

3.3.1概念

ZooKeeper 提供了分布式数据发布/订阅功能一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能够做出相应的处理。
ZooKeeper 中,引入了 Watcher 机制实现这种分布式的通知功能
ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
触发事件种类很多, 如:节点创建,节点删除,节点改变,子节点改变等。
总的来说可以概括 Watcher 为以下三个过程:

  • 客户端向服务端注册 Watcher、
  • 服务端事件发生触发 Watcher、
  • 客户端回调 Watcher 得到触发事件情况
    Curator 引入了Cache来实现对Zookeeper服务端事件的监听

3.3.2Watch 机制特点

  • 一次性触发
    事件发生触发监听,一个 watcher event 就会被发送到设置监听的客户端,这种效果是一次性的, 后续再次发生同样的事件,不会再次触发。
  • 事件封装
    ZooKeeper 使用 WatchedEvent 对象来封装服务端事件并传递。WatchedEvent 包含了每一个事件的三个基本属性:通知状态(keeperState), 事件类型(EventType) 和节点路径(path)
  • event 异步发送
    watcher 的通知事件从服务端发送到客户端是异步的。
  • 先注册再触发
    Zookeeper 中的 watch 机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。

3.4分布式锁

相比于redis和数据库 性能稳定可靠
分布式锁,这个主要得益于 ZooKeeper 保证了数据的强一致性
锁服务可以分为两类,一个是保持独占,另一个是控制时序。

所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把 zk 上的一个 znode 看作是一把锁,通过 createznode 的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。

控制时序,就是所有试图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了。做法和上面基本类似,只是这/distribute_lock 已经预先存在,客户端在它下面创建临时有序节点
(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL 来指定)。 Zk 的父节点(/distribute_lock)维持一份 sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序。
在这里插入图片描述在这里插入图片描述

3.5模拟12306售票案例

在这里插入图片描述

4.Zookeeper集群搭建

在这里插入图片描述

4.1上传zookeeper并解压

tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz

4.2移动解压的文件,并重新命名

mkdir /usr/local/zookper-cluster
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-1
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-2
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-3

在这里插入图片描述

4.3创建data目录,并将conf下zoo_sample.cfg改名为zoo.cfg

mkdir /usr/local/zookper-cluster/zookper-1/data
mkdir /usr/local/zookper-cluster/zookper-2/data
mkdir /usr/local/zookper-cluster/zookper-3/data
mv /usr/local/zookper-cluster/zookper-1/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg

mv /usr/local/zookper-cluster/zookper-2/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg

mv /usr/local/zookper-cluster/zookper-3/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

4.4配置每一个Zookeeper的dataDir和clientPort

分别为2181 2182 2183

修改/usr/local/zookper-cluster/zookper-1/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
clientPort=2181
dataDir=/usr/local/zookper-cluster/zookper-1/data

修改/usr/local/zookper-cluster/zookper-2/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
clientPort=2182
dataDir=/usr/local/zookper-cluster/zookper-2/data

修改/usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
clientPort=2183
dataDir=/usr/local/zookper-cluster/zookper-3/data

4.5配置集群

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

echo 1 >/usr/local/zookper-cluster/zookper-1/data/myid
echo 2 >/usr/local/zookper-cluster/zookper-2/data/myid
echo 3 >/usr/local/zookper-cluster/zookper-3/data/myid

4.5.2在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表

集群服务器IP列表如下

vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg

server.1=192.168.56.10:2181:3881
server.2=192.168.56.10:2182:3882
server.3=192.168.56.10:2183:3883

解释:server.服务器id=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口

4.6启动集群

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

4.7集群角色

在这里插入图片描述

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

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

相关文章

js 回到顶部逻辑实现和elementUI源码解析

回到顶部 大家或多或少都会遇到“回到顶部”这样的需求&#xff0c;在此分享这个技术点以及可能遇到的问题。再分析element源码。 回到顶部代码实现 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta http-…

基于PHP+MySQL的个人博客系统毕设

随着时代和网络的发展,人们越来越希望通过多种模式来展示自己。于是个人博客就出现了,它可以更好的让人们来记录自己的工作和学习方式。博客不仅仅可以让自己抒发个人感情,还可以展示自己真实的生活,从而建立起一种友好的交友平台。 PHP个人博客系统毕设系统分为前台和后台两部…

Python每日一练 02

Python每日一练 02 文章目录Python每日一练 02一、对象二、对象属性三、赋值一、对象 Python中所有数字、序列、集合、映射、类、实例、异常、模块、类与类的实例、函数、方法、布尔值、空值等都被称为对象。 二、对象属性 每个对象都有3个基本属性&#xff1a; 类型(type)…

Flink-源算子Source(获取数据源)的使用

5.1 整体介绍 获取执行环境读取数据源定义基于数据的转换操作定义计算结果的输出位置触发程序执行 5.2 创建集成环境 5.2.1 获取执行环境 批处理getExecutionEnvironment 提交命令行设置 bin/flink run -Dexecution.runtime-modeBATCH ...代码 StreamExecutionEnvironme…

pip Command Not Found – Mac 和 Linux 错误被解决

使用Python时&#xff0c;可能需要安装和使用某些软件包。有一个命令可用于’pip‘ 使用pip&#xff0c;您可以安装、升级和卸载各种Python包。在本文中&#xff0c;您将学习如何使用它&#xff0c;以及如何处理pip错误。 如何使用 pip Pip是一个可以在Linux或Mac命令行上使用…

HTTP(http+抓包Fiddler+协议格式+请求+响应)

目录 &#x1f984;1. 了解HTTP &#x1f984;2. 抓包 &#x1f984;3. http协议格式 &#x1f432;3.1 完整的HTTP请求格式 &#x1f432;3.2 完整的HTTP响应的格式 HTTP请求 &#x1f984;4. 认识URL &#x1f984;5. http中的"方法" &#x1f432;5.1…

智能与工程学院2022级计算机朱元华

智能与工程学院 《高级语言程序设计》 小组学习任务书 第 1 次 专业年级&#xff1a; 2022级计算机 指导教师&#xff1a; 朱元华 2022-2023学年 第 1 学期 一、任务 XXX信息管理系统的需求分析和功能设计 二、分组形式 学生自由组合&#xff0c;5-8人为一组&#xff0c;根据…

Tuxera NTFS2023Mac读写ntfs磁盘工具

Tuxera Ntfs for mac2023是Mac中专用于读写外置存储的工具&#xff0c;具有强大的磁盘管理和修复功能&#xff0c;它在Mac上完全读写NTFS格式硬盘&#xff0c;快捷的访问、编辑、存储和传输文件。能够在 Mac 上读写 Windows NTFS 文件系统。Tuxera NTFS 实现在Mac OS X系统读写…

【Spring】——6、按照条件向Spring容器中注册bean

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

静态时序分析简明教程(六)]时钟组与其他时钟特性

生成时钟的sdc约束方法一、写在前面1.1 快速导航链接二、时钟组2.1 引入时钟组2.2 set_clock_group2.2.1 -name2.2.2 -group clock_list2.2.3 -logically_exclusive|-physically_exclusive|-asynchronous2.2.4 -allow_path2.2.5 -comment三、其他时钟特性3.1 过渡时间3.2 偏移与…

【Linux】进程间通信——管道

目录 一、概念 二、管道函数 1.popen函数 2.pclose函数 3.文件函数 三、管道的操作 1.管道的分类 无名管道 有名管道 管道的特点 四、管道的实现 操作系统对进程之间相互保护 两个进程之间相互通信 前言&#xff1a; 进程间通信的方法/IPC机制都有哪些&#xff1a; …

求二进制中1的个数的三种方法

求二进制中的1的个数 文章目录第一种方法&#xff1a;模2除2第二种方法&#xff1a;利用操作符右移后与1第三种方法&#xff1a;该数与上比它小1的数&#xff08;最优的方法&#xff09;第一种方法&#xff1a;模2除2 首先明白如何得到一个数的十进制的每一位&#xff1f; 以1…

PHP代码审计入门-DVWA靶场CSRF篇

0x00 写在前面 从零学习php&#xff0c;最终目的实现代码审计入门&#xff0c;软件采用sublime text&#xff0c;环境使用phpstudy搭建&#xff0c;数据库是navicat&#xff0c;需要有基本的前端基础、简单的phpmysql后端基础、渗透知识和漏洞原理&#xff0c;文章跟随流沙前…

bizlog通用操作日志组件(使用篇)

引言 如上图所示&#xff0c;产品的新需求&#xff0c;需要将操作人在系统中具体编辑操作的变更内容记录下来。 按正常思路来说&#xff0c;无非就是将修改前后的对象字段逐个比较&#xff0c;再拼接为详细的操作描述记录到操作日志表中。如果是一个模块的需求&#xff0c;单独…

用HTML+CSS做一个学生抗疫感动专题网页设计作业网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢?

转自: springboot如何修改thymeleaf中默认的页面路径及默认的后缀名呢? 下文讲述springboot修改thymefleaf修改页面默认路径及后缀名的方法分享&#xff0c;如下所示: 实现思路:只需在配置文件中修改相应的配置即可&#xff0c;如&#xff1a;application.yaml spring:thym…

MySQL单表查询操作详解,不做CRUD程序员

在我们对数据进行操作时&#xff0c;查询无疑是至关重要的&#xff0c;查询操作灵活多变&#xff0c;我们可以根据开发的需求&#xff0c;设计高效的查询操作&#xff0c;把数据库中存储的数据展示给用户。 文章目录前言1. 基础查询1.1 基础查询语法1.2 基础查询练习2. 条件查询…

大数据路线

一、概念部分 1.1 大数据、数仓、数据湖、中台的概念 区别数仓数据湖使用场景批处理&#xff0c;BI&#xff0c;数据可视化机器学习、预测分析、数据分析Schema写入型读取型数据源类型OLTP为主的结构化数据loT&#xff0c;日志&#xff0c;各个端等结构非结构均可性价比需要快…

牛客刷题总结——Python入门08:面向对象、正则表达式

&#x1f935;‍♂️ 个人主页: 北极的三哈 个人主页 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;Python领域优质创作者。 &#x1f4d2; 系列专栏&#xff1a;《牛客题库-Python篇》 &#x1f310;推荐《牛客网》——找工作神器|笔试题库|面试经验|实习经验内推&am…

Design A Youtube

title: Notes of System Design No.05 — Design a Youtube description: ‘Design a Youtube’ date: 2022-05-14 13:45:37 tags: 系统设计 categories: 系统设计 01. Funtional Requirements 02. Non Functional Requirements 03.Assumption 04 API 05 High Level Design 上…