分布式锁

分布式锁,这个主要得益于 ZooKeeper 为我们保证了数据的强一致性。锁服务可以分为两类,一个是 保持独占,另一个是 控制时序。

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

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

分布式锁    单纯的Lock锁或者synchronize只能解决单个jvm线程安全问题

分布式 Session 一致性问题

分布式全局id(也可以使用分布式锁)

分布式锁,产生的原因是 集群

在单台服务器上 如何生成订单号(保证唯一),方案 UUid+时间戳方式, redis方式

生成订单号, 秒杀抢购时候,

首先预测100w订单号,生成放在redis。客户端下单,直接redis去获取即可。因为redis单线程的,多个线程去获取时候,安全呀。

实际150w用户。当redis剩下50w订单号时候,继续生成补充之。

如果在集群情况,UUid+时间戳。不能保证唯一性!,原因:

如果单台:

uuid+时间戳,生成的代码逻辑:

package com.toov5.Lock;

import java.text.SimpleDateFormat;
import java.util.Date; //生成订单号 时间戳
public class OrderNumGenerator {
//区分不同的订单号
private static int count = 0;
//单台服务器,多个线程 同事生成订单号
public String getNumber(){
try {
Thread.sleep(300);
} catch (Exception e) {
// TODO: handle exception
}
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count; //时间戳后面加了 count } }

开启100个线程调用之:

package com.toov5.Lock;

public class OrderService implements  Runnable {

     private OrderNumGenerator    orderNumGenerator  = new OrderNumGenerator(); //定义成全局的

     public void run() {
getNumber();
} public void getNumber(){
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName()+"num"+number);
} public static void main(String[] args) {
OrderService orderService = new OrderService();
for (int i = 0; i <100; i++) { //开启100个线程
new Thread(orderService).start();
}
} }

结果:

多个线程共享区同一个全局变量,线程安全问题!

解决方案就是加锁嘛!

或者使用 lock锁也可以

public class OrderService implements Runnable {
private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
// 使用lock锁
private java.util.concurrent.locks.Lock lock = new ReentrantLock(); public void run() {
getNumber();
} public void getNumber() {
try {
// synchronized (this) {
lock.lock();
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",生成订单ID:" + number);
// } } catch (Exception e) { } finally {
lock.unlock();
}
} public static void main(String[] args) {
System.out.println("####生成唯一订单号###");
OrderService orderService = new OrderService();
for (int i = 0; i < 100; i++) {
new Thread(orderService).start();
} }
}

如果是集群环境下:

  

每台jvm都有一个 count   都有 自增的代码 操作这个 count  三个不同的jvm 独立的  用户请求 过来 映射到哪个 就操作哪个

这时候就产生分布式锁的问题

这时候需要分布式锁:共享一个count

jvm1 操作时候 其他的jvm2 和 jvm3 不可以操作他!

分布式锁  保证分布式领域中共享数据安全问题

1、数据库实现(效率低,不推荐)

2、redis实现(使用redission实现,但是需要考虑思索,释放问题。繁琐一些)

3、Zookeeper实现   (使用临时节点,效率高,失效时间可以控制)

 4、Spring Cloud 实现全局锁(内置的)

业务场景

在分布式情况,生成全局订单号ID

产生问题

在分布式(集群)环境下,每台JVM不能实现同步,在分布式场景下使用时间戳生成订单号可能会重复

分布式情况下,怎么解决订单号生成不重复

  1. 使用分布式锁
  2. 提前生成好,订单号,存放在redis取。获取订单号,直接从redis中取。

使用分布式锁生成订单号技术

1.使用数据库实现分布式锁

缺点:性能差、线程出现异常时,容易出现死锁

2.使用redis实现分布式锁

缺点:锁的失效时间难控制、容易产生死锁、非阻塞式、不可重入

3.使用zookeeper实现分布式锁

实现相对简单、可靠性强、使用临时节点,失效时间容易控制

什么是分布式锁

分布式锁一般用在分布式系统或者多个应用中,用来控制同一任务是否执行或者任务的执行顺序。在项目中,部署了多个tomcat应用,在执行定时任务时就会遇到同一任务可能执行多次的情况,我们可以借助分布式锁,保证在同一时间只有一个tomcat应用执行了定时任务

使用Zookeeper实现分布式锁

Zookeeper实现分布式锁原理

使用zookeeper创建临时序列节点来实现分布式锁,适用于顺序执行的程序,大体思路就是创建临时序列节点,找出最小的序列节点,获取分布式锁,程序执行完成之后此序列节点消失,通过watch来监控节点的变化,从剩下的节点的找到最小的序列节点,获取分布式锁,执行相应处理,依次类推……

