Jump to: navigation, search

Difference between revisions of "Zoodiscovery"

(I want to get notified about discovered services)
(Download OSGi bundles)
 
(19 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
==[[Zoodiscovery/FAQ]]==
 +
[[Zoodiscovery/FAQ|FAQ]]
 +
 
==What is ZooDiscovery?==
 
==What is ZooDiscovery?==
  
Line 25: Line 28:
 
In the spirit of component based development: If you use ECF Discovery and you want the functionality that is provided by Zookeeper, you can replace your existing ECF implementation by this new one. Upgrade without pain.
 
In the spirit of component based development: If you use ECF Discovery and you want the functionality that is provided by Zookeeper, you can replace your existing ECF implementation by this new one. Upgrade without pain.
  
==Download==
+
==Building and Running a dedicated Zookeeper server==
Downland both bundles: org.apache.zookeeper & org.eclipse.ecf.provider.zookeeper<br> from CVS server at http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ecf/?root=RT_Project<br>
+
Building a Zookeeper based discovery server is already done on GitHub here: [https://github.com/ECF/ZooServer https://github.com/ECF/ZooServer]
Anonymous CVS info:  :pserver:anonymous@dev.eclipse.org:/cvsroot/rt
+
  
If you use Eclipse, just open the CVS Repositories perspective and add a new CVS repository by pressing the button in the view toolbar. Then copy everything between quotes here ":pserver:anonymous@dev.eclipse.org:/cvsroot/rt" and press paste (CTRL+V) in the wizard.  
+
==Download OSGi bundles==
 +
OSGi bundles can be downloaded from our ECF download site. Instructions are here [https://www.eclipse.org/ecf/downloads.php https://www.eclipse.org/ecf/downloads.php]
 +
 
 +
==Download Source==
 +
Download both bundles: org.apache.zookeeper & org.eclipse.ecf.provider.zookeeper<br> from git server at http://git.eclipse.org/c/ecf/org.eclipse.ecf.git<br>
 +
 
 +
If you use Eclipse, just open the git Repositories perspective, then copy everything between quotes here "http://anonymous@git.eclipse.org/gitroot/ecf/org.eclipse.ecf.git", and add a new git repository by pressing the button in the view toolbar.  
  
 
You can find the apache zookeeper bundle in the '''org.eclipse.ecf/protocols/bundles''' directory.<br>
 
You can find the apache zookeeper bundle in the '''org.eclipse.ecf/protocols/bundles''' directory.<br>
Line 63: Line 71:
 
  //"ecf.discovery.zookeeper" is the container name we want to initiate.
 
  //"ecf.discovery.zookeeper" is the container name we want to initiate.
 
  container = ContainerFactory.getDefault().createContainer("ecf.discovery.zoodiscovery");
 
  container = ContainerFactory.getDefault().createContainer("ecf.discovery.zoodiscovery");
  } catch(ContainerCreateException e1){ // TODO  
+
  } catch(ContainerCreateException e1){ // TODO
 
  }
 
  }
 
</source>
 
</source>
  
  then one of the senario's  
+
then we pick''' just one''' of our imaginary senario's and connect.
 
<source lang="java">
 
<source lang="java">
 
  //Scenario 1.  We build an ECF ID  to use it to connect with ZooDiscovery  instance z1 running at 192.1.10.10.
 
  //Scenario 1.  We build an ECF ID  to use it to connect with ZooDiscovery  instance z1 running at 192.1.10.10.
Line 86: Line 94:
 
  //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 
  //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 
  new String[] { "zoodiscovery.flavor.standalone=192.1.10.10,192.1.33.33,192.1.34.44" });
 
  new String[] { "zoodiscovery.flavor.standalone=192.1.10.10,192.1.33.33,192.1.34.44" });
+
  </source>
</source><source lang="java">
+
 
 +
<source lang="java">
 
  // then connect
 
  // then connect
 
  z0.connect(target, null);
 
  z0.connect(target, null);
</source>
+
  // After calling connect(target, null) successfully (no exception!), ZooDiscovery takes it over from here and
  '''Note:''' After calling connect(target, null) successfully (no exception!), ZooDiscovery takes it over from here and will keep trying connecting (reconnecting in case of lost connections) automatically.
+
// will keep trying connecting (reconnecting in case of lost connections) automatically.
<source lang="java">
+
</source>
 +
 
 +
