When an client is connecting to an etch service, a uri is used to specify the details of the connection. Likewise, a listener uses a uri to specify the details of the listening point. Typically this code looks something like thi
RemoteNameServiceServer server = NameServiceHelper.newServer( "tcp://host:4001?filter=KeepAlive&TcpTransport.reconnectDelay=4000", null, factory );
The uri specifies all the details of the etch connection in a convenient form. Yet the uri is still hard to manage. With it embedded in code we must recompile the client to change the connection details. We can load the uri from some other place, say a configuration file, environment variable, or the command line, but all we've done is move the problem to yet another hard to manage place. (Here 'manage' specifically refers to the process of obtaining the uri or updating it after a change.)
What can we do to make the uri easier to manage? The main issue is that the service implementation and deployment specifies what the uri should be. Any change to the service implementation or deployment could trigger a need to update the uri that clients use to connect to it. So, one fact (connection uri), one source (service implementation and deployment), but a delivery path which is not automatic and often includes a human. Can you imagine trying to update 30,000 clients with a new connection uri?
The classic solution applies here and represents the start of the next phase in etch development. Etch needs a Name Service.
At its heart a name service is easy. We use them every day without really thinking about them. Given a simple but abstract identifier a more complicated but concrete identifier is produced by some sort of lookup. In exchange for the abstraction, we obtain some independence from the complicated details of the concrete identifier, allowing the concrete identifier to change as needed by the environment. For example, apache.org is translated into the internet protocol v4 address 184.108.40.206 for us by dns. My name, sccomer, is translated into a uid (1079) by my nearby Linux box and used for all sorts of evil purposes. In both cases I can use the simple name as a substitute for the more complicated name seamlessly in the environments I work in. Let's call the abstract identifier the source. Let's call the concrete identifier the target.
In order for the name service to be useful to me, I have to be able to depend upon and trust it. That is, the translation process needs to be available when I need it, and the translations need to be accurate and, most importantly, secure. By secure I mean that if I'm going to connect to a service and supply some credentials and use it to do my work, I have an expectation that the service itself is reliable and trustworthy. That it is not being spoofed. This translates into access controls limiting who can make changes to the underlying data used to implement the name service translation, safeguards to prevent the translation being modified in transit, and some assurance that I'm using the right name service instance.
The following design principles are important to adhere to:
- A service or application should not have be overtly aware of the name service. It should be possible to deploy a service or application with or without the name service, with no conditional code or changes to code. Thus use of a name service is purely a deployment consideration and is not required.
- The name service should be supportable in a variety of styles or modes without changing the fundamental functional interface. Indeed, the basic contract should be very simple.
The name service should be defined using etch. Perhaps this is obvious but I'll say it here for completeness.
The source should be specified in a uri. This allows us to exchange the target uri for a source uri + name service api to achieve our goal.
The existing client framework would already be Naming Service ready, in the sense that, even with the old uri format, everything would work as it used to work before. This transparency allows the current clients to be completely forward compatible with the new Naming Service functionality underneath. Obviously, if the client decides to update the uri to the new uri (according to the Naming Service specifications), that would be supported too.
To implement the functionality of the name service we need to have these elements:
- An api to access the name translations, update them, etc.
- A mechanism to protect the name service api from unauthorized access.
- An etch connection scheme which uses the name service api to automate the translation process for the client, and the publishing mechanism for the listener.
Within a given name service database may be many entries offering the same (or, essentially the same) api, that is, etch service name (e.g., etch.examples.perf.Perf). This is just as it is for any other service available over the network (nfs file servers, smtp mail servers, jabber im servers, etc.). This suggests that the api might be useful as an organizational concept for the name service database.
Suppose we partition the name space into domains based on the api being offered. Within a given api domain, there may be a number of named instances. The instance name is used to uniquely identify a running instance of the service offering the api. There may be several ways to access an instance (called schemes in etch). The instance might offer tls and soap schemes, for example.
This suggests a three part name: api, instance, and scheme. We can combine the three parts into a single name by using the slash character as a separator. This gives the name a path-like quality and also allows it to be easily embedded in a uri:
One obvious way to express api is to use the fully qualified service name of the etch idl. These names are composed of standard identifiers separated by periods (e.g., org.apache.etch.examples.perf.Perf).
The instance name and scheme should not contain the slash character for obvious reasons. Since we want to embed these in a uri, the instance name and scheme should not contain any other uri significant characters either. If we stick to the same format as the api, we still have a large and interesting name space to work with.
Since the scheme corresponds to a uri scheme name, then the same uri scheme syntax is required. This is pretty much a standard identifier.
This partitioning of the source name is not required, just a suggestion.
So, here is a fully specified source for the Perf service named foo with tcp scheme:
Name Service Api Details
Please see the accompanying ns.etch file for specific api details and documentation.
An etch scheme is introduced to gain automatic access to the name service. This removes from most clients and listeners any burden relating to name service, and makes using a name service essentially transparent (i.e., no program changes are required).
Here's an example etch scheme:
This would connect to a name service, lookup the source, and then connect to the returned target. We did have some words here about alternative specifications, perhaps searching based on location, etc. This was felt to complicate the api and could be handled in a better way by a more sophisticated service implementation.
Nameservice Client and Listener details
Which name service is used?
The name service instance to be used might be configured in a number of ways:
- Via the source uri,
- Environment variable,
- Via web container, application resources, configuration file,
- Dhcp option (configures all clients within a subnet).
How does one configure the name service instance via the source uri?
The name service instance uri can be directly embedded into the source uri as follows:
The host portion of the uri would be taken to be the location of the name service instance. The problem with this approach is that it isn't a well-formed uri. An alternative that works better is this:
etch:org.apache.etch.examples.perf.Perf/foo/tcp?ns=<uri for the name service instance>
This technique gives more flexibility at the cost of having to escape all the special characters in the ns uri.
How does one configure the name service instance via environment variable?
Before the program is run, an environment variable would be defined which specified the uri of the name service. The name service code would know to look for that environment variable.
How does one configure the name service instance via web container, etc.
Many web containers have a mechanism for passing settings to a configured .war file. Alternatively, a specially named file in the path or a property in a configuration file would serve the purpose. It would be up to the installation / deployment process to set the appropriate value.
How does one configure the name service instance via dhcp?
Many enterprise class dhcp servers support a lightweight option system associated with the network, scope, or host. One can query these options by broadcasting the request onto the local network and receiving a response. Common option requests are for subnet mask, router, dns servers, domain name, lease time, etc. An option number would have to be assigned, and then the name service instance uri value configured as appropriate for each dhcp server in the network. Option space may be limited, so this may not be practical. See RFC 2132.
Can name service be partitioned or federated?
Yes. The name space could have structure which suggests a hierarchy of name service instances serving various domains. A request to lookup or register a name not in the local domain could be routed to another name service instance for processing.
Can name service be replicated?
Yes. Using the subscribe feature, one name service could replicate the contents of another (in a publisher/subscriber fashion). Lookup could be satisfied locally, while register could flow through to the publisher or the relationship could be more symmetric.
If I connect to a service using the uri etch:Foo/bar/tcp and the connection goes down, how do I reconnect?
When a connection is brought up, the source is looked up to discover the target to connect to. If the connection goes down and is then brought up again, that lookup process must be repeated to ensure you are connection to the most current value of target for the given source.