Dubbo source code learning series integrates the zookeeper registry and provides a watcher mechanism

Posted by HiddenS3crets on Fri, 18 Feb 2022 23:28:30 +0100

1. Replace map with zookeeper registry

In the previous article, the remote registrymap was used to replace the cache of the zookeeper implementation service. The disadvantage of this method is that the map needs to be shared through files. If the list of service provider interfaces changes, it cannot be modified in time. How can we use the zookeeper registry to realize the function of service registration and discovery?

Zookeeper itself is a file system. You can mount the url related information of the service to the specified node directory, and then monitor whether the service provider interface service changes through the watch mechanism. If there are changes, the watcher will receive zookeeper's notification.

2. Integrate curator

curator is the secondary encapsulation of zookeeper client, which provides better convenience in providing client instances. It mainly includes three modules: Framework (core), client (providing client support) and Recipes (cluster management, leader election and distributed lock).

<dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.1.0</version>
        </dependency>

        <!-- curator client-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>4.1.0</version>
        </dependency>

        <!-- leader Election, distributed lock-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
        </dependency>

After adding the above dependencies, we can create a zookeeper client through curator, which can be changed to localhost in Windows environment.

 static {
        client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", new RetryNTimes(3, 1000));
        client.start();
    }

It mainly includes two methods, registry() and get().

Note: when using the get() method, we can use the cache idea to judge whether the Class name (fully qualified name) of the interface is in the map. If it is, we can get it from the map. If not, we can pull it from the zookeeper registry and cache it in the map.

package com.example.dubbo.registry;

import com.alibaba.fastjson.JSONObject;
import com.example.dubbo.framework.URL;
import com.example.dubbo.provider.api.UserInterface;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author bingbing
 * @date 2021/4/30 0030 18:39
 * Register interface name and service address list
 */
public class ZookeeperRegistry {


    // Initialize client in static block
    static CuratorFramework client;

    private static String dubboServicePath = "/dubbo/service/";

    static {
        client = CuratorFrameworkFactory.newClient("localhost:2181", new RetryNTimes(3, 1000));
        client.start();
    }

    // Using map as local cache
    private static Map<String, List<URL>> listMap = new HashMap<>();

    // Use temporary nodes to mount URL related information
    public static void registry(String interfaceName, URL url) {
        // 1. Mount the url information to the zookeeper directory
        try {
            String result = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(String.format(dubboServicePath + "%s/%s", interfaceName, JSONObject.toJSONString(url)));
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }




    public static List<URL> get(String interfaceName) {
        // 1. If there is in the local map, get the data from the map
        if (listMap.containsKey(interfaceName)) {
            // Monitoring mechanism
//            watch();
            return listMap.get(interfaceName);
        }
        List<URL> urlList = new ArrayList<>();
        // 2. If there is no local map, get the data from the zookeeper registry
        try {
            List<String> strs = client.getChildren().forPath(String.format(dubboServicePath + "%s", interfaceName));
            for (String s : strs) {
                // Deserialize url object
                urlList.add(JSONObject.parseObject(s, URL.class));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 3. Cache the information in zookeeper into the local map
        listMap.put(interfaceName, urlList);
        return urlList;
    }

    public static void main(String[] args) {
        System.out.println(client);
        // 1. Registration
        String interfaceName = "UserService";
        List<URL> urlList = Arrays.asList(new URL("localhost", 8080), new URL("localhost", 9000));
        for (URL url : urlList) {
            ZookeeperRegistry.registry(interfaceName, url);
        }
        List<URL> urlRes = ZookeeperRegistry.get(interfaceName);
        System.out.println(urlRes);
    }

}

Modify consumer

Replace RemoteRegistry with zookeeperRegistry

package com.example.dubbo.framework;

import com.example.dubbo.protocol.http.HttpClient;
import com.example.dubbo.provider.LocalRegistry;
import com.example.dubbo.registry.RemoteRegistry;
import com.example.dubbo.registry.ZookeeperRegistry;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Random;

/**
 * @author bingbing
 * @date 2021/4/29 0029 10:46
 */
public class ProxyFactory<T> {


    public static <T> T getProxy(final Class interfaceclass) {

        return (T) Proxy.newProxyInstance(interfaceclass.getClassLoader(), new Class[]{interfaceclass}, new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                //  You can define mocks by yourself and degrade services through mocks

                // 2. Get the service address list and pull the new service list from zookeeper
//                List<URL> lists = RemoteRegistry.get(interfaceclass.getName());
                List<URL> lists = ZookeeperRegistry.get(interfaceclass.getName());
                // 3. Load balancing strategy: select a url to use.
                URL url = LoadBalance.random(lists);
                // 3. Send http request
                HttpClient client = new HttpClient();
                Invocation invocation = new Invocation(interfaceclass.getName(), method.getName(), objects, method.getParameterTypes());
                Object obj = client.send(url, invocation);
                return obj;
            }
        });
    }

}

Modify service provider

Also, at startup, replace RemoteRegistry with ZookeeperRegistry

package com.example.dubbo.provider;

import com.example.dubbo.framework.URL;
import com.example.dubbo.protocol.http.HttpServer;
import com.example.dubbo.provider.api.UserInterface;
import com.example.dubbo.provider.impl.UserInterfaceImpl;
import com.example.dubbo.registry.RemoteRegistry;
import com.example.dubbo.registry.ZookeeperRegistry;


/**
 * @author bingbing
 * @date 2021/4/29 0029 10:18
 */
public class ProviderApplication {

    public static void main(String[] args) {
        // 1. Specify the container startup address and port
        URL url = new URL("127.0.0.1", 9090);
        // 2. Local registration
        LocalRegistry.regist(UserInterface.class.getName(), UserInterfaceImpl.class);
        // 3. Remote registration
//        RemoteRegistry.registry(UserInterface.class.getName(), url);
        ZookeeperRegistry.registry(UserInterface.class.getName(), url);
        // 4. Start the container and process the request
        HttpServer server = new HttpServer();
        server.start(url);
    }
}

After rewriting and starting the service, observe the call result:

  

So far, the integration of zookeeper has been successful!

3. Provide watcher mechanism

Every time I get data from the local Map, first check whether Zookeeper sends messages to my consumers. If so, I will update the Map first and then return from the local Map.

package com.example.dubbo.registry;

import com.alibaba.fastjson.JSONObject;
import com.example.dubbo.framework.URL;
import com.example.dubbo.provider.api.UserInterface;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author bingbing
 * @date 2021/4/30 0030 18:39
 * Register interface name and service address list
 */
public class ZookeeperRegistry {


    // Initialize client in static block
    static CuratorFramework client;

    private static String dubboServicePath = "/dubbo/service/";

    static {
        client = CuratorFrameworkFactory.newClient("localhost:2181", new RetryNTimes(3, 1000));
        client.start();
    }

    // Using map as local cache
    private static Map<String, List<URL>> listMap = new HashMap<>();

    // Use temporary nodes to mount URL related information
    public static void registry(String interfaceName, URL url) {
        // 1. Mount the url information to the zookeeper directory
        try {
            String result = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(String.format(dubboServicePath + "%s/%s", interfaceName, JSONObject.toJSONString(url)));
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * Monitor zookeeper through the watch mechanism
     *
     * @return
     */
    public static void watch() {
        //Update the service address list of interfaceName to zookeeper
        AtomicReference<Boolean> watchFlag= new AtomicReference<>(false);
        Watcher watcher = (WatchedEvent -> {
            watchFlag.set(true);
            System.out.println("Event listening!" + WatchedEvent);
        });
        if (watchFlag.get()==false) {
            return;
        }
        // 1. Get all interface nodes and refresh the local map

        try {
            List<String> strs = client.getChildren().forPath(dubboServicePath);
            for (String s : strs) {
                // Get the Class first
                String userInterfaceName = JSONObject.parseObject(s, UserInterface.class).getClass().getName();
                List<URL> urlList = new ArrayList<>();
                if (listMap.containsKey(userInterfaceName)) {
                    List<String> urlStrs = client.getChildren().forPath(dubboServicePath + userInterfaceName);
                    for (String urlStr : urlStrs) {
                        urlList.add(JSONObject.parseObject(urlStr, URL.class));
                    }
                    //Update map
                    listMap.put(userInterfaceName, urlList);
                }
            }
        } catch (Exception e) {

        }


        // 2. Match the service address list according to the interfaceName and update the previous address list.

        // 3. The local map updates the address list corresponding to interfaceName
    }


    public static List<URL> get(String interfaceName) {
        // 1. If there is in the local map, get the data from the map
        if (listMap.containsKey(interfaceName)) {
            // Monitoring mechanism
            watch();
            return listMap.get(interfaceName);
        }
        List<URL> urlList = new ArrayList<>();
        // 2. If there is no local map, get the data from the zookeeper registry
        try {
            List<String> strs = client.getChildren().forPath(String.format(dubboServicePath + "%s", interfaceName));
            for (String s : strs) {
                // Deserialize url object
                urlList.add(JSONObject.parseObject(s, URL.class));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 3. Cache the information in zookeeper into the local map
        listMap.put(interfaceName, urlList);
        return urlList;
    }

    public static void main(String[] args) {
        System.out.println(client);
        // 1. Registration
        String interfaceName = "UserService";
        List<URL> urlList = Arrays.asList(new URL("localhost", 8080), new URL("localhost", 9000));
        for (URL url : urlList) {
            ZookeeperRegistry.registry(interfaceName, url);
        }
        List<URL> urlRes = ZookeeperRegistry.get(interfaceName);
        System.out.println(urlRes);
    }

}

 

 

 

 

Topics: microservice