Service Discovery with Apache Curator, discoverycurator
Introduction to Curator
Curator is a client tool for Zookeeper (people who do not know about Zookeeper can access ZooKeeper). It encapsulates connection processing between ZooKeeper client and zookeeper server and common operations on Zookeeper, provides abstract encapsulation of ZooKeeper application scenarios (recipe, such as the shared lock service and cluster leader election mechanism. Of course, there are Fluent APIs that he looks very comfortable. Curator mainly reduces the complexity of zk in the following aspects:
- Retry Mechanism: a pluggable Retry Mechanism is provided, which configures a retry policy for capturing all recoverable exceptions, and also provides several standard retry policies (such as exponential compensation ).
- Connection status monitoring: After the Curator is initialized, the zk connection will be monitored all the time. Once the connection status changes, corresponding processing will be performed.
- Zk client instance management: Curator manages the connection between the zk client and the server cluster, and recreates the zk instance as needed to ensure reliable connection with the zk cluster.
- Support for various use cases: Curator supports most use cases supported by zk (even scenarios not supported by zk itself). These implementations follow the best practices of zk, and considered various extreme situations.
With the above processing, Curator allows users to focus on their own businesses without spending more energy on zk itself.Here we will introduce the Service Discovery module of Curator.
Service Discovery
We usually need to know the service address, port, or other information when calling the service. Generally, we write them into the program, but as the service grows, maintenance becomes more and more difficult. More importantly, because the addresses are configured in the program, we do not know whether remote services are available. When we add or delete services, do we need to configure it in the configuration file again? At this time, Zookeeper is very helpful. We can register our service to Zookeeper and create a temporary node (when the connection is closed, the node will be deleted ), store our service information (url, ip, port, and other information) and store these temporary nodes under the node named serviceName. In this way, we need to obtain the address of a service, you only need to find this path in Zookeeper, and then you can read the service information stored in it. At this time, we can call our service based on this information. In this way, we can dynamically add and delete services through Zookeeper, so that once a service expires, it will be automatically removed from Zookeeper, basically, the Service Discovery in Curator is doing this.
The following two images are used to compare the differences between Service calling and using Dynamic Service Registry.
After zookeeper is used for service registration:
For more information about the service discovery of Apache curator, see the official documentation: http://curator.apache.org/curator-x-discovery/index.html.
Use of Service Discovery
Generally, it is divided into Service Registry and Service Discovery, corresponding to the server and client. That is, the service provider registers its own information to Zookeeper. Then, the client searches for the service information in Zookeeper and then calls the service information based on the information (see ). After talking so much about the code.
First, we define a payload, which stores some service information. This information will be stored in Zookeeper. Here is just an example. You can also write more information you want.
package discovery;import org.codehaus.jackson.map.annotate.JsonRootName;/** * Created by hupeng on 2014/9/16. */@JsonRootName("details")public class InstanceDetails { private String id; private String listenAddress; private int listenPort; private String interfaceName; public InstanceDetails(String id, String listenAddress, int listenPort,String interfaceName) { this.id = id; this.listenAddress = listenAddress; this.listenPort = listenPort; this.interfaceName = interfaceName; } public InstanceDetails() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getListenAddress() { return listenAddress; } public void setListenAddress(String listenAddress) { this.listenAddress = listenAddress; } public int getListenPort() { return listenPort; } public void setListenPort(int listenPort) { this.listenPort = listenPort; } public String getInterfaceName() { return interfaceName; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } @Override public String toString() { return "InstanceDetails{" + "id='" + id + '\'' + ", listenAddress='" + listenAddress + '\'' + ", listenPort=" + listenPort + ", interfaceName='" + interfaceName + '\'' + '}'; }}
We should first write about service registration, that is, what the server is doing.
package discovery;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.x.discovery.ServiceDiscovery;import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;import org.apache.curator.x.discovery.ServiceInstance;import org.apache.curator.x.discovery.details.JsonInstanceSerializer;import java.io.IOException;/** * Created by hupeng on 2014/9/16. */public class ServiceRegistrar{ private ServiceDiscovery<InstanceDetails> serviceDiscovery; private final CuratorFramework client; public ServiceRegistrar(CuratorFramework client,String basePath) throws Exception { this.client = client; JsonInstanceSerializer<InstanceDetails> serializer = new JsonInstanceSerializer<InstanceDetails>(InstanceDetails.class); serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class) .client(client) .serializer(serializer) .basePath(basePath) .build(); serviceDiscovery.start(); } public void registerService(ServiceInstance<InstanceDetails> serviceInstance) throws Exception { serviceDiscovery.registerService(serviceInstance); } public void unregisterService(ServiceInstance<InstanceDetails> serviceInstance) throws Exception { serviceDiscovery.unregisterService(serviceInstance); } public void updateService(ServiceInstance<InstanceDetails> serviceInstance) throws Exception { serviceDiscovery.updateService(serviceInstance); } public void close() throws IOException { serviceDiscovery.close(); }}
Generally, the service information is registered when the service is started. For example, if we are a web project, you can write a Servlet Listener for registration. For convenience, write a Main method for testing. If we store our information in payload, UriSpec may not be defined.
Package discovery; import org. apache. curator. framework. curatorFramework; import org. apache. curator. framework. curatorFrameworkFactory; import org. apache. curator. retry. exponentialBackoffRetry; import org. apache. curator. x. discovery. serviceInstance; import org. apache. curator. x. discovery. uriSpec; import java. util. UUID;/*** User: hupeng * Date: 14-9-16 * Time: */public class ServerApp {public static void main (String [] args) throws Exception {CuratorFramework client = CuratorFrameworkFactory. newClient ("127.0.0.1: 2181", new ExponentialBackoffRetry (1000, 3); client. start (); ServiceRegistrar serviceRegistrar = new ServiceRegistrar (client, "services"); ServiceInstance <InstanceDetails> instance1 = ServiceInstance. <InstanceDetails> builder (). name ("service1 "). port (1, 12345 ). address ("192.168.1.100") // if the address is not specified, the local ip address is used. payload (new InstanceDetails (UUID. randomUUID (). toString (), "192.168.1.100", 12345, "Test. service1 ")). uriSpec (new UriSpec ("{scheme}: // {address }:{ port }")). build (); ServiceInstance <InstanceDetails> instance2 = ServiceInstance. <InstanceDetails> builder (). name ("service2 "). port (1, 12345 ). address ("192.168.1.100 "). payload (new InstanceDetails (UUID. randomUUID (). toString (), "192.168.1.100", 12345, "Test. service2 ")). uriSpec (new UriSpec ("{scheme}: // {address }:{ port }")). build (); serviceRegistrar. registerService (instance1); serviceRegistrar. registerService (instance2); Thread. sleep (Integer. MAX_VALUE );}}
Write Service discovery again
package discovery;import com.google.common.collect.Lists;import com.google.common.collect.Maps;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.utils.CloseableUtils;import org.apache.curator.x.discovery.ServiceDiscovery;import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;import org.apache.curator.x.discovery.ServiceInstance;import org.apache.curator.x.discovery.ServiceProvider;import org.apache.curator.x.discovery.details.JsonInstanceSerializer;import org.apache.curator.x.discovery.strategies.RandomStrategy;import java.io.Closeable;import java.io.IOException;import java.util.List;import java.util.Map;/** * Created by hupeng on 2014/9/16. */public class ServiceDiscoverer { private ServiceDiscovery<InstanceDetails> serviceDiscovery; private Map<String, ServiceProvider<InstanceDetails>> providers = Maps.newHashMap(); private List<Closeable> closeableList = Lists.newArrayList(); private Object lock = new Object(); public ServiceDiscoverer(CuratorFramework client ,String basePath) throws Exception { JsonInstanceSerializer<InstanceDetails> serializer = new JsonInstanceSerializer<InstanceDetails>(InstanceDetails.class); serviceDiscovery = ServiceDiscoveryBuilder.builder(InstanceDetails.class) .client(client) .basePath(basePath) .serializer(serializer) .build(); serviceDiscovery.start(); } public ServiceInstance<InstanceDetails> getInstanceByName(String serviceName) throws Exception { ServiceProvider<InstanceDetails> provider = providers.get(serviceName); if (provider == null) { synchronized (lock) { provider = providers.get(serviceName); if (provider == null) { provider = serviceDiscovery.serviceProviderBuilder(). serviceName(serviceName). providerStrategy(new RandomStrategy<InstanceDetails>()) .build(); provider.start(); closeableList.add(provider); providers.put(serviceName, provider); } } } return provider.getInstance(); } public synchronized void close(){ for (Closeable closeable : closeableList) { CloseableUtils.closeQuietly(closeable); } }}
Client test program:
Package discovery; import org. apache. curator. framework. curatorFramework; import org. apache. curator. framework. curatorFrameworkFactory; import org. apache. curator. retry. exponentialBackoffRetry; import org. apache. curator. utils. closeableUtils; import org. apache. curator. x. discovery. serviceInstance;/*** User: hupeng * Date: 14-9-16 * Time: 8: 16 */public class ClientApp {public static void main (String [] args) throws Exception {CuratorFramework client = CuratorFrameworkFactory. newClient ("127.0.0.1: 2181", new ExponentialBackoffRetry (1000, 3); client. start (); ServiceDiscoverer serviceDiscoverer = new ServiceDiscoverer (client, "services"); ServiceInstance <InstanceDetails> instance1 = serviceDiscoverer. getInstanceByName ("service1"); System. out. println (instance1.buildUriSpec (); System. out. println (instance1.getPayload (); ServiceInstance <InstanceDetails> instance2 = serviceDiscoverer. getInstanceByName ("service1"); System. out. println (instance2.buildUriSpec (); System. out. println (instance2.getPayload (); serviceDiscoverer. close (); CloseableUtils. closeQuietly (client );}}
Okay, the code is here. If you have any questions, please correct them.