[Microservice architecture design and implementation] 4.5 Service discovery, registration and configuration management

Past review:

Chapter 1: [Cloud Native Concepts and Technologies]

Chapter 2: [Containerized Application Design and Development]

Chapter 3: [Container-based deployment, management and scaling]

Chapter 4: [4.1 Microservice Architecture Overview and Design Principles]

Chapter 4: [4.2 Definition and Division of Service Boundary]

Chapter 4: [4.3 Communication between services and API design]

Chapter 4: [4.4 Separation and Servicing of Database and Data Storage]

4.5 Service discovery, registration and configuration management

4.5.1 Service Discovery

Under cloud native, the microservice architecture has become the mainstream architecture of applications. Service discovery is an important part of microservice architecture, which is responsible for automatic discovery of service instances, load balancing and failover. The following is a simple Java code example that shows how to use service discovery to automatically discover microservice instances under cloud native.

First, we need to create a service registry to store all service instances. In this example, we use Zookeeper to store service instances.

import org.apache.zookeeper.*;  
import java.util.List;

public class ServiceRegistry {
    
    

   private ZooKeeper zk;  
   private String rootPath;  
   private List<ServiceInstance> services;

   public ServiceRegistry(String zkAddress, String zkPassword, String zkNodePath) {
    
      
       this.zk = new ZooKeeper(zkAddress, 5000, new Watcher() {
    
      
           public void process(WatchedEvent event) {
    
      
               if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
    
      
                   loadServices();  
               }  
           }  
       });  
       this.rootPath = zkNodePath;  
   }

   private void loadServices() {
    
      
       try {
    
      
           services = zk.getChildren(rootPath, false).stream()  
                   .sorted()  
                   .map(s -> new ServiceInstance(s, zk.exists(s, false)))  
                   .collect(Collectors.toList());  
       } catch (Exception e) {
    
      
           e.printStackTrace();  
       }  
   }

   public ServiceInstance getServiceInstance(String serviceName) {
    
      
       for (ServiceInstance service : services) {
    
      
           if (service.getName().equals(serviceName)) {
    
      
               return service;  
           }  
       }  
       return null;  
   }

   public void addServiceInstance(ServiceInstance serviceInstance) {
    
      
       zk.add(rootPath, serviceInstance.toZooObject());  
   }

   public void removeServiceInstance(String serviceName) {
    
      
       for (ServiceInstance service : services) {
    
      
           if (service.getName().equals(serviceName)) {
    
      
               zk.delete(service.getPath(), -1);  
               return;  
           }  
       }  
       throw new IllegalArgumentException("Service " + serviceName + " not found.");  
   }  
}

In this example, we use Zookeeper to store service instances. When a service instance needs to be discovered, we traverse all service instances and use the service name to find the corresponding service instance. If the service instance does not exist, a new service instance is added. If the service instance exists, update the state of the service instance.

Finally, we need to write a service instance class to represent a microservice instance.

import org.springframework.beans.factory.annotation.Value;  
import org.springframework.stereotype.Component;

@Component  
@Value("${service.name}")  
public class ServiceInstance {
    
    

   private String path;  
   private String name;  
   private boolean enabled;

   public ServiceInstance(String path, boolean enabled) {
    
      
       this.path = path;  
       this.name = name;  
       this.enabled = enabled;  
   }

   public String getPath() {
    
      
       return path;  
   }

   public String getName() {
    
      
       return name;  
   }

   public boolean isEnabled() {
    
      
       return enabled;  
   }

   public void setEnabled(boolean enabled) {
    
      
       this.enabled = enabled;  
   }

   public static ServiceInstance fromZooObject(org.apache.zookeeper.ZooObject zobj) {
    
      
       return new ServiceInstance(zobj.getData().get(0).getByteValue("path"),  
                   zobj.getData().get(0).getByteValue("state") == 1);  
   }

   public static ServiceInstance fromString(String zkPath) {
    
      
       return ServiceInstance.fromZooObject(org.apache.zookeeper.Zoo 蛋类 zobj -> zobj.getData().get(0).get(zkPath).getByteValue());  
   }  
}

In the above example, we used fromZooObjectand fromStringmethod to load and generate service instance object from Zookeeper storage.

Finally, when we need to discover the microservice instance, we can call the method ServiceRegistryin the class getServiceInstanceto get the latest service instance object, and use fromZooObjectthe or fromStringmethod to generate the service instance object.

4.5.2 Service Registration

Under the cloud native environment, service registration in the microservice architecture is a very important link. It is responsible for identifying service instances to other services, and provides service discovery and load balancing. The following is a simple Java code example that shows how to use service registration to automatically register microservice instances under cloud native.

First, we need to create a service registry to store all service instances. In this example, we use Zookeeper to store service instances.

import org.apache.zookeeper.*;    
import java.util.List;

public class ServiceRegistry {
    
    

   private ZooKeeper zk;    
   private String rootPath;    
   private List<ServiceInstance> services;

