分布式锁实现方案-基于zookeeper的分布式锁实现(原理与代码)

目录

一、基于zookeeper的分布式锁

1.1 基于Zookeeper实现分布式锁的原理

1.1.1 分布式锁特性说明

1.1.1.1 特点分析

1.1.1.2 本质

1.1.2 Zookeeper 分布式锁实现原理

1.1.2.1 Zookeeper临时顺序节点特性

1.1.2.2 Zookeeper满足分布式锁基本要求

1.1.2.3 Watcher机制

1.1.2.3 总结

1.2 分布式锁流程说明

1.2.1 分布式锁流程图

1.2.2 流程说明

1.3 分布式锁代码实现

1.3.1 自己手写,实现Lock接口

1.3.1.1 分布式锁ZookeeperDistributedLock

1.3.1.2 模拟下单处理OrderServiceHandle

1.3.1.3 订单号生成类OrderCodeGenerator

1.3.1.4 分布式锁测试类TestZookeeperDistributedLock

1.3.1.5 测试效果

1.3.2 基于Apache Curator 框架调用

1.3.2.1 maven依赖

1.3.2.2 代码实现

1.3.2.2.1 分布式锁类CuratorDistributeLock

1.3.2.2.2 测试类TestCuratorDistributedLock

1.3.2.3 执行结果


一、基于zookeeper的分布式锁

1.1 基于Zookeeper实现分布式锁的原理

1.1.1 分布式锁特性说明

1.1.1.1 特点分析
  • 每次只能一个占用锁;
  • 可以重复进入锁;
  • 只有占用者才可以解锁;
  • 获取锁和释放锁都需要原子
  • 不能产生死锁
  • 尽量满足性能
1.1.1.2 本质

同步互斥,使得处理任务能够一个一个逐步的过临界资源。

1.1.2 Zookeeper 分布式锁实现原理

1.1.2.1 Zookeeper临时顺序节点特性

zookeeper中有一种临时顺序节点,它具有以下特征:

  • 时效性,当会话结束,节点将自动被删除
  • 顺序性,当多个应用向其注册顺序节点时,每个顺序号将只能被一个应用获取
1.1.2.2 Zookeeper满足分布式锁基本要求
  1. 因为顺序性,可以让最小顺序号的应用获取到锁,从而满足分布式锁的 每次只能一个占用锁,因为只有它一个获取到,所以可以实现 重复进入 ,只要设置标识即可。锁的释放,即删除应用在zookeeper上注册的节点,因为每个节点只被自己注册拥有,所以只有自己才能删除,这样就满足只有占用者才可以解锁
  2. zookeeper的序号分配是原子的,分配后即不会再改变,让最小序号者获取锁,所以获取锁是原子的
  3. 因为注册的是临时节点,在会话期间内有效,所以不会产生死锁
  4. zookeeper注册节点的性能能满足几千,而且支持集群,能够满足大部分情况下的性能
1.1.2.3 Watcher机制

Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事
件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然
后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。

在实现分布式锁的时候,主要利用这个机制,实现释放锁的时候,通知等待锁的线程竞争锁。

1.1.2.3 总结

综上可知,Zookeeper其实是基于临时顺序节点特性实现的分布式锁。当然,还结合了他的Watcher机制,实现释放锁的时候,通知等待锁的线程去竞争锁。

1.2 分布式锁流程说明

1.2.1 分布式锁流程图

1.2.2 流程说明

  1. client判断/lock目录是否存在,如果不存在则向其注册/lock的持久节点
  2. client向/lock/目录下注册/lock/Node-前缀的临时顺序节点,并得到顺序号
  3. client获取/lock/目录下的所有临时顺序子节点
  4. client判断临时子节点序号中是否存在比自身的序号小的节点。如果不存在,则获取到锁;如果存在,则对象该临时节点做watch监控
  5. 获得锁的线程,执行业务逻辑,执行完之后,删除临时节点,完成锁的释放。
  6. 等待锁的线程如果收到监控的临时节点被删除的通知,则再重复4、5、6步骤,进入下一个获得锁、释放锁的循环。

1.3 分布式锁代码实现

1.3.1 自己手写,实现Lock接口

1.3.1.1 分布式锁ZookeeperDistributedLock
package com.ningzhaosheng.distributelock.zookeeper;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;


/**
 * @author ningzhaosheng
 * @date 2024/4/17 18:13:38
 * @description 基于zookeeper实现的分布式锁
 */