如何使用zk实现分布式锁?

临时节点

持久节点

分布式锁使用 临时节点,实现:

实现步骤:

多个Jvm同时在Zookeeper上创建同一个相同的节点( /Lock)

zk节点唯一的! 不能重复!节点类型为临时节点, jvm1创建成功时候,jvm2和jvm3创建节点时候会报错,该节点已经存在。这时候 jvm2和jvm3进行等待。

jvm1的程序现在执行完毕,执行释放锁。关闭当前会话。临时节点不复存在了并且事件通知Watcher,jvm2和jvm3继续创建。

ps:zk强制关闭时候,通知会有延迟。但是close()方法关闭时候,延迟小

如果程序一直不处理完,可能导致思索(其他的一直等待)。设置有效期~  直接close()掉 其实连接也是有有效期设置的 大家可以找下相关资料看下哦

上代码!

引入Jar包:

<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.toov5.FbsLock</groupId>
<artifactId>FbsLock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
</dependencies> </project>

 创建锁的接口

package com.toov5.Lock;

public interface ExtLock {

    //ExtLock基于zk实现分布式锁
public void getLock(); //释放锁
public void unLock(); }

模板方法模式

package com.toov5.Lock;

import org.I0Itec.zkclient.ZkClient;

//将重复代码抽象到子类中(模板方法设计模式)
public abstract class ZookeeperAbstractLock implements ExtLock {
private static final String CONNECTION="192.168.91.5:2181";
protected ZkClient zkClient = new ZkClient(CONNECTION);
private String lockPath="/lockPath"; //获取锁
public void getLock() {
//1、连接zkClient 创建一个/lock的临时节点
// 2、 如果节点创建成果,直接执行业务逻辑,如果节点创建失败,进行等待
if (tryLock()) {
System.out.println("#####成功获取锁######");
}else {
//进行等待
waitLock();
} //3、使用事件通知监听该节点是否被删除 ,如果是,重新进入获取锁的资源 } //创建失败 进行等待
abstract void waitLock(); abstract boolean tryLock(); //释放锁
public void unLock() {
//执行完毕 直接连接
if (zkClient != null) {
zkClient.close();
System.out.println("######释放锁完毕######");
} } }

创建子类实现上面的 抽象方法

package com.toov5.Lock;

import java.util.concurrent.CountDownLatch;

import org.I0Itec.zkclient.IZkDataListener;
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock { @Override
boolean tryLock() {
try {
zkClient.createEphemeral(lockPath);
// System.out.println("#########获取锁######");
return true;
} catch (Exception e) {
// 如果失败 直接catch
return false;
}
} @Override
void waitLock() { IZkDataListener iZkDataListener = new IZkDataListener() { // 节点被删除
public void handleDataDeleted(String arg0) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown(); // 计数器为0的情况,await 后面的继续执行
} } // 节点被修改
public void handleDataChange(String arg0, Object arg1) throws Exception { }
}; // 监听事件通知
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
// 控制程序的等待
if (zkClient.exists(lockPath)) { //如果 检查出 已经被创建了 就new 然后进行等待
countDownLatch = new CountDownLatch(1);
try {
countDownLatch.wait(); //等待时候 就不往下走了 当为0 时候 后面的继续执行
} catch (Exception e) {
// TODO: handle exception
}
}
//后面代码继续执行
//为了不影响程序的执行 建议删除该事件监听 监听完了就删除掉
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener); } }

生产订单号:

package com.toov5.Lock;

