Curator的介绍

  Curator就是Zookeeper的一个客户端工具(不知道Zookeeper的同学可以到http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/学习下),封装ZooKeeper client与ZooKeeper server之间的连接处理以及zookeeper的常用操作,提供ZooKeeper各种应用场景(recipe, 比如共享锁服务, 集群领导选举机制)的抽象封装。当然还有他看起来非常舒服的Fluent风格的API。 Curator主要从以下几个方面降低了zk使用的复杂性:

  • 重试机制:提供可插拔的重试机制, 它将给捕获所有可恢复的异常配置一个重试策略, 并且内部也提供了几种标准的重试策略(比如指数补偿).
  • 连接状态监控: Curator初始化之后会一直的对zk连接进行监听, 一旦发现连接状态发生变化, 将作出相应的处理.
  • zk客户端实例管理:Curator对zk客户端到server集群连接进行管理. 并在需要的情况, 重建zk实例, 保证与zk集群的可靠连接
  • 各种使用场景支持:Curator实现zk支持的大部分使用场景支持(甚至包括zk自身不支持的场景), 这些实现都遵循了zk的最佳实践, 并考虑了各种极端情况.

  Curator通过以上的处理, 让用户专注于自身的业务本身, 而无需花费更多的精力在zk本身.这里我们介绍的是Curator的Service Discovery模块

Service Discovery

  我们通常在调用服务的时候,需要知道服务的地址,端口,或者其他一些信息,通常情况下,我们是把他们写到程序里面,但是随着服务越来越多,维护起来也越来越费劲,更重要的是,由于地址都是在程序中配置的,我们根本不知道远程的服务是否可用,当我们增加或者删除服务,我们又需要到配置文件中配置么? 这时候,Zookeeper帮大忙了,我们可以把我们的服务注册到Zookeeper中,创建一个临时节点(当连接断开之后,节点将被删除),存放我们的服务信息(url,ip,port等信息),把这些临时节点都存放在以serviceName命名的节点下面,这样我们要获取某个服务的地址,只需要到Zookeeper中找到这个path,然后就可以读取到里面存放的服务信息,这时候我们就可以根据这些信息调用我们的服务。这样,通过Zookeeper我们就做到了动态的添加和删除服务,做到了一旦一个服务时效,就会自动从Zookeeper中移除,基本上Curator中的Service Discovery就是做的这点事。
  下面我们用两张图片来比较一下,一般情况下的服务调用,和使用 Dynamic Service Registry 的区别

    

  使用zookeeper做服务注册之后:

关于Apache curator的service discovery的一些介绍可以参考官方文档:http://curator.apache.org/curator-x-discovery/index.html

Service Discovery 的使用

  一般而言,分为 Service Registry 和 Service Discovery,对应服务端和客户端。也就是由服务提供者,讲自身的信息注册到Zookeeper,然后,客户端通过到Zookeeper中查找服务信息,然后根据信息就行调用(见上图)。说了这么多,上代码了。

  首先我们定义个payload,我们这一在里面存储一些服务信息。这个信息会被保存在Zookeeper,这里只是举个例子,你还可以写入更多你想要的信息。

  

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 + '\'' +
'}';
}
}

我们先写服务注册,也就是服务端那边做的事情。

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

一般情况下,会在我们服务启动的时候就将服务信息注册,比如我们是web项目的话可以写一个Servlet Listener进行注册,这里为了方便,写一个Main方法进行测试,如果我们把我们的信息存储在payload中的话,UriSpec是可以不定义的。

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: 下午8:05
*/
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(12345)
.address("192.168.1.100") //address不写的话,会取本地ip
.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(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);
}
}

再来写Service discovery

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

客户端测试程序:

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

好了,代码就到这里,如果有什么问题的话,请指正。

Service Discovery with Apache Curator的更多相关文章

  1. 15. 使用Apache Curator管理ZooKeeper

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  2. Apache Curator is a Java/JVM client library for Apache ZooKeeper

    http://curator.apache.org/index.html Welcome to Apache Curator What is Curator? Curator n ˈkyoor͝ˌāt ...

  3. ZooKeeper之service discovery

    Zookeeper整体介绍 ZooKeeper is a centralized service for maintaining configuration information, naming, ...

  4. 15. 使用Apache Curator装饰ZooKeeper

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  5. 使用Apache Curator管理ZooKeeper(转)

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  6. Open-Source Service Discovery

    Service discovery is a key component of most distributed systems and service oriented architectures. ...

  7. 使用Apache Curator监控Zookeeper的Node和Path的状态

    1.Zookeeper经常被我们用来做配置管理,配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同 ...

  8. Zookeeper客户端Apache Curator

    本文不对Zookeeper进行介绍,主要介绍Curator怎么操作Zookeeper. Apache Curator是Apache ZooKeeper的Java / JVM客户端库,Apache Zo ...

  9. Apache Curator: Zookeeper客户端

    Apache Curator Framework url: http://curator.apache.org/curator-framework/ The Curator Framework is ...

随机推荐

  1. web.xml配置详解之listener与context-param

    1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个节点: <listener></listener> 和 <context- ...

  2. IIS7下.NET4.0 网站UrlRewriter.dll重写无后缀路径 失效

    解决方法: 1.添加通配符脚本映射,选择:C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll 2.找到和网站相对的连接池,选择 ...

  3. cocos2d-x 用浏览器打开网页

    转自:http://www.xuebuyuan.com/1396292.html,http://www.cocoachina.com/bbs/read.php?tid=88589 First!! 源代 ...

  4. xshell 远程连接Linux

    Linux系统(Ubuntu 16.04) Window系统(win10) 工具:xshell Linux下, sudo apt-get update sudo apt-get install ope ...

  5. Swift学习笔记十

    枚举 一个枚举为一组相关联的值定义一个通用类型,并且让你可以在代码中类型安全地操作这些值. C中的枚举将关联的名字指派给一系列整型值.Swift中的枚举类型更为活泼,并不需要为每个成员指定值,如果指定 ...

  6. MVC4数据注释与验证

    Using Validation Annotations Required必须项验证属性 [Required] public string FirstName { get; set; } [Requi ...

  7. 用Java实现向Cassandra数据库中插入和查询数据

    所用jar包: 其中jxl.jar和dom4j.jar,jaxen-1.1-beta-6.jar是解析XML文件用的jar包,如果不解析XML文件可以不用. 代码如下: package com.loc ...

  8. 终端I/O之规范模式

    规范模式很简单:发一个读请求,输入完一行后,终端驱动程序即刻返回.下列几个条件都会造成读返回: 所要求的字节数已经读到时,读返回.无需读一个完整的行.如果都了部分行,也不会丢失任何信息,下一次读从前一 ...

  9. nginx平滑重启与平滑升级的方法

    如何实现nginx平滑重启与平滑升级? 平滑重启 kill -HUP `cat /usr/local/www/nginx/logs/nginx.pid` 平滑升级nginx: cd /yujialin ...

  10. python代码查询港澳通行证办理进度

    查询港澳通行证办理进度查询的python 3.3代码.利用socket请求相关网站,获得结果后利用正则找出办理进度.其实用urllib代码会更简洁,不过当时在下还不熟悉urllib~ 直接上代码: i ...