1. Business scenario
We usually have an order number when shopping. How to ensure the uniqueness of the order number in the case of high concurrency? For example, we should not only ensure the reliability of performance (distributed) but also ensure that duplicate order numbers are not generated. At this time, we need to use distributed locks. The implementation method of distributed locks introduced here is to use Zookeeper. We will not introduce Zookeeper too much here, but mainly look at how to implement distributed locks.
2. What is a distributed lock
Distributed lock is a way to control synchronous access to shared resources between distributed systems. In distributed systems, it is often necessary to coordinate their actions. If different systems or different hosts of the same system share one or a group of resources, they often need to be mutually exclusive to prevent interference with each other to ensure consistency. In this case, distributed locks need to be used. (quoted from Baidu Encyclopedia)
3. Use Zookeeper to realize distributed locking
Use zookeeper to create temporary sequence nodes to realize distributed locks. It is suitable for sequential execution. The general idea is to create temporary sequence nodes, find the smallest sequence node and obtain distributed locks. After the program execution is completed, the sequence node disappears. Monitor the changes of nodes through watch, find the smallest sequence node from the rest of nodes and obtain distributed locks, Execute the corresponding processing, and so on... (for simplicity, we only create temporary nodes without judging the order)
4. Practical projects
Maven dependency:
<dependencies> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency> </dependencies>
OrderNumGenerator:
//Generate order class public class OrderNumGenerator { //Global order id public static int count = 0; public String getNumber() { SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); return simpt.format(new Date()) + "-" + ++count; } }
Lock:
/** * Custom distributed lock */ public interface Lock { // Acquire lock void getLock(); // Release lock void unlock(); }
ZookeeperAbstractLock:
/** * Refactor the duplicate code and hand over the duplicate code to the subclass for execution */ public abstract class ZookeeperAbstractLock implements Lock { // zk connection address private static final String CONNECTSTRING = "127.0.0.1:2181"; // Create zk connection protected ZkClient zkClient = new ZkClient(CONNECTSTRING); protected static final String PATH = "/lock"; @Override public void getLock() { if (tryLock()) { System.out.println("obtain zk Lock successful"); } else { // wait for waitLock(); // Reacquire lock getLock(); } } // Waiting lock protected abstract void waitLock(); // Whether to acquire the lock successfully. If successful, return true. If failed, return false protected abstract boolean tryLock(); @Override public void unlock() { if (zkClient != null) { zkClient.close(); System.out.println("Close connection release lock resource..."); } } }
ZookeeperDistrbuteLock:
public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock { private CountDownLatch countDownLatch = null; @Override protected boolean tryLock() { try { zkClient.createEphemeral(PATH); return true; } catch (RuntimeException e) { return false; } } @Override protected void waitLock() { // Use event listening to get the notification of node deletion IZkDataListener iZkDataListener = new IZkDataListener() { // When a node is deleted @Override public void handleDataDeleted(String s) throws Exception { System.out.println("The node was deleted:" + s); if (countDownLatch != null) { // awaken countDownLatch.countDown(); } } // When a node changes @Override public void handleDataChange(String s, Object o) throws Exception { } }; // Registration node information acquisition event notification zkClient.subscribeDataChanges(PATH, iZkDataListener); if (zkClient.exists(PATH)) { // Create semaphore countDownLatch = new CountDownLatch(1); try { countDownLatch.await(); } catch (Exception e) { } } // Delete event notification zkClient.unsubscribeDataChanges(PATH, iZkDataListener); } }
OrderSerice:
// Order generation calls business logic public class OrderSerice implements Runnable { // Generate order number OrderNumGenerator orderNumGenerator = new OrderNumGenerator(); private ZookeeperDistrbuteLock lock = new ZookeeperDistrbuteLock(); @Override public void run() { getnNumber(); } private void getnNumber() { try { lock.getLock(); // Simulate user generated order number String number = orderNumGenerator.getNumber(); System.out.println("Current thread:" + Thread.currentThread().getName() + " Generated order number:" + number); } catch (Exception e) { e.printStackTrace(); }finally { // Release lock lock.unlock(); } } public static void main(String[] args) { System.out.println("Simulation generated order number"); for (int i = 0; i < 100; i++) { new Thread(new OrderSerice()).start(); } } }