<source lang="java">
 
  // To advertise services we need adapting our container this way:
 
  // To advertise services we need adapting our container this way:
 
  IDiscoveryAdvertiser discoveryAdvertiser = (IDiscoveryAdvertiser) z0.getAdapter(IDiscoveryAdvertiser.class);
 
  IDiscoveryAdvertiser discoveryAdvertiser = (IDiscoveryAdvertiser) z0.getAdapter(IDiscoveryAdvertiser.class);
Line 136: Line 147:
  
 
<source lang="java">
 
<source lang="java">
  // We configure central ZooDiscovery  container '''z1''' (on machine with IP address "192.1.10.10")  with its own IP address:
+
  // We configure central ZooDiscovery  container z1 (on machine with IP address "192.1.10.10")  with its own IP address:
 
  //...code as above  
 
  //...code as above  
 
  //referring to itself
 
  //referring to itself
Line 218: Line 229:
 
=Advanced configuration=
 
=Advanced configuration=
  
 +
 +
==Automatically starting ZooDiscovery==
 +
By specifying the property
 +
-Dzoodiscovery.autoStart
 +
the Discovery provider will not require you to engage in ECF specific connection conversations. Instead this will done automatically. Please be aware that -Dzoodiscovery.autoStart=false will also autostart zoodiscovery. If you want to start ZooDiscovery yourself then omit the property alltogether.
 +
 +
 +
==Logging==
 +
Zoodiscovery works with the OSGi logging framework. Since this can be tedious to configure, you can supply a console log function:
 +
-Dzoodiscovery.consoleLog
 +
which will dump some discovery registrations to the console.
 +
 +
Please note that the main zookeeper library uses log4j. If you want logging from this you have to provide this yourself. Please see bug
 +
 +
  https://bugs.eclipse.org/bugs/show_bug.cgi?id=337667
 +
 +
<br>
  
 
==Fine tuning the underlying ZooKeeper==
 
