Implementation of service registration and discovery based on Zookeeper

After we have learned about Zookeeper in detail, we can do it ourselves to implement a service registration and discovery based on Zookeeper. First, we need to establish two microservices, an order service (OrderService) and a product service (ProductService), which When ordering services, you need to call product services.


First of all, let ’s take a look at how the product service (ProductService) is registered on Zookeeper. First, we will come to build a SpringBoot project.
Insert picture description here

When the service starts, we need to register the service with Zookeeper, here we need to use the Servlet APIServletContextListenerThe interface, which can monitor the life cycle of the ServletContext object, is actually monitoring the life cycle of the Web application.


When the Servlet container starts or terminates the Web application, it will trigger the ServletContextEvent event, which is handled by the ServletContextListener. Two methods for handling ServletContextEvent events are defined in the ServletContextListener interface.
Insert picture description here

@WebListener
public class InitListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            Properties properties =  new Properties();
            properties.load(InitListener.class.getClassLoader().getResourceAsStream("application.properties"));

            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            int port = Integer.valueOf(properties.getProperty("server.port"));
            ServiceRegister.register(hostAddress,port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

Here we use @WebListenerannotations, which we have briefly introduced in the three major components of the Web, from web.xml to annotations , but here we do n’t forget to add @ServletComponentScanannotations to the SpringBoot startup class
Insert picture description here
Insert picture description here


Of course, we can also use another way, we @WebListener @ServletComponentScancan also use annotations, directly in the SpringBoot startup class, @Beancan also be used , as follows:
Insert picture description here



Next, let's take a closer look at how to register it in Zookeeper in the InitListener class. Here, we first obtained the configuration file application.properties in the project. As for how to obtain it, there are too many file paths in Java to get resources. Ways


Of course, we can also add the annotation @Component to the Spring environment, and then we use @Value to obtain the service port number in the configuration file.
Insert picture description here
Insert picture description here


In the above, not only the port number, but also the IP address, we need to register IP + PORT to Zookeeper. Here we must think of the node that we introduced previously to store IP + PORT information. Here we can use our Any one of the three clients introduced earlier, here we choose Curator , the advantage of this client over the native client, we have already introduced in detail before, here we need to introduce related dependencies

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.12</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-log4j12</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!--对zookeeper的底层api的一些封装-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.3.0</version>
</dependency>
<!--封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式Barrier-->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.3.0</version>
</dependency>

A little attention here is that we need slf4j in Zookeeper, because it conflicts with the version of slf4j in SpringBoot, and then it is very simple to register the information to the Zookeeper node. Here if we use a native client, Then we need more additional operations, such as judging the existence of the parent node layer by layer, if not, create the parent node, and also need to use CountDownLatch and Watch to wait for a successful connection when connecting to the client

public class ServiceRegister {

    private static final String CONNENT_ADDR = "127.0.0.1:2181";
    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME = "/products";

    public static void register(String address, int port) throws Exception {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(CONNENT_ADDR)
                .connectionTimeoutMs(5000)  //连接超时时间
                .sessionTimeoutMs(5000)     //会话超时时间
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .build();
        curatorFramework.start();

        String path = BASE_SERVICES + SERVICE_NAME;
        String server_path = address + ":" + port;

        curatorFramework.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath(path + "/child", server_path.getBytes());
    }
}

Next, we have almost completed the work of product service. We can change the port number in application.properties to start multiple product services. The interface provided by the product service to the outside is as follows, which is simply to return the port number of the service.
Insert picture description here




After completing the registration of the product service, the next step is definitely the discovery of the order service. First of all, we also need to create a SpringBoot project. Similarly, we still need to use the ServletContextListener interface in the Servlet API
Insert picture description here
Insert picture description here

Unlike the product service, the InitListener class in the product service is to register the IP + PORT information of the product service to the Zookeeper when the Servlet container is started, and the InitListener in the order service is to start when the Servlet container is started. The product service information registered in Zookeeper is pulled down for order service to call

public class InitListener implements ServletContextListener {

    private static final String CONNENT_ADDR = "127.0.0.1:2181";
    private static final String BASE_SERVICES = "/services";
    private static final String SERVICE_NAME = "/products";

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                .connectString(CONNENT_ADDR)
                .connectionTimeoutMs(5000)  //连接超时时间
                .sessionTimeoutMs(5000)     //会话超时时间
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                .build();
        curatorFramework.start();

        List<String> newServerList = new ArrayList<>();
        String path = BASE_SERVICES + SERVICE_NAME;
        final PathChildrenCache cache = new PathChildrenCache(curatorFramework, path, true);
        try {
            cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
            cache.getListenable().addListener(new PathChildrenCacheListener() {
                //监听子节点的变化
                @Override
                public void childEvent(CuratorFramework cf, PathChildrenCacheEvent event) throws Exception {
                    List<String> list = curatorFramework.getChildren().forPath(path);
                    for (String subNode : list) {
                        byte[] bytes = curatorFramework.getData().forPath(path + "/" + subNode);
                        String host = new String(bytes, "utf-8");
                        newServerList.add(host);
                    }
                    LoadBalance.SERVICE_LIST = newServerList;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

Here we need to monitor the changes of the nodes on Zookeeper, because in case of a product service down or new product service, we need to be able to obtain information in time. For the introduction of Curator monitoring, see Zookeeper client (5)-Curator
Insert picture description here


When we listen to the change of the node, we will update its value in the LoadBalance class, which is mainly a simple simulation of load balancing. Here we are randomly selecting a service from all product services when the order service calls the product service

public class LoadBalance {

    public volatile static List<String> SERVICE_LIST;

    public String choseService() {
        String result = "";
        if (!CollectionUtils.isEmpty(SERVICE_LIST)) {
            int index = new Random().nextInt(SERVICE_LIST.size());
            result = SERVICE_LIST.get(index);
        }
        return result;
    }
}

Here, our service registration and discovery based on Zookeeper is basically completed. Then we also provide a Controller in the order service. The Controller will call the Controller of the product service. Here, we use the RestTemplate as follows:

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private RestTemplate restTemplate;

    private LoadBalance loadBalance = new LoadBalance();

    @RequestMapping("/getOrder")
    public Object getProduct(HttpServletRequest request) {
        String host = loadBalance.choseService();
        return restTemplate.getForObject("http://" + host + "/product/getProduct", Object.class);
    }
}

Insert picture description here




The above completes a simple service registration and discovery. Here we will test it. We can change the port number in the applicat.properties file in the Product Service to start multiple product services. Here we start 8080 , 8081 two service services, we can use ZooInspector to view the node information of Zookeeper, as follows:
Insert picture description hereInsert picture description here


Then we are starting an order service and refreshing in the browser constantly, we can see that different product services will be called randomly
Insert picture description here
Insert picture description here

286 original articles published · Liked12 · Visitors 10,000+

Guess you like

Origin blog.csdn.net/newbie0107/article/details/105442764