ZooKeeper Distributed topics and introduction to Dubbo microservices

Zookeeper implements distributed locks

What multithreading

Multithreading In order to improve the running efficiency of application programs, there are multiple execution paths in a process, which are executed in parallel without affecting each other.

Here is not much about the introduction of threads, to learn more about threads please go to github.com/haoxiaoyong…

Below we only for distributed environment implementation of distributed lock introduction;

What is the Java Memory model

The shared Memory model refers to the Java Memory model (JMM), which determines that a shared variable written by one thread can be visible to another thread. From an abstract point of view, JMM defines an abstract relationship between threads and main memory: Shared variables between threads are stored in main memory, and each thread has a private local memory where it stores copies of shared variables to read/write. Local memory is an abstraction of the JMM and does not really exist. It covers caches, write buffers, registers, and other hardware and compiler optimizations

From the above figure, thread A and thread B must go through the following two steps in order to communicate:

  1. First, thread A flusher the updated shared variables from local memory A to main memory.

  2. Thread B then goes into main memory to read the shared variables that thread A has updated previously.

As shown in the figure above, local memory A and B have copies of the shared variable X in main memory. So let’s say that at the beginning, all three of these memory x values are 0. When thread A executes, it temporarily stores the updated x value (suppose 1) in its local memory, A. When thread A and thread B need to communicate, thread A will first refresh the modified X value in its local memory to the main memory, and the x value in the main memory becomes 1. Thread B then goes to main memory to read thread A’s updated x value, and thread B’s local memory x value also changes to 1. Taken as A whole, these two steps are essentially thread A sending messages to thread B, and this communication must go through main memory. The JMM provides memory visibility assurance for Java programs by controlling the interaction between main memory and the local memory of each thread.

Summary: What is the Java Memory Model? The Java Memory Model, or JMM, defines the visibility of one thread to another. Shared variables are stored in main memory, and each thread has its own local memory. When multiple threads access the same data at the same time, local memory may not be flushed to main memory in time, so thread-safety issues can occur.

Traditionally, order ID is generated

Generate order class

public class OrderNumGenerator {

    // Global order id;
    public static int count = 0;

    public String getNumber(a) {

        try {
            //TimeUnit.SECONDS.sleep(2);
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        return simpt.format(new Date()) + "-"+ ++count; }}Copy the code

Order number generation using multithreading simulation

public class OrderService implements Runnable {

    private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();

    public void run(a) {
        getNumber();
    }

    public void getNumber(a) {
        String number = orderNumGenerator.getNumber();
        System.out.println(Thread.currentThread().getName() + ", generate order ID: + number);
    }

    public static void main(String[] args) {
        System.out.println("#### generates unique order number ###");
        for (int i = 0; i < 100; i++) {
            new Thread(newOrderService()).start(); }}}Copy the code

This is where thread safety comes in;

There are many ways to solve this thread safety problem

For example, use synchronized or lock

Synchronized is omitted here. To learn more about the semantics and use of synchronized, go to github.com/haoxiaoyong…

Using lock locks to solve thread safety problems:

Generate order class

public class OrderNumGenerator {

    // Global order id;
    public static int count = 0;

    public String getNumber(a) {

        try {
            //TimeUnit.SECONDS.sleep(2);
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        SimpleDateFormat simpt = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        return simpt.format(new Date()) + "-"+ ++count; }}Copy the code

No changes have been made;

Using multithreading simulation to generate order numbers (lock):

public class OrderService implements Runnable {

    private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();

    // Use lock to lock
    private java.util.concurrent.locks.Lock lock = new ReentrantLock();

    public void run(a) {
        getNumber();
    }

