1.四种方法保证数据安全
分布式锁 保证分布式领域中共享数据安全问题
1、数据库实现(效率低,不推荐)
2、redis实现(使用redission实现,但是需要考虑思索,释放问题。繁琐一些)
3、Zookeeper实现 (使用临时节点,效率高,失效时间可以控制)
4、Spring Cloud 实现全局锁(内置的)
2.应用场景
在分布式情况,生成全局订单号ID
3.什么是分布式锁
分布式锁一般用在分布式系统或者多个应用中,用来控制同一任务是否执行或者任务的执行顺序。在项目中,部署了多个tomcat应用,在执行定时任务时就会遇到同一任务可能执行多次的情况,我们可以借助分布式锁,保证在同一时间只有一个tomcat应用执行了定时任务
4.实现Zookeeper分布式锁流程
- 客户端连接上zookeeper,并在指定节点(locks)下创建临时顺序节点node_n
- 客户端获取locks目录下所有children节点
- 客户端对子节点按节点自增序号从小到大排序,并判断自己创建的节点是不是序号最小的,若是则获取锁;若不是,则监听比该节点小的那个节点的删除事件
- 获得子节点变更通知后重复此步骤直至获得锁;
- 执行业务代码,完成业务流程后,删除对应的子节点释放锁。
5.两种方式实现分布式锁
5.1原生方式
使用java和zk api书写以上逻辑实现分布式锁,操作zookeeper使用的是apache提供的zookeeper的包。通过实现Watch接口,实现process(WatchedEvent event)方法来实施监控,使CountDownLatch来完成监控,在等待锁的时候使用CountDownLatch来计数,等到后进行countDown,停止等待,继续运行。
5.2 Curator方式
Curator是Netflix公司一个开源的zookeeper客户端,在原生API接口上进行了包装,解决了很多ZooKeeper客户端非常底层的细节开发。同时内部实现了诸如Session超时重连,Watcher反复注册等功能,实现了Fluent风格的API接口,是使用最广泛的zookeeper客户端之一。
6.实现锁方法
6.1 普通方法
public class CreateCode {
private static int count = 0;
public static 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
}
}
import com.dyt.utils.CreateCodeUtil;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class OrderService implements Runnable{
//使用lock锁
private Lock lock = new ReentrantLock();
@Override
public void run() {
getNumber();
//getNumber1();
}
/***
* 第一种方式:synchronized
* 第二种方式:lock
*/
// public synchronized void getNumber1(){
// String number = CreateCodeUtil.getNumber();
// System.out.println(Thread.currentThread().getName()+"num"+number);
// }
public void getNumber(){
try {
lock.lock();
String number = CreateCodeUtil.getNumber();
System.out.println(Thread.currentThread().getName()+"num"+number);
}catch (Exception e){
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
OrderService orderService = new OrderService();
for (int i = 0; i < 100; i++) {
new Thread(orderService).start();
}
}
}
6.2 Curator框架实现分布式锁
-
导入依赖
<!--zookeeper的客户端依赖 org.apache.curator是Netflix公司开源的一个Zookeeper客户端, 与zookeeper提供的原生客户端相比,Cutrator的抽象层次更高,简化了 zookeeper客户端的开发量 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> </dependency>
-
zkLock类
package com.dyt.utils; 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.RetryNTimes; import java.util.concurrent.TimeUnit; public class zkLock { private static final String ZK_ADDRESS = "192.168.100.188:2181"; private static final String ZK_LOCK_PATH = "/zklock/lock0"; private static void zklock(){ final CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, new RetryNTimes(10, 5000)); client.start(); System.out.println(client.getState()); System.out.println("zk client start successfully!"); final InterProcessMutex mutex = new InterProcessMutex(client, ZK_LOCK_PATH); for (int i = 0; i < 3; i++) { Runnable myRunnable = new Runnable() { public void run() { doWithLock(client, mutex); } }; Thread thread = new Thread(myRunnable, "Thread-" + i); thread.start(); } } private static void doWithLock(CuratorFramework client, InterProcessMutex mutex) { try { String name = Thread.currentThread().getName(); /** * acquire(long time, TimeUnit unit)方法,该方法有两个入参分别是超时时间和时间单位。当到达超时时间时会抛出异常终止方法继续阻塞 */ if (mutex.acquire(1, TimeUnit.SECONDS)) { System.out.println(name + " hold lock"); System.out.println(client.getChildren().forPath(ZK_LOCK_PATH)); Thread.sleep(5000L); System.out.println(name + " release lock"); } } catch (Exception e) { e.printStackTrace(); } finally { try { mutex.release(); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { zkLock.zklock(); } }
-
启动三个线程去获取锁,线程1获取到锁sleep5秒,超时时间设置为1s,线程0和线程2阻塞等待1s后,便会抛出异常