   public ServiceRegistry(String zkAddress, String zkPassword, String zkNodePath) {
    
        
       this.zk = new ZooKeeper(zkAddress, 5000, new Watcher() {
    
        
           public void process(WatchedEvent event) {
    
        
               if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
    
        
                   loadServices();    
               }    
           }    
       });    
       this.rootPath = zkNodePath;    
   }

   private void loadServices() {
    
        
       try {
    
        
           services = zk.getChildren(rootPath, false).stream()    
                   .sorted()    
                   .map(s -> new ServiceInstance(s, zk.exists(s, false)))    
                   .collect(Collectors.toList());    
       } catch (Exception e) {
    
        
           e.printStackTrace();    
       }    
   }

   public ServiceInstance getServiceInstance(String serviceName) {
    
        
       for (ServiceInstance service : services) {
    
        
           if (service.getName().equals(serviceName)) {
    
        
               return service;    
           }    
       }    
       return null;    
   }

   public void addServiceInstance(ServiceInstance serviceInstance) {
    
        
       zk.add(rootPath, serviceInstance.toZooObject());    
   }

   public void removeServiceInstance(String serviceName) {
    
        
       for (ServiceInstance service : services) {
    
        
           if (service.getName().equals(serviceName)) {
    
        
               zk.delete(service.getPath(), -1);    
               return;    
           }    
       }    
       throw new IllegalArgumentException("Service " + serviceName + " not found.");    
   }    
}

In this example, we use Zookeeper to store service instances. When a service instance needs to be registered, we traverse all service instances and use the service name to find the corresponding service instance. If the service instance does not exist, a new service instance is added. If the service instance exists, update the state of the service instance.

Finally, we need to write a service instance class to represent a microservice instance.

import org.springframework.beans.factory.annotation.Value;    
import org.springframework.stereotype.Component;

@Component    
@Value("${service.name}")    
public class ServiceInstance {
    
    

   private String path;    
   private String name;    
   private boolean enabled;

   public ServiceInstance(String path, boolean enabled) {
    
        
       this.path = path;    
       this.name = name;    
       this.enabled = enabled;    
   }

   public String getPath() {
    
        
       return path;    
   }

   public String getName() {
    
        
       return name;    
   }

   public boolean isEnabled() {
    
        
       return enabled;    
   }

   public void setEnabled(boolean enabled) {
    
        
       this.enabled = enabled;    
   }

   public static ServiceInstance fromZooObject(org.apache.zookeeper.ZooObject zobj) {
    
        
       return new ServiceInstance(zobj.getData().get(0).getByteValue("path"),    
                   zobj.getData().get(0).getByteValue("state") == 1);    
   }

   public static ServiceInstance fromString(String zkPath) {
    
        
       return ServiceInstance.fromZooObject(org.apache.zookeeper.Zoo 蛋类 zobj -> zobj.getData().get(0).get(zkPath).getByteValue());    
   }    
}

In the above example, we used fromZooObjectand fromStringmethod to load and generate service instance object from Zookeeper storage.

4.5.3 Configuration Management

Under the cloud native environment, the configuration management of microservices is very important. It is responsible for managing various configurations of microservices, such as service discovery, load balancing, container orchestration, and more. The following is a simple Java code example that shows how to use configuration management to automatically manage the configuration of microservices under cloud native.

First, we need to create a configuration management container to store and manage all configurations. In this example, we use Zookeeper to store the configuration.

import org.apache.zookeeper.*;    
import org.springframework.beans.factory.annotation.Value;    
import org.springframework.stereotype.Component;  

@Component    
@Value("${zk.address}")    
public class ConfigurationManager {
    
        
    private ZooKeeper zk;    
    private String zkAddress;  

    public ConfigurationManager(String zkAddress) {
    
        
        this.zkAddress = zkAddress;    
        try {
    
        
            zk = new ZooKeeper(zkAddress, 5000, new Watcher() {
    
        
                public void process(WatchedEvent event) {
    
        
                    if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
    
        
                        loadConfigurations();    
                    }    
                }    
            });    
        } catch (Exception e) {
    
        
            e.printStackTrace();    
        }    
    }  

    private void loadConfigurations() {
    
        
        try {
    
        
            Configurations.loadFromZookeeper(zk);    
        } catch (Exception e) {
    
        
            e.printStackTrace();    
        }    
    }  

    public Configurations getConfigurations() {
    
        
        return Configurations.loadFromZookeeper(zk);    
    }  

    public void addConfiguration(Configuration configuration) {
    
        
        Configurations.add(configuration);    
    }  

    public void removeConfiguration(String configurationName) {
    
        
        Configurations.remove(configurationName);    
    }    
}    

In the above example, we use Zookeeperto store the configuration. When a configuration needs to be loaded, we iterate over all configurations and use the configuration name to find the corresponding configuration. If the configuration does not exist, a new configuration is added. If the configuration exists, the configuration is updated.

Finally, we need to write a configuration class to represent the configuration of a microservice.

import org.springframework.beans.factory.annotation.Value;    
import org.springframework.stereotype.Component;  

@Component    
@Value("${service.name}")    
public class Configuration {
    
        
    private String configurationName;    
    private String configurationValue;  

    public Configuration(String configurationName, String configurationValue) {
    
        
        this.configurationName = configurationName;    
        this.configurationValue = configurationValue;    
    }  

    public String getConfigurationName() {
    
        
        return configurationName;    
    }  

    public String getConfigurationValue() {
    
        
        return configurationValue;    
    }    
}

In the above example, we use a configuration class to represent the configuration of a microservice. When the configuration needs to be loaded, we can call the method ConfigurationManagerin the class getConfigurationsto get the latest configuration, and use Configurationthe class to generate the configuration object.

Guess you like

Origin blog.csdn.net/weixin_44427181/article/details/131285886