    public void getNumber(a) {
        try {
            lock.lock();
            String number = orderNumGenerator.getNumber();
            System.out.println(Thread.currentThread().getName() + ", generate order ID: + number);
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public static void main(String[] args) {
        System.out.println("#### generates unique order number ###");
        OrderService orderService = new OrderService();
        for (int i = 0; i < 100; i++) {
            newThread(orderService).start(); }}}Copy the code

Compare and contrast with before and those changes?

lock.lock(); Locked,

lock.unlock(); Release the lock

Also notice the OrderService object in the main method; It’s only instantiated once;

It prints perfectly to 100;

The following describes generating order ids in a distributed environment;

In a distributed (clustered) environment, synchronization cannot be achieved for each JVM, and the use of timestamps to generate order numbers in distributed scenarios may be repeated

Use distributed lock generation order number technology

1. Disadvantages of using a database to implement distributed locks: Poor performance and deadlocks are easy to occur when threads are abnormal 2. Using Redis to realize distributed lock disadvantages: lock failure time is difficult to control, prone to deadlock, non-blocking, non-reentrant 3. Zookeeper distributed lock implementation is relatively simple, reliable, using temporary nodes, easy to control the failure time

What is distributed locking

Distributed locks are generally used in distributed systems or multiple applications to control whether the same task is executed or the task execution sequence. In the project, multiple Tomcat applications are deployed, and the same task may be executed several times during scheduled task execution. We can use distributed locking to ensure that only one Tomcat application executes scheduled task at the same time

Use Zookeeper to implement distributed locks

Zookeeper implements distributed locking

Using zookeeper node to implement distributed lock, create a temporary sequence is suitable for the sequential program, general idea is to create a temporary sequence nodes, find out the minimum sequence nodes, access to distributed lock, disappear after completion of the program execution sequence nodes, through the watch to monitor the change of the nodes, find the smallest sequence from the rest of the node, Obtain distributed locks, perform corresponding processing, and so on…

Add the dependent

<dependency>
   <groupId>com.101tec</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.10</version>
</dependency>
Copy the code

Create Lock interface

public interface Lock {

    // Get the lock resource
    void getLock(a);
    / / releases the lock
    void unLock(a);

}
Copy the code

Create the ZookeeperAbstractLock abstract class

public abstract class ZookeeperAbstractLock implements Lock {

    // zk connection address
    private static final String CONNECTSTRING = "127.0.0.1:2181";

    // Create a ZK connection
    protected ZkClient zkClient = new ZkClient(CONNECTSTRING);

    protected static final String PATH = "/lock";

    public void getLock(a){
        if(tryLock()){
            System.out.println("## Get lock resources ####");
        }else {
            / / wait for
            waitLock();
            // Retrieve the resourcegetLock(); }}// Get the lock resource
    abstract boolean tryLock(a);

    / / wait for
    abstract void waitLock(a);

    public void unLock(a) {
        if(zkClient ! =null) {
            zkClient.close();
            System.out.println("Release lock resources..."); }}}Copy the code

ZookeeperDistrbuteLock class

public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {

    private CountDownLatch countDownLatch = null;

    boolean tryLock(a) {

        try {
            zkClient.createEphemeral(PATH);
            return true;
        } catch (Exception e) {
// e.printStackTrace();
            return false; }}void waitLock(a) {
        IZkDataListener izkDataListener = new IZkDataListener() {

            public void handleDataDeleted(String path) throws Exception {
                // Wake up the waiting thread
                if(countDownLatch ! =null) { countDownLatch.countDown(); }}public void handleDataChange(String path, Object data) throws Exception {}};// Register events
        zkClient.subscribeDataChanges(PATH, izkDataListener);
        if (zkClient.exists(PATH)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch(Exception e) { e.printStackTrace(); }}// Delete the listenerzkClient.unsubscribeDataChanges(PATH, izkDataListener); }}Copy the code

Run the Zookeeper lock

public class OrderService implements Runnable {
    
   private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
   // Use lock to lock
   // private java.util.concurrent.locks.Lock lock = new ReentrantLock();
   private Lock lock = new ZookeeperDistrbuteLock();
   public void run(a) {
      getNumber();
   }
   public void getNumber(a) {
      try {
         lock.getLock();
         String number = orderNumGenerator.getNumber();
         System.out.println(Thread.currentThread().getName() + ", generate order ID: + number);
      } catch (Exception e) {
         e.printStackTrace();
      } finally{ lock.unLock(); }}public static void main(String[] args) {
      System.out.println("#### generates unique order number ###");
// OrderService orderService = new OrderService();
      for (int i = 0; i < 100; i++) {
         new Thread( newOrderService()).start(); }}}Copy the code

Execute the main method:

## Get lock lock resources ####2019-08-19-22-32-50-1 Create order ID:2019-08-19-22-32-50-1 Create order ID:2019-08-19-22-32-50-1 Create order ID:2019-08-19-22-32-50-1 Create order ID...## Get lock lock resources ####2019-08-19-22-32-59-2...## Get lock lock resources ####Order ID:2019-08-19-22-33-08-3 Release lock resources...## Get lock lock resources ####2019-08-19-22-33-17-4...## Get lock lock resources ####2019-08-19-22-33-26-5 Order ID:2019-08-19-22-33-26-5## Get lock lock resources ####2019-08-19-22-33-35-6 Release lock resources...## Get lock lock resources ####2019-08-19-22-33-44-7 Release the lock resource...## Get lock lock resources ####2019-08-19-22-33-53-8...## Get lock lock resources ####Thread-17, 34-02-9 :2019-08-19-22-34-02-9## Get lock lock resources ####2019-08-19-22-11-10 ThREAD-19, 34-34-11-10## Get lock lock resources ####...Copy the code