public class ZookeeperDistributedLock implements Lock {

    private static Logger logger = LoggerFactory.getLogger(ZookeeperDistributedLock.class);

    // zookeeper 地址
    private String ZOOKEEPER_IP_PORT = "192.168.152.130:2181";
    // zookeeper 锁目录
    private String LOCK_PATH = "/LOCK";

    // 创建 zookeeper客户端zkClient
    private ZkClient client = null;

    private CountDownLatch cdl;

    // 当前请求的节点前一个节点
    private String beforePath;
    // 当前请求的节点
    private String currentPath;


    /**
     * 初始化客户端和创建LOCK目录
     *
     * @param ZOOKEEPER_IP_PORT
     * @param LOCK_PATH
     */
    public ZookeeperDistributedLock(String ZOOKEEPER_IP_PORT, String LOCK_PATH) {
        this.ZOOKEEPER_IP_PORT = ZOOKEEPER_IP_PORT;
        this.LOCK_PATH = LOCK_PATH;
        client = new ZkClient(ZOOKEEPER_IP_PORT, 4000, 4000, new SerializableSerializer());
        // 判断有没有LOCK目录,没有则创建
        if (!this.client.exists(LOCK_PATH)) {
            this.client.createPersistent(LOCK_PATH);
        }
    }

    @Override
    public void lock() {
        if (!tryLock()) {
            //对次小节点进行监听
            waitForLock();
            lock();
        } else {
            logger.info(Thread.currentThread().getName() + " 获得分布式锁!");
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        // 如果currentPath为空则为第一次尝试加锁,第一次加锁赋值currentPath
        if (currentPath == null || currentPath.length() <= 0) {
            // 创建一个临时顺序节点
            currentPath = this.client.createEphemeralSequential(LOCK_PATH + '/', "lock");
            System.out.println("---------------------------->" + currentPath);
        }

        // 获取所有临时节点并排序,临时节点名称为自增长的字符串如:0000000400
        List<String> childrens = this.client.getChildren(LOCK_PATH);
        //由小到大排序所有子节点
        Collections.sort(childrens);
        //判断创建的子节点/LOCK/Node-n是否最小,即currentPath,如果当前节点等于childrens中的最小的一个就占用锁
        if (currentPath.equals(LOCK_PATH + '/' + childrens.get(0))) {
            return true;
        }
        //找出比创建的临时顺序节子节点/LOCK/Node-n次小的节点,并赋值给beforePath
        else {
            int wz = Collections.binarySearch(childrens, currentPath.substring(6));
            beforePath = LOCK_PATH + '/' + childrens.get(wz - 1);
        }

        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    //等待锁,对次小节点进行监听
    private void waitForLock() {
        IZkDataListener listener = new IZkDataListener() {
            public void handleDataDeleted(String dataPath) throws Exception {
                logger.info(Thread.currentThread().getName() + ":捕获到DataDelete事件!---------------------------");
                if (cdl != null) {
                    cdl.countDown();
                }
            }

            public void handleDataChange(String dataPath, Object data) throws Exception {

            }
        };

        // 对次小节点进行监听,即beforePath-给排在前面的的节点增加数据删除的watcher
        this.client.subscribeDataChanges(beforePath, listener);
        if (this.client.exists(beforePath)) {
            cdl = new CountDownLatch(1);
            try {
                cdl.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.client.unsubscribeDataChanges(beforePath, listener);
    }

    @Override
    public void unlock() {
        // 删除当前临时节点
        client.delete(currentPath);
    }

    @Override
    public Condition newCondition() {
        return null;
    }

}

1.3.1.2 模拟下单处理OrderServiceHandle
package com.ningzhaosheng.distributelock.zookeeper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;

/**
 * @author ningzhaosheng
 * @date 2024/4/17 21:45:46
 * @description 模拟订单处理
 */
public class OrderServiceHandle implements Runnable {
    private static OrderCodeGenerator ong = new OrderCodeGenerator();

    private Logger logger = LoggerFactory.getLogger(OrderServiceHandle.class);

    // 按照线程数初始化倒计数器,倒计数器
    private CountDownLatch cdl = null;

    private Lock lock = null;

    public OrderServiceHandle(CountDownLatch cdl, Lock lock) {
        this.cdl = cdl;
        this.lock = lock;
    }

    // 创建订单
    public void createOrder() {
        String orderCode = null;

        //准备获取锁
        lock.lock();
        try {
            // 获取订单编号
            orderCode = ong.getOrderCode();
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            //完成业务逻辑以后释放锁
            lock.unlock();
        }

        // ……业务代码

        logger.info("insert into DB使用id:=======================>" + orderCode);
    }


    @Override
    public void run() {
        try {
            // 等待其他线程初始化
            cdl.await();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 创建订单
        createOrder();
    }
}

1.3.1.3 订单号生成类OrderCodeGenerator
package com.ningzhaosheng.distributelock.zookeeper;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author ningzhaosheng
 * @date 2024/4/17 21:44:06
 * @description 生成订单号
 */
public class OrderCodeGenerator {
    // 自增长序列
    private static int i = 0;

    // 按照“年-月-日-小时-分钟-秒-自增长序列”的规则生成订单编号
    public String getOrderCode() {
        Date now = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        return sdf.format(now) + ++i;
    }
}

1.3.1.4 分布式锁测试类TestZookeeperDistributedLock
package com.ningzhaosheng.distributelock.zookeeper;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;

/**
 * @author ningzhaosheng
 * @date 2024/4/17 21:48:28
 * @description zookeeper分布式锁测试类
 */
public class TestZookeeperDistributedLock {
    public static void main(String[] args) {
        // zookeeper 地址
        String ZOOKEEPER_IP_PORT = "192.168.31.9:2181";
        // zookeeper 锁目录
        String LOCK_PATH = "/LOCK";
        // 线程并发数
        int NUM = 10;

        CountDownLatch cdl = new CountDownLatch(NUM);
        for (int i = 1; i <= NUM; i++) {
            // 按照线程数迭代实例化线程
            Lock lock = new ZookeeperDistributedLock(ZOOKEEPER_IP_PORT, LOCK_PATH);
            new Thread(new OrderServiceHandle(cdl, lock)).start();
            // 创建一个线程,倒计数器减1
            cdl.countDown();
        }
    }
}

1.3.1.5 测试效果

从上图执行结果中可以看出,在多线程情况下,分布式锁获取和释放正常。

1.3.2 基于Apache Curator 框架调用

1.3.2.1 maven依赖
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>5.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.2.0</version>
        </dependency>
1.3.2.2 代码实现

这里模拟业务使用分布式锁,还是使用的OrderServiceHandle类,这里只给出分布式锁实现类和测试类,不再给出OrderServiceHandle代码,可以参考上一小节的OrderServiceHandle类。

1.3.2.2.1 分布式锁类CuratorDistributeLock
package com.ningzhaosheng.distributelock.zookeeper.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @author ningzhaosheng
 * @date 2024/4/17 22:03:45
 * @description 实现Lock接口(其实可以不用,可以直接使用InterProcessMutex,这里是为了和jvm的Lock锁保持一致,所以做了一层封装)
 */
public class CuratorDistributeLock implements Lock {

    private CuratorFramework client;
    private InterProcessMutex mutex;

    public CuratorDistributeLock(String connString, String lockPath) {
        this(connString, lockPath, new ExponentialBackoffRetry(3000,5));
    }

    public CuratorDistributeLock(String connString, String lockPath, ExponentialBackoffRetry retryPolicy) {
        try {
            client = CuratorFrameworkFactory.builder()
                    .connectString(connString)
                    .retryPolicy(retryPolicy)
                    .build();
            client.start();

            mutex = new InterProcessMutex(client, lockPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void lock() {
        try {
            // 获取锁
            mutex.acquire();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        try {
            // 释放锁
            mutex.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

1.3.2.2.2 测试类TestCuratorDistributedLock
package com.ningzhaosheng.distributelock.zookeeper.curator;

import com.ningzhaosheng.distributelock.zookeeper.OrderServiceHandle;

import java.util.concurrent.CountDownLatch;

/**
 * @author ningzhaosheng
 * @date 2024/4/17 21:54:33
 * @description 基于 apache curator分布式锁测试类
 */
public class TestCuratorDistributedLock {
    private static final String ZK_ADDRESS = "192.168.31.9:2181";
    private static final String LOCK_PATH = "/distributed_lock";

    public static void main(String[] args) {

        int NUM = 10;
        CountDownLatch cdl = new CountDownLatch(NUM);
        for (int i = 1; i <= NUM; i++) {
            // 按照线程数迭代实例化线程
            /** 创建CuratorDistributeLock
             * 其实可以不用,可以直接使用InterProcessMutex,这里是为了和jvm的Lock锁保持一致,所以做了一层封装
             */
            CuratorDistributeLock curatorDistributeLock = new CuratorDistributeLock(ZK_ADDRESS,LOCK_PATH);
            new Thread(new OrderServiceHandle(cdl, curatorDistributeLock)).start();
            // 创建一个线程,倒计数器减1
            cdl.countDown();
        }
    }
}

1.3.2.3 执行结果

从执行结果可以看出,基于apche curator框架实现zookeeper锁,它也是按照临时顺序节点的顺序获取锁的,每次获得锁的节点都是最小顺序节点,然后等待锁的线程,会基于watcher机制,每次给最小临时顺序节点加回调,监听节点的变更(即释放锁的线程会删除节点),然后再重新判断最小临时顺序节点,最小的获得锁执行,依次循环完成。

好了,本次内容就分享到这,欢迎关注本博主。如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

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

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

相关文章

重生奇迹mu坐骑怎么升级

重生奇迹mu坐骑怎么升级 1、前期&#xff0c;都是主线任务&#xff0c;我们必须要跟着主线任务走&#xff0c;前面的话升级一次需要的经验很少的&#xff0c;一天下来可以升级100级是轻轻松松的&#xff0c;主线任务是比较多的&#xff0c;我们跟着任务一直做差不多可以到150级…

陇剑杯 省赛 攻击者1 CTF wireshark 流量分析

陇剑杯 省赛 攻击者1 题目 链接&#xff1a;https://pan.baidu.com/s/1KSSXOVNPC5hu_Mf60uKM2A?pwdhaek 提取码&#xff1a;haek ├───LogAnalize │ ├───linux简单日志分析 │ │ linux-log_2.zip │ │ │ ├───misc日志分析 │ │ acce…

MATLAB实现遗传算法优化BP神经网络预测数值(GABP)

遗传算法&#xff08;Genetic Algorithm, GA&#xff09;和反向传播&#xff08;Back Propagation, BP&#xff09;神经网络是两种强大的算法&#xff0c;分别用于优化和机器学习。将遗传算法与BP神经网络结合&#xff0c;可以利用遗传算法的全局搜索能力来优化BP神经网络的初始…

CAP实战Demo理论

问题 分布式微服务研发总会遇到CAP理论相关&#xff0c;但是没有相应例子&#xff0c;总是遗忘。 实践 实验 节点1 含有App1 Data1 节点2 含有App2 Data1 当节点1写请求执行成功&#xff0c;节点1数据为Data2&#xff0c;网络不通无法同步时 节点2 发送读请求 满足A可用性…

HBase安装部署

Apache HBase是按列存储数据的NoSQL类型数据库&#xff0c;其数据文件是存储在Hadoop集群中&#xff0c;支持数据以及服务的高可用性以及支持集群节点的大规模可扩展性&#xff0c;本文主要描述HBase的安装部署。 如上所示&#xff0c;HBase的总体架构&#xff0c;HBase Master…

「GO基础」变量

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【java】(软考)面向对象---责任链解析

目录 责任链的意义 手写笔记 ​编辑 责任链的意义 当您把请求给出时&#xff0c;如果某对象不能实现您的操作&#xff0c;责任链会自动把您的请求传给它的下一级 从而避免请求的发送者和接受者之间的耦合关系 这里以2007年下半年试题七进行说明 题目描述 某企业的采购审批…

EFK安装与使用!!!

一、将你的项目进行打包。 二、上传到docker&#xff0c; 启动项目 三、修改前端的代理路径 四、EFK相关配置 1、docker-compose.yml&#xff1a; version: 3 services:kibana:image: kibana:7.14.0ports:- "5601:5601"environment:- ELASTICSEARCH_HOSTShttp://19…

Excel数据处理:使用条件格式

选中你要特别体现的列 然后在开始处点击条件格式 标记大于1500000的值 点击突出显示 点击大于 输入1500000 设置单元格格式 点击条件格式 点击清除 去掉表头的格式 标记重复值强调显示 公式的使用 要去锁定单元格的值&#xff08;加上F4&#xff09; 比较后一列相同位置…

数据结构(七)——B树和B+树

7.4.1_1 B树 5叉查找树 //5叉排序树的结点定义 struct Node {ElemType keys[4]; //最多4个关键字struct Node &child[5]; //最多5个孩子int num; //结点中有几个关键字 }; 如何保证查找效率&#xff1f; eg:对于5叉排序树&#xff0c;规定…

kafka学习1 - 线程、进程消息通信方式、JMS模型、Kafka原理图

1、线程和线程之间的数据交互 两个不同的线程之间是可以通过堆内存的方式进行数据交互的&#xff1b; T1线程的数据发送得到堆内存&#xff0c;T2线程就可以共享堆内存的数据。 存在的问题&#xff1a; T1线程发送数据的速率是50/s&#xff0c;T2线程消费的速率是30/s&#…

Jmeter-非GUI模式下运行jmeter脚本-适用于服务器上持续集成测试

背景 大部分Jmeter脚本都是部署在Linux上运行&#xff0c;利用Jenkins做接口自动化&#xff0c;定时巡检任务。 执行命令 1.进入jmeter的目录&#xff0c;bin文件夹 cd C:\path\to\jmeter\bin2.运行脚本文件 jmeter -n -t D:\{脚本文件目录}\xxx.jmx -l D:\{脚本文件目录}…

mac可以玩steam吗 mac安装steam教程 苹果电脑能打steam游戏吗 苹果电脑怎么安装windows 苹果mac电脑配置AI功能的M4芯片

众所周知&#xff0c;Steam作为一个热门的游戏平台&#xff0c;深受国内外玩家的喜爱&#xff0c;平台中包含了无数的游戏&#xff0c;在作战时玩家们能够与朋友们互动聊天&#xff0c;还能匹配好友组队&#xff0c;同时还能增进与同伴的默契度。 但是最近有玩家们提问说&#…

Python编程技巧揭秘:深入理解Lambda函数,如何使用匿名函数简化你的代码

文章目录 1. Lambda函数2. 在实际应用中使用Lambda2.1 使用Lambda函数进行列表排序2.2 在高阶函数中使用Lambda 3. Lambda的局限性和注意点 在这篇文章中&#xff0c;将深入探讨Python中的Lambda函数&#xff0c;这是一种强大的编程工具&#xff0c;可以以更简洁、高效的方式编…

C语言:内存动态开辟

一、malloc和free 1.如果开辟成功&#xff0c;则返回一个指向开辟好空间的指针。 2.如果开辟失败&#xff0c;则返回一个NULL指针&#xff0c;因此malloc的返回值一定要做检查。 3.返回值的类型是 void* &#xff0c;所以malloc函数并不知道开辟空间的类型&#xff0c;具体在…

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录

【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 一、检查服务是否开启 找到 【Docker Desktop Service】&#xff0c;然后&#xff0c;启动他&#xff1b; 你也可以直接设置为“自动” 找到服务&#xff0c;右键》属性》启动类型&#xff1a;自动》点击…

java解决常见递归问题

最基本的&#xff0c;斐波那契数列&#xff0c;阶乘&#xff08;0&#xff0c;1的阶乘均为1&#xff09; 返回字母“x”第一次出现的位置 使用递归编写一个函数&#xff0c;读取一个字符串&#xff0c;返回字母“x”第一次出现的位置。例如&#xff0c;字符串 "abcdefgh…

014Node.js时间格式包silly-datetime安装与使用

下载&#xff1a; https://www.npmjs.com/网站上下载silly-datetime 安装 npm i silly-datetime --save var sd require(silly-datetime);console.log(new Date()); //2024-04-18T04:40:38.505Zvar dsd.format(new Date(), YYYY-MM-DD HH:mm);console.log(d); //2024…

B树(B-tree)

B树(B-tree) B树(B-tree)是一种自平衡的多路查找树&#xff0c;主要用于磁盘或其他直接存取的辅助存储设备 B树能够保持数据有序&#xff0c;并允许在对数时间内完成查找、插入及删除等操作 这种数据结构常被应用在数据库和文件系统的实现上 B树的特点包括&#xff1a; B树为…

前端常用的数据加密方式

前端开发中&#xff0c;数据安全是至关重要的一个方面。数据加密是保护用户隐私和信息安全的关键方法之一。 前端常用的数据加密方式涵盖了对传输数据的加密、存储数据的加密以及客户端与服务器端之间通信的加密。 1. 对称加密算法 对称加密算法使用相同的密钥进行加密和解密…