Service Discovery with Apache Curator
23858 ワード
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自体に多くの精力を費やすことなく、ユーザー自身の業務そのものに専念させる.ここで紹介するのは、CuratorのService Discoveryモジュールです
Service Discovery
私たちは通常、サービスを呼び出すときに、サービスのアドレス、ポート、または他の情報を知る必要があります.通常、私たちは彼らをプログラムに書きますが、サービスが多くなるにつれて、メンテナンスもますます苦労しています.さらに重要なのは、アドレスがプログラムに構成されているため、リモートのサービスが利用できるかどうか分かりません.サービスを追加または削除する場合は、プロファイルに構成する必要がありますか?この時、Zookeeperは助かりました.私たちのサービスをZookeeperに登録することができます.接続が切断されると削除される一時ノードを作成し、サービス情報を保存します.(url,ip,portなどの情報)、これらの一時ノードをserviceNameで命名されたノードの下に格納します.これにより、あるサービスのアドレスを取得するには、Zookeeperでこのpathを見つけて、そこに格納されているサービス情報を読み取るだけで、これらの情報に基づいてサービスを呼び出すことができます.これにより、Zookeeperで私たちは動的にサービスを追加・削除すると、サービスの時効が1つになると自動的にZookeeperから削除されるようになり、基本的にはCuratorのService Discoveryが行うことになります.一般的なサービスコールとDynamic Service Registryの違いを2枚の画像で比較してみましょう
zookeeperを使用してサービス登録を行った後:
Apache curatorのservice discoveryについてのいくつかの紹介は、公式ドキュメント:http://curator.apache.org/curator-x-discovery/index.htmlを参照してください.
Service Discoveryの使用
一般的には、サービス側とクライアントに対応するサービスレジストリとサービス側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プロジェクトであればサーブレット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);
}
}
はい、コードはここまでです.何か問題があれば、指摘してください.