There have been no administrative functionality in ZeroMQ, nor there is one in nanomsg. Adding such functionality is currently being discussed on nanomsg mailing list and I feel that it may prove confusing for those not directly involved in the discussion. This blog post thus tries to introduce the basic idea and the use cases without digging too deep into the implementation details.
When you are connecting two components via nanomsg, you typically do it something like this:
nn_connect (s, "tcp://192.168.0.111:5555");
The problem with that is that the IP address is hardcoded into the executable. When you get to the point of deploying the application from the development environment to the production environment, the IP addresses are going to change. Even if hostnames were used instead of IP addresses, they are going to change when the application is deployed to a different environment. Thus, to be able to do the transition without having to re-compile the executable, you can, for example, pass the connection string as a command line argument:
$ ./mycomponent tcp://192.168.0.111:5555
And the implementation would look like this:
nn_connect (s, agv [1]);
The above works OK, however, when there is large number of sockets in the application, the command line approach becomes rather annoying. In such cases people often prefer to use configuration files:
$ cat mycomponent.config
socketA tcp://192.168.0.111:5555
socketB tcp://192.168.0.111:5556
socketC tcp://192.168.0.23:978
And the corresponding implementation in the component would look something like this:
nn_connect (s, get_config_option ("myconponent.config", "socketA"));
To understand the full extent of the configuration problem you have to take following facts into account:
- The socket can be either connected (nn_connect) or bound (nn_bind).
- There may be multiple connects and binds on a single socket.
- There are certain socket options that should be set be administrator rather than by programmer.
When you build all this flexibility into the configuration file syntax, you'll end up with something like this:
$ cat mycomponent.config
socketA sndprio 4
connect tcp://192.168.0.111:5555
sndprio 8
tcp_nodelay 1
connect tcp://192.168.0.111:5556
socketB tcp_nodelay 1
bind tcp://eth0:978
If even the configuration files are not flexible enough for you, you may want to have all the configuration data stored in some kind of database and let the socket query the database to retrieve the connects, the binds and the socket options. The upside of this approach is that you don't have to distribute the configuration files to all nodes in the system. The downside is that you have to install and maintain the database in the first place.
If you decide to keep the configuration information in a database, even more sophisticated functionality becomes possible. Say, you have 1000 identical blade servers in your datacenter, all of them meant to run the same component. With naive implementation of the configuration database you would have to create 1000 identical records. If you added new server to the cluser you would have to add a new record. Given that all those records are the same, wouldn't it be nicer if you had just one record instead? In such case you wouldn't even have to mess with the database when new server is added to the cluster.
If you decide to implement such functionality, you need some kind of wildcard matching, or even more complex query algorithms built into the configuration service.
At this point, I believe, the enterprise-focused readers can already imagine the future GUI tools that would help you configure the messaging topologies by drawing actual graphs etc.
Now, given that 100% of the applications have this configuration management stuff written by hand, it makes sense to implement it once and let everyone use it.
Furthermore, hand-written configuration management is likely to be limited in some ways and don't allow for the full flexibility discussed above. For example, passing connection string is passed as an command-line argument:
nn_bind (s, agv [1]);
may seem to be the minimal viable solution. However, it holds only to the point where the admin wants to connect the socket instead of binding it. Call to nn_bind is hard-coded into the executable and the admin has to modify and re-compile the component — if that is at all possible (consider binary packages!)
To summarise the points above: Current discussion on nanomsg mailing list is about adding a functionality to nanomsg that is required by 100% of the applications and which is very easy to get wrong, if written by hand. Thus, I don't believe it classifies as "unnecessary complexity" or "feature creep".
Finally, let me list some finer questions raised in the discussion:
- Should the configuration system be pluggable? In other words, should we allow plugins for MySQL, Redis, DNS, etc. to store the configuration data?
- What are the pros and cons of different storage solutions? For example, using DNS to store configuration data feels like a nice solution, because DNS is already installed and running everywhere. You don't have to install and maintain it as you would have to do with MySQL server. It has also nice caching behaviour and even allows for globally accessible configuration records — such as delivering address of a service to the remote clients. The downside, on the other hand, is that write access to DNS is often restricted to network administrators which are not necessarily the same people as those deploying the application.
- How should a socket identify itself to the configuration database? In other words, what is the key for configuration records. Socket name? IP address? Hostname? Combination of all the above?
- What should be in the body of the configuration records? Should socket options be there? If so, which of them? Etc.
EDIT:
I think I haven't made it clear what's the ultimate goal of the whole endeavour. Let me explain it in form of an use case.
Imagine that we are going to create a distributed application that processes meteorological data. To do so we need a pub/sub topology to distribute the raw meteorological data to individual components.
First, the admin creates the topology by adding a record to DNS, say a TXT record called "meteo-data" with the value of "pub connect tcp:server001:5555; sub connect tcp:server001:5556;". Then he starts a pub/sub device at server001.
Second, programmers of individual components connect to the topology by its name rather that to particular IP address or hostname:
s = nn_socket (AF_SP_CONFIG, NN_PUB);
nn_connect (s, "meteo-data");
And that's it. As can be seen there are no configuration files, no need to distribute anything or install any additional software etc.
Martin Sústrik, September 10th, 2013
It is true that 100% of applications need configuration. That being said I am not so sure that every 3rd party component (nn) should have it's own config file. If you look at it from a broader context then you will see that an "application" already has a config file (my case). Introducing nano into the mix would be more difficult if it involved tearing some parts of it out.
If you decide to use the config file approach then I suggest you use coyaml from tailhook. Best I've seen.
That's a pretty relevant argument. In short, if there's already an application config file, why not store the topology info in that file as well?
My thinking about it is that the topology and the application are conceptually different entities. For example, you can have a pub/sub topology for distributing weather forecasts that can be used by many different applications. Someone sets the topology first, makes sure that the data flow through etc. Then the applications can be written to consume the data from the topology.
In other words, topology is a full-blown stand-alone entity. It exists even if no application is connected to it, same way as network infrastructure exists even though no data is passed through at the moment.
Personally, I wouldn't worry too much about pluggable systems to store configuration data. The "public" interface for configuration information can be a configuration file. It works for almost every other system service I work with. I can then use something external (puppet, chef, a bunch of home grown shell scripts talking to MySQL, whatever) to generate that configuration file. Doing it this way neatly decoupling the actual config from the way it's stored and distributed, and means you don't have to care about supporting a whole bunch of extra dependencies.
That said, I think there's a certain elegance to storing the connection information as SRV records in DNS. :) Then you just need to configure applications to look for a well known service name to get the connection details. On the other hand, that's not really flexible enough to distribute anything more than host/port information, so you still need admin configuration files…
On the third hand, nanomsg is fundamentally a library, and it's not the "role" of the library to configure itself. That's the responsibility of the host application it's integrated with. I personally dislike the notion that log4j has its own separate configuration file in every Java app I deploy, for example (while the log4j config format is consistent across apps, the actual configuration data is very app specific, so there's no real benefit to it being in a separate configuration file!). Perhaps the goal of nanomsg is to make the API amenable to easy configuration (though I'm not sure exactly what that means!)?
Anyway, this is random drive-by commenting from an outsider, who doesn't understand the broader conversation, so feel free to take it with a pinch of salt. ;-)
nanomsg is meant to be a part of networking stack. Right now it's in user-space library, but the long-term prospect is to make it part of the kernel.
As an analogy, let's think of TCP/IP stack. Once it was not integral part of the operating system, rather a stand-alone library.
Back then it could be argued that it's not the task of the library to configure itself. Rather, every application should supply all IP configuration options itself.
Fortunately, that's not what happened. Today, IP is configured on the machine-wide basis and applications can simply use it without having to deal with the configuration of the stack.
As someone that worked at TIBCO i think it is worth folks looking at FTL in this context. FTL provides a central configuration management solution. This has some very useful applications. However, I also think FTL suffers by not offering a way for customers to configure the library locally.
FTL uses a JSON configuration format. Which I think is a reasonable way to go with nanomsg. The library could provide a simple API for getting that JSON from somewhere else (mongodb, a rest api, a web server, …) as a URL, providing the JSON as a string, or building library elements manually.
If you want to go a step further, FTL provides dynamic updates, where changes made at the central server are pushed out to the clients.
Anyway, I think FTL offers a nice model for this option and is worth looking at.
As for the URLs mapping to config into, yes, that's what being discussed currently. Paul Colomiets made a nice prototype for that and was able to set up a rather complex topology with it:
https://docs.google.com/file/d/0Byd_FTQksoKLY3lyZnlQalFtRnc/edit
As for the dynamic updates, that's basically the DNS solution. You update the DNS records and they get automatically propagated everywhere.
As for the local configuration of the library, that's why "pluggable" configaration service was discussed. The idea was that you can plug in a component that reads the config info from a config file, one that reads it from DNS or yet another that reads if from MySQL.
That being said, there are some questions to answer about local config files. Specifically, as Jiri Sedlacek notes above, if the configuration file is local, why not make it part of the application's config file. That defintely makes sense, but kills the idea of having a single generic mechanism for retrieving config into (the format of app's config file can be different for each app).
I'm far from being an expert in configuration management, but i'm quite sure that there are many ways of solving the problem, all better than the others given a particular context (technology, legacy, security, philosophy, corporate guidelines, personnal habits, etc).
I tend to believe that the fewer roles a component has the better it is. Then ability to compose several pieces together at will is Freedom.
I still haven't got time to dive deep into the source of nanomsg, but what appealed to me from the outside was its name, that it is written in c, has minimal api and a modular protocol system.
It would be nice to have another beautiful piece of software dedicated to solve the specific problem of configuring network topologies but I think it should be fully decorelated from nanomsg.
Thanks again for your blog and your work on nanomsg.
I would love to have the two fully separated myself.
The problem with that is that that way you would get 2 sets of APIs:
nn_socket vs. nn_config_socket, nn_connect vs. nn_config_connect etc.
That in turn means that every language binding would have to split into 2 projects, one providing access to the library, other one providing access to the config service.
I think I tend to agree with the view that nanomsg should be first and foremost a networking library so I'd be hesitant to start blending into it config management. On paper it perhaps looks like a good idea, but my feeling is that it most likely won't fit most people's requirements fully and would suffer dreaded feature creep as a result.
I also disagree with your assertion that admins demand the power to dictate whether sockets are bound or connected. Of course, things like connect addresses, ports, etc should be under the control of admins but it sounds like you'd like to delegate topology decisions and architectural design to a group of people who would perhaps consider it the system developers responsibility to build a sane, fault resistant and scalable system.
This is certainly my experience when building backend systems at scale. I would rather submit a newly developed system that has been architecturally designed against a topology model that's been peer reviewed, than to simply hand over a collection of components and say "we'll, it's up to you now…".
Well, how would you do that if you don't know how the network in the production environment looks like?
As for bound/connected it's seen often in the wild. Say, in dev env you bind a service a let clients connect to it. In prod env you start a device and let the service connect to it.
If you have no idea how the network looks in the production environment, then you're already heavily compromised as a system designer.
It sounds like you have a totally different view on this, presumably based on feedback you've received that people really want to write generic components that can be glued together in various configurations by a team of people who don't necessarily have intimate knowledge of how these components work.
Anyway, my point was that I hope nanomsg continues to offer a simple interface for connecting and binding sockets while perhaps supporting an additional interface to support a richer feature set for socket configuration. It sounds like this is your plan so that makes me happy :-)
My understanding is that you almost never know how the production network will look like. It may be the case with your personal hobby projects, but elsewhere there are only 2 options:
1. The software is generic. It will be sold to multiple customers and deployed in their environments. Programmer has no idea what will those networks look like.
2. The software is single-purpose and will be deployed at a single site the programmer is familiar with. Even in such case, time has to be taken into account. Typical lifespan of an enterprise product is ~10 years. During that time the underlying network is going to change significantly. Companies grow and merge, new technologies are introduced etc. In short, when writing the software, the programmer has no idea what network it will be running on in the future.
Anyway, as for separation of core functionality and configuration functionality, I am much in favor of that, however, I don't know how to do that without hurting user experience much (see one of my comments above).
We don't have hobby projects at Spotify :-)
1. The software is generic. It will be sold to multiple customers…
Agreed.
2. The software is single-purpose and will be deployed at a single site…
I'm not sure I agree with your ~10 years assertion, but I get the point you're trying to make. It just makes me a little nervous that someone can potentially reorganise a system topology without a full understanding of how it works. And without any changes to the code. I just don't think this fits with reality.
Ah, it's you :) I haven't realised.
As a historical analogy, consider that once it must have been inconceivable that you could simply speak to any other box by specifying only its address: "You know, this new IP thing makes me feel uneasy. Just imagine that the admins are supposed to re-arrange the network without understanding how the applications work. And with no changes to the code. It seems like Vint and Bob are completely off the track."
Thus, let's treat it as a challenge. At the moment we have no way to build network-layout-unaware applications. Wouldn't it be nice to have that one day?
You can use DHCP for this. Check this old (2004) presentation, where Yannick Cadin explains how to use a DHCP server to send configuration to several applications, not only dns config, ip, ntp and other minimal config.
HTH
http://2004.eurobsdcon.org/uploads/media/EBSD04_slides_41.pdf
That's interesting. I haven't even though of DHCP as an option.
You can look how sqlite pragmas work. You can configure sqlite at compile time as usual and /or sending pragmas, this manner you can modify threading support, buffer memory, wal mode and a long list of different settings, some of them at running time.
What about testing?
There are (at least) two annoyances in (integration) testing with current networking stacks:
Given that nanomsg really "just" provides a virtual network on top of IP, it seems to me that omitting a "user provides all configuration" (via files or whatever) option would be a mistake when it comes to testing support. Such configuration would actually also have to be per-context (in ZMQ terminology) to enable parallel testing without spawning multiple instances of the application — usually spawning separate instances of the application would not be a problem, but it is a big problem if you're running with a managed runtime (JVM or .Net equivalent, for example) because of the startup time.
As for the issue with TCP ports, look here: http://250bpm.com/blog:18 At the end, there's a proposed solution for multiplexing over a single TCP port. I've already done some of the implementation work, actually.
As for the other point, I don't really follow. May you elaborate?
Re: port multiplexing. That's excellent, though the "port" bit is really just a detail — my point is really that if there's some exclusive resource which cannot easily be configured for test setups then that becomes a problem for parallel testing.
I suppose the worst case here really is parallel testing of the same application — let's say we want to run tests for all branches of an application (on push).
The second point basically just goes to the possibility of isolating test configuration from "real" configuration — I think partially related to your point 3 (identification), but a bit broader, I think. Let's assume that I want to run tests for two (or more) applications (in parallel or otherwise) or multiple instances of the same application. Is there some way to have:
AFAICT a simple configuration file (not globally installed) lets one support all of these scenarios, but any kind of "global" configuration (DNS, DHCP, etc.) could place severe limits on what can be done in these cases.
(There's a lot of overlap between these issues and the issues with integration testing against "real" databases.)
Maybe I've just missed something, though. I just wanted to bring your attention to these issues before it's too late :)
Interesting discussion.
In an ideal world, where system administrators are either
A. all doping on happy pills and are highly cooperative or
B. they do not exist
The DNS solution is the best solution i think. Random thought: make a small DNS server available to the app.
Random thought #2
Assuming that either topology could fit into memory, the topology entity should be a data structure (in C) with a CRUD API. Config files or other ways to manage the toplogy would be built on top of the CRUD API. Internally, listener functions will bi-directionally sync the topology data structure with the actual topology of the network. The data structure could also publish changes to an INPROC, IPC, or TCP connection so that you could build real-time visualizations of the topology.
If the fits-in-memory assumption is too strong for your taste, then I vote for DNS or mini-DNS .
Either way, it seems like a separate project from nanomsg.
Post preview:
Close preview