import java.text.SimpleDateFormat;
import java.util.Date; //生成订单号 时间戳
public class OrderNumGenerator {
//区分不同的订单号
private static int count = 0;
//单台服务器,多个线程 同事生成订单号
public String getNumber(){
try {
Thread.sleep(500);
} catch (Exception e) { }
SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
return simpt.format(new Date()) + "-" + ++count; //时间戳后面加了 count } }

运行方法:

package com.toov5.Lock;

public class OrderService implements Runnable {

    private OrderNumGenerator orderNumGenerator = new OrderNumGenerator(); // 定义成全局的
private ExtLock lock = new ZookeeperDistrbuteLock(); public void run() {
getNumber();
} public synchronized void getNumber() { // 加锁 保证线程安全问题 让一个线程操作
try {
lock.getLock();
String number = orderNumGenerator.getNumber();
System.out.println(Thread.currentThread().getName() + ",number" + number); } catch (Exception e) { } finally {
lock.unLock();
}
} public static void main(String[] args) {
// OrderService orderService = new OrderService();
for (int i = 0; i < 100; i++) { // 开启100个线程
//模拟分布式锁的场景
new Thread(new OrderService()).start();
}
} }

运行结果:

代码欣赏:

分布式锁(Zookeeper实现)的更多相关文章

  1. [Java复习] 分布式锁 Zookeeper Redis

    一般实现分布式锁都有哪些方式? 使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗? 这两种分布式锁的实现方式哪种效率比较高? 1. Zookeeper 都有哪些使用场 ...

  2. 分布式锁 ----zookeeper实践 (排它锁)

    排它锁概念: Exclusive Locks,被称为X锁,写锁,独占锁.如果事物T1对数据对象O1加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读写操作,其他事务必须等到T1释放锁后才能进 ...

  3. zookeeper 实现分布式锁zookeeper 使用 Curator 示例监听、分布式锁

    下载地址: http://download.csdn.net/download/ttyyadd/10239642

  4. Docker 下的Zookeeper以及.ne core 的分布式锁

    单节点 1.拉取镜像:docker pull zookeeper 2.运行容器 a.我的容器同一放在/root/docker下面,然后创建相应的目录和文件, mkdir zookeeper cd zo ...

  5. Redis、Zookeeper实现分布式锁——原理与实践

    Redis与分布式锁的问题已经是老生常谈了,本文尝试总结一些Redis.Zookeeper实现分布式锁的常用方案,并提供一些比较好的实践思路(基于Java).不足之处,欢迎探讨. Redis分布式锁 ...

  6. Redis分布式锁服务(八)

    阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...

  7. Java分布式锁看这篇就够了

    ### 什么是锁? 在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量. 而同步的本质是通过锁来实现的 ...

  8. Redis分布式锁服务(转)

    原文:http://www.cnblogs.com/mushroom/p/4752499.html 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj ...

  9. 分布式锁-session-事务相关

    1. 分布式锁--zookeeper 1). client调用create()方法创建"/root/lock_"节点,注意节点类型是EPHEMERAL_SEQUENTIAL. 2) ...

随机推荐

  1. Codeforces Round #317 [AimFund Thanks-Round] (Div. 2) Array 模拟

    题目链接:http://codeforces.com/contest/572/problem/A 题意 就给你两个数组,问你能不能从A数组中取出k个,B数组中取出m个,使得这k个都大于这m个. 题解 ...

  2. hdu6219(最大空凸包)

    题意: 给一些点,求出一个最大的空凸包,这个凸包里没有任何给定点且要求这个凸包面积最大 分析: 枚举凸包左下角的点,然后dp[i][j]表示凸包的最后两条边是j->i和i->O情况下凸包的 ...

  3. go 协程与主线程强占运行

    最近在学习了Go 语言 ,  正好学习到了 协程这一块 ,遇到了困惑的地方.这个是go语言官方文档 . 在我的理解当中是,协程只能在主线程释放时间片后才会经过系统调度来运行协程,其实正确的也确实是这样 ...

  4. iOS -- 开源项目和库

    TimLiu-iOS 目录 UI 下拉刷新 模糊效果 AutoLayout 富文本 图表 表相关与Tabbar 隐藏与显示 HUD与Toast 对话框 其他UI 动画 侧滑与右滑返回手势 gif动画 ...

  5. hdu 4300 Clairewd’s message(具体解释,扩展KMP)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4300 Problem Description Clairewd is a member of FBI. ...

  6. jQuery中的:input选择器

    jQuery中的:input选择器 jQuery中的:input选择器包含input, textarea, select 和 button这些标签. <!DOCTYPE html> < ...

  7. tcp ip协议笔记(1)——简单介绍

    前言 本人记性不佳,看书健忘,以此笔记来记录看书后自己所知所想,已达到加深对tcp ip的理解.本笔记不过我看完书后自己所写的总结,权当是书后复习. 一.为什么会有tcp ip协议        ...

  8. 使用canvas 的api 实现 图片的显示 及 压缩

    在移动端压缩图片并且上传主要用到filereader.canvas 以及 formdata 这三个h5的api.逻辑并不难.整个过程就是: (1)用户使用input file上传图片的时候,用file ...

  9. 简学lingo(四)——实例篇

    本片将会接着上篇实例来写,如要參照能够看下上篇的http://blog.csdn.net/yzu_120702117/article/details/38453791 如有句法上的问题能够參照我之前总 ...

  10. 【CODEFORCES】 B. Dreamoon and Sets

    B. Dreamoon and Sets time limit per test 1 second memory limit per test 256 megabytes input standard ...