e.g. SELECT * FROM distributed_lock WHERE business_code='demo' FOR UPDATE
Pros and Cons
Pros:
Easy to build
Cons:
Big pressure on database if there are high number of concurrent requests. Recommend to separate the business logic DB and lock DB
Example
// DistributeLockMapper.xml <selectid="selectDistributeLock"resultType="com.example.distributelock.model.DistributeLock"> select * from distribute_lock where business_code = #{businessCode,jdbcType=VARCHAR} for update </select>
@Slf4jpublicclassZkLockimplementsAutoCloseable,Watcher{privateZooKeeper zooKeeper;privateString znode;publicZkLock() throwsIOException {this.zooKeeper=newZooKeeper("localhost:2181",10000,this); }publicbooleangetLock(String businessCode) {try {// Create business root node, e.g. /rootStat stat =zooKeeper.exists("/"+ businessCode,false);if (stat==null) {zooKeeper.create("/"+ businessCode,businessCode.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); }// Create temporary sequential node /order/order_00000001 znode =zooKeeper.create("/"+ businessCode +"/"+ businessCode +"_",businessCode.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);// Get all nodes under business nodeList<String> childrenNodes =zooKeeper.getChildren("/"+ businessCode,false);// Sort children nodes under rootCollections.sort(childrenNodes);// Obtain the node which has the least sequential numberString firstNode =childrenNodes.get(0);// If the node created is the first one, then get the lockif (znode.endsWith(firstNode)) {returntrue; }// If not the first child node, then monitor the previous nodeString lastNode = firstNode;for (String node:childrenNodes) {if (znode.endsWith(node)) {// watch parameter is implemented in the process method belowzooKeeper.exists("/"+businessCode+"/"+lastNode, watch:true);break; }else { lastNode = node; } }// Wait for the previous node to release// This is synchronized (this) {wait(); }returntrue; } catch (Exception e) {e.printStackTrace(); }returnfalse; } @Overridepublicvoidclose() throwsException { zooKeeper.delete(znode, -1); // path, version: version is to avoid deleting wrong node. Passing -1 here because it is not used before at all
zooKeeper.close();log.info("I have unlocked!"); } @Overridepublicvoidprocess(WatchedEvent event) {// Only get notification when the previous node get deleted. if (event.getType() ==Event.EventType.NodeDeleted) {synchronized (this) {notify(); } } }}@Slf4jpublicclassZookeeperController{ @AutowiredprivateCuratorFramework client; @RequestMapping("zkLock")publicStringzookeeperLock() {log.info("entered method!");try (ZkLock zkLock =newZkLock()) {if (zkLock.getLock("order")) {log.info("get the lock");Thread.sleep(10000); } } catch (IOException e) {e.printStackTrace(); } catch (Exception e) {e.printStackTrace(); }log.info("finish method execution!");return"finish method execution!"; }}
Curator
Motivation: Curator encapsulates the one-time watch logic so easier to use.
There are three methods which could set watcher: GetData(); getChildren(); exists().
Whenever there is a change to the watched data, the result will be returned to client.