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); } }