==Fine tuning the underlying ZooKeeper==
being edited...
+
The class [http://www.eclipse.org/ecf/org.eclipse.ecf.docs/api/org/eclipse/ecf/provider/zookeeper/core/DefaultDiscoveryConfig.html DefaultDiscoveryConfig] contains text on how to pass zookeeper flags to the zookeeper instance.
 +
 
 +
In general, zookeeper flags (specific the ones that cannot be passed by the Java properties mechanism) can be passed by using the construct:
 +
 
 +
-Dzoodiscovery.zookeeperFlag=value
 +
 
 +
The description of these flags as well as other flags [http://hadoop.apache.org/zookeeper/docs/r3.3.3/zookeeperAdmin.html#sc_configuration are found here]

Latest revision as of 11:31, 2 May 2014

Zoodiscovery/FAQ

FAQ

What is ZooDiscovery?

ZooDiscovery is a discovery mechanism that runs as an OSGi service. It leverages Apache ZooKeeper robustness and implements Eclipse ECF Discovery API.(Hence the name!). ZooDiscovery is flexible and easy to configure.

This work is funded and made open by Remain Software and Industrial-TSI
Tinker.gif

Concepts

If you use OSGi remote services (See OSGi Compendium Specs chapter 13) you have to know the other end. In large installations this configuration can be quite cumbersome.

At Remain Software we develop software to manage nodes in a network.

We want our nodes to register themselves to us when they are in the network. This is fine if you manage a small office but not if you manage smart lightbulbs in a sky scraper.
Lightbubl.gif

As soon as the lightbulb is screwed into its socket, it can tap some power to activate its OSGi runtime. The runtime will activate the ILightBulb interface with methods dim(int), on() and off() as a remote service. Now, how do we get this service to interested parties...

ECF For the Win

The ECF discovery framework enabled us to create a Zookeeper based Discovery implementation. An addition to the already existing JmDNS (Zeroconf/Bonjour) and jSLP implementations.

An Apache Zookeeper server will replicate configuration data between other Zookeeper servers. The Zookeeper servers know each other and clients know one Zookeeper Server. So the smart bulb (which runs OSGi or did I mention that already?) is preconfigured with the address of its nearest Zookeeper server or gets this information dynamically by some kind of IP broadcast.

When the lightbulb publishes its Remote Service, ECF wakes up and publishes this service through the provided Discovery implementations. The Zookeeper discovery provider will immediately notify its nearest peers and the new lightbulb service is registered in all Zookeeper instances. When the Zookeeper instance that is connected to an interested party receives the data, the Discovery implementation will publish this service in its OSGi container.

The Lightbulb Control Center is waiting for the ILightBulb service and creates a UI in its console. The Building Maintainer can now control the lightbulb.

Zookeeperecf.gif

In the spirit of component based development: If you use ECF Discovery and you want the functionality that is provided by Zookeeper, you can replace your existing ECF implementation by this new one. Upgrade without pain.

Building and Running a dedicated Zookeeper server

Building a Zookeeper based discovery server is already done on GitHub here: https://github.com/ECF/ZooServer

Download OSGi bundles

OSGi bundles can be downloaded from our ECF download site. Instructions are here https://www.eclipse.org/ecf/downloads.php

Download Source

Download both bundles: org.apache.zookeeper & org.eclipse.ecf.provider.zookeeper
from git server at http://git.eclipse.org/c/ecf/org.eclipse.ecf.git

If you use Eclipse, just open the git Repositories perspective, then copy everything between quotes here "http://anonymous@git.eclipse.org/gitroot/ecf/org.eclipse.ecf.git", and add a new git repository by pressing the button in the view toolbar.

You can find the apache zookeeper bundle in the org.eclipse.ecf/protocols/bundles directory.
You can find the discovery provider bundle in the org.eclipse.ecf/providers/bundles directory.

Inner Concepts

ZooDiscovery implements both ECF discovery interfaces: IDiscoveryAdvertiser and IDiscoveryLocator. That is, ZooDiscovery can publish our services and gets us noticed about discovered services. Perfect! But how? A ZooDiscovery instance running at your machine does its job by exchanging data with other ZooDiscovery instance(s) running elsewhere. So each running ZooDiscovery service must know where that other "elsewhere" exactly is. This is why we should first make our ZooDiscovery happy, giving it an IP address to play with.
To keep it smooth, let's take it step by step following these cases:

How to configure ZooDiscovery container?

To tell ZooDiscovery where to look for new services, we use one of these three configuration properties: "zoodiscovery.flavor.standalone", "zoodiscovery.flavor.centralized" or "zoodiscovery.flavor.replicated". Whichever property you use, the ZooDiscovery container is configured the same way. Moreover, ZooDiscovery keeps trying re/connecting automatically.

Note: IP addresses used throughout this wiki are for illustrative purpose, so needless to say you should substitute them with your own IP addresses.

Standalone ZooDiscovery: "zoodiscovery.flavor.standalone"

Let's start with "zoodiscovery.flavor.standalone" for the following illustration. This property accepts as value one or more IP addresses of target machines we want to connect to.

For example, suppose we have 4 ZooDiscovery instances z0, z1, z2 and z3:
z0 instance running on locallhost resolving to IP address "192.1.10.9"
z1 instance running on machine with IP address "192.1.10.10"
z2 instance running on machine with IP address "192.1.33.33"
z3 instance running on machine with IP address "192.1.34.44"

and some arbitrary scenario's: We (z0) want:
Scenario 1 - to discover services advertised by z1, z0 should, then, talk to z1 instance and our property would be set this way: "zoodiscovery.flavor.standalone=192.1.10.10".
Scenario 2 - to discover services advertised by z1 and z2, we set our property so: "zoodiscovery.flavor.standalone=192.1.10.10 , 192.1.33.33".
Scenario 3 - to discover services advertised by z1,z2 and z3, our property is now set so: "zoodiscovery.flavor.standalone=192.1.10.10 , 192.1.33.33, 192.1.34.44".

Note the comma used to separate the list of the IP's.

In code this would look like this:

 IContainer z0= null;
 try { 
 //"ecf.discovery.zookeeper" is the container name we want to initiate.
 container = ContainerFactory.getDefault().createContainer("ecf.discovery.zoodiscovery");
 } catch(ContainerCreateException e1){ // TODO  
 }
then we pick just one of our imaginary senario's and connect.
 //Scenario 1.  We build an ECF ID  to use it to connect with ZooDiscovery  instance z1 running at 192.1.10.10.
 ID target = container.getConnectNamespace().createInstance(  
 //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 new String[] { "zoodiscovery.flavor.standalone=192.1.10.10" });
 //Or scenario 2. We build an ECF ID to use it to connect with 2 ZooDiscovery  instances,  z1 running at 192.1.10.10 and z2 at 192.1.33.33.
 ID target = container.getConnectNamespace().createInstance(  
 //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 new String[] { "zoodiscovery.flavor.standalone=192.1.10.10,192.1.33.33" });
 //Or Scenario 3. We build an ECF ID  to use it to connect with 3 ZooDiscovery  instances z1, z2 and z3  running at 192.1.10.10, 192.1.33.33 and 192.1.34.44, respectively.
 ID target = container.getConnectNamespace().createInstance(  
 //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 new String[] { "zoodiscovery.flavor.standalone=192.1.10.10,192.1.33.33,192.1.34.44" });
 // then connect
 z0.connect(target, null);
 // After calling connect(target, null) successfully (no exception!), ZooDiscovery takes it over from here and
 // will keep trying connecting (reconnecting in case of lost connections) automatically.
 // To advertise services we need adapting our container this way:
 IDiscoveryAdvertiser discoveryAdvertiser = (IDiscoveryAdvertiser) z0.getAdapter(IDiscoveryAdvertiser.class);
 //then we enjoy calling IDiscoveryAdvertiser contract methods
 // To localize/discover services we need adapting it this way:
 IDiscoveryLocator discoveryLocator = (IDiscoveryLocator) z0.getAdapter(IDiscoveryLocator.class);
 
 //then we enjoy calling IDiscoveryLocator  contract methods.

So far, so good. But what does standalone stand for? The answer lies in another hint question: can z1,z2 or z3 discover services published by z0 with the same configuration code above. The answer is: Sorry folks! No. For example, if we wanted z1 to discover what z0 is pubishing as well, then we should've made z1 (on machine 192.1.10.10) connect to z0 :

 //ZooDiscovery instance z1 on machine 192.1.10.10 that will discover  services published by z1 (on machine 192.1.10.9):
 IContainer z1= null;
 try { 
 //"ecf.discovery.zookeeper" is the container name we want to initiate.
 container = ContainerFactory.getDefault().createContainer("ecf.discovery.zoodiscovery");
 } catch(ContainerCreateException e1){ // TODO 
 } 
 //We build an ECF ID  to use it to connect with a ZooDiscovery instance z0 running at 192.1.10.9.
 ID target = container.getConnectNamespace().createInstance(  
 //In "zoodiscovery.flavor.standalone" we don't set our own IP address. Only the target machine(s) address(es) we want to connect to, are set.
 new String[] { "zoodiscovery.flavor.standalone=192.1.10.9" }); 
 // then try connecting. 
 z1.connect(target, null);  
 
 // To advertise services we need adapting our container this way:
 IDiscoveryAdvertiser discoveryAdvertiser = (IDiscoveryAdvertiser) z1.getAdapter(IDiscoveryAdvertiser.class);
 
 // To localize/discover services we need adapting it this way:
 IDiscoveryLocator discoveryLocator = (IDiscoveryLocator) z1.getAdapter(IDiscoveryLocator.class);
 
 //then we enjoy calling IDiscoveryLocator  contract methods.


So, the configuration property "zoodiscovery.flavor.standalone=ipaddress1, ipaddress2,ipaddress4,ipaddress5,..." instructs our (being initiated) ZooDiscovery to connect to machines with IP addresses ipaddress1, ipaddress2, ipaddress4,ipaddress5, and discover services published by ZooDiscovery instances running on each one. Should they need to discover our services as well, then they must connect to us by including our IP address when configuring and initiating their ZooDiscovery instances. So each ZooDiscovery stands independent from other ZooDiscovery instances.

Centralized ZooDiscovery: "zoodiscovery.flavor.centralized"

In contrast to "zoodiscovery.flavor.standalone", flavor "zoodiscovery.flavor.centralized" expects exactly one IP address, the location of ZooDiscovery instance you choose to let play the central role. All of ZooDiscovery instances configured with this property publish to one point (ZooDiscovery running at that IP address) and discover services known at that central point. That is, you need to connect just to one central ZooDiscovery and get all services published by all other members.

let's assume z1 instance (running on machine with IP address "192.1.10.10") plays this central role.

 // We configure central ZooDiscovery  container z1 (on machine with IP address "192.1.10.10")  with its own IP address:
 //...code as above 
 //referring to itself
 ID target = container.getConnectNamespace().createInstance(  
 new String[] { "zoodiscovery.flavor.centralized=192.1.10.10" });
 //...code as above.
 
 // We configure ZooDiscovery   container z0 (on machine  "192.1.10.9") to talk to the center z1 
 ID target = container.getConnectNamespace().createInstance(  
 new String[] { "zoodiscovery.flavor.centralized=192.1.10.10" });
 
 // We configure ZooDiscovery   container z2 (on machine  "192.1.33.33") to talk to the center z1 
 ID target = container.getConnectNamespace().createInstance(  
 new String[] { "zoodiscovery.flavor.centralized=192.1.10.10" });
 
 // We configure ZooDiscovery   container z3 (on machine  "192.1.34.44") to talk to the center z1
 ID target = container.getConnectNamespace().createInstance(  
 new String[] { "zoodiscovery.flavor.centralized=192.1.10.10" });

As you can see, just one IP address is used to configure all other memebers (ZooDiscovery instances: z0, z1,z2 and z3 in this case). The IP address of the central ZooDiscovery which all participating instances publish their services to, and discover from. It differs with the stand alone property in that you connect to one point and get services published by all participating ZooDiscovery instances, but if the central ZooDiscovery (in our example the one runnning at 192.1.10.10) is down, then the whole orbiting ZooDiscovery members can neither discover nor publish services. They all depend on one center, shut down the center and every other instance is headless. Bring the center up, and every other member is happily live and kicking. Additionally, this property expects exactly one IP address as value.

Replicated ZooDiscovery: "zoodiscovery.flavor.replicated"

...being edited

How to build a ServiceInfo object and publish it

 //Some  location
 URI uri = URI.create("http://www.example.com");
 //Some service priority
 int priority = 0;
 //Some service weight
 int weight = 3;
 //Some random service properties
 ServiceProperties serviceProperties = new ServiceProperties();
 serviceProperties.setProperty("foobar", new String("foobar"));
 serviceProperties.setPropertyBytes("foobar1", new byte[] { 1, 2, 3 });
 IServiceTypeID serviceTypeID = null;
 try {
 serviceTypeID = ServiceIDFactory.getDefault().createServiceTypeID(
 DiscoveryContainer.getSingleton().getConnectNamespace(),new String[] {"service1","service2"}, new String[] {"someProtocol"});
 } catch (IDCreateException e) {//TODO			
 }
 //build a service info instance to be published
 ServiceInfo  serviceInfo = new ServiceInfo(uri, "myServiceName", serviceTypeID, priority, weight, serviceProperties);
 //advertise the service
 discoveryAdvertiser.registerService(serviceInfo);

Tip: If you already have an OSGi ServiceReference of the service in hand, it might be handy to build a ServiceInfo instance this way:

 //Using AdvertisedService class means creating a compile-time dependency on "org.eclipse.ecf.provider.zookeeper.core". So bear in mind when using it.
 IServiceInfo advertised = new AdvertisedService(myServiceReference);
 discoveryAdvertiser.registerService(serviceInfo);

I want to get notified about discovered services

You get notified about discovered services by registering yourself as an IServiceListener. Let's take it for a ride and make an inner listener class to see how lightweight its contract is:

 IServiceListener sl = new IServiceListener() {
 public void serviceUndiscovered(IServiceEvent anEvent) {
 // service lost, do something..
 }
 public void serviceDiscovered(IServiceEvent anEvent) {
 // new service is in, do something..
 }
 }; 
 //register to get informed
 discoveryLocator.addServiceListener(sl);
 
 //To register for service discoveries with a specific type, you might add the type as well using this method:
 addServiceListener(IServiceTypeID aType, IServiceListener aListener), instead.
 
 //To register for service types discoveries, you might consider registering as IServiceTypeListener

Note: ZooDiscovery tracks OSGi services being registered under IServiceListener or IServiceTypeListener, and add them as listeners so that (if you choose to) you don't have to add them explicitly the way we did just above. This is handy when your design is a bit more dynamic/component driven.

Advanced configuration

Automatically starting ZooDiscovery

By specifying the property

-Dzoodiscovery.autoStart

the Discovery provider will not require you to engage in ECF specific connection conversations. Instead this will done automatically. Please be aware that -Dzoodiscovery.autoStart=false will also autostart zoodiscovery. If you want to start ZooDiscovery yourself then omit the property alltogether.


Logging

Zoodiscovery works with the OSGi logging framework. Since this can be tedious to configure, you can supply a console log function:

-Dzoodiscovery.consoleLog

which will dump some discovery registrations to the console.

Please note that the main zookeeper library uses log4j. If you want logging from this you have to provide this yourself. Please see bug

 https://bugs.eclipse.org/bugs/show_bug.cgi?id=337667


Fine tuning the underlying ZooKeeper

The class DefaultDiscoveryConfig contains text on how to pass zookeeper flags to the zookeeper instance.

In general, zookeeper flags (specific the ones that cannot be passed by the Java properties mechanism) can be passed by using the construct:

-Dzoodiscovery.zookeeperFlag=value

The description of these flags as well as other flags are found here