The auditor will listen to a UDP multicast ip:port. Everytime, he hears a musician playing, he will add the troubadour in his intern container which is containing active musicians. In addition, every second, the auditor will clean his container to keep only the one that he heard the past five seconds. It's also possible to etablish a TCP connection to get the musicians container of the auditor (in JSON format)
When creating a musician, we specify the instrument we want him to play. He will then send a UDP package every second containg his uuid (that as been generated when he has been created) and the sound his instrument (in JSON format).
These variables can be found in docker/image-auditor/src/config.js
ENV. VARIABLE | DESC. | VALUE |
---|---|---|
NETWORK.udp.ip | Multicast address. We listen to Musician, that will broadcast on this address. | 239.255.22.5 |
NETWORK.udp.port | Same as ip. We need to have the same port on the Musician endpoint. | 1450 |
NETWORK.tcp.port | TCP port where active musicians are sent. Used by the validation protocol. | 2205 |
AUDITOR_CONFIG.clean_musicians_interval | Time in ms when we update our list of active Musician. | 1000 |
AUDITOR_CONFIG.max_musician_delay | Time in ms, under which a Musician is considered as active. | 5000 |
INSTRUMENTS | For each noise, the instrument that made it. | {"ti-ta-ti":"piano","pouet":"trumpet","trulu":"flute","gzi-gzi":"violin","boum-boum":"drum"} |
These variables can be found in docker/image-musician/src/config.js
ENV. VARIABLE | DESC. | VALUE |
---|---|---|
NETWORK.udp.ip | Multicast address. We make music for anyone listening here. | 239.255.22.5 |
NETWORK.udp.port | We make music for anyone listening on the combinaison IP:PORT. | 1450 |
MUSICIAN_CONFIG.play_interval | Delay between each sound a musician will emit a sound if he's active. | 1000 |
INSTRUMENTS | For each instrument, the noise it produces. | {"piano":"ti-ta-ti", "trumpet":"pouet","flute":"trulu","violin":"gzi-gzi","drum":"boum-boum"} |
We have the following docker images.
NAME | DESC |
---|---|
res/auditor | Docker image containing the Auditor nodejs application. |
res/musician | Docker image containing the Musician nodejs application. |
How can we represent the system in an architecture diagram, which gives information both about the Docker containers, the communication protocols and the commands?
Here's a (simplified) schema representing the entities and how they communicate to each other.
As we can see, the end user chooses the instruments he wants the musician to play, when running the docker. Then, the musician emits a sound with the instrument thanks to UDP. The auditor listen on the same UDP port and can obtains the sound and determine the instrument used.
The validation block is kind of a "entité magique". We don't need to know what it does, but only how to communicate with it. And we communicate with it with TCP on the port 2205.
The musicians will send a UDP datagram every seconds.
The auditor will receive the UDP datagram and store the musician into a Map using the musician's uuid as key.
The musician wil send his uuid and the sound his instrument's making. eg:
{
uuid: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
sound: "pouet"
}
What data structures do we need in the UDP sender and receiver? When will we update these data structures? When will we query these data structures?
Inside the UDP receiver/auditor: we use a Map with the uuid of the musician as key and the object as value which can contains the date of his last heard sound and the instrument his playing. Plus, a js object used as a dictionary (key => value) to store the instrument and their sounds. (the ease the use we use the sound as key and the instrument name as value).
Inside the UDP sender / musician: we also use a js object as dictionary. But in this one, we use the name of the instrument as key (and the sound as value).
We can simply call the method JSON.stringify(yourObject)
.
As its name states it (Node Package Manager), npm is a dependency manager for NodeJs. npm is to Javascript what Meaven is to Java and what Composer is to PHP.
The npm install command
can have two purposes. If you use it without arguments it will simply install the needed packages specified into the package.json
(which you be presents where you execute the command). If you specified a parameter after the command eg:npm install trash
, it will install the package trash. It will also add the dependency into the package.json for the next installations.
According to this Stackoverflow post, the flag --save
is no more useful. It was used to add the package into the package.json but as previously mentioned, it's automacally done when using npm install without flag
https://docs.npmjs.com/cli/v7/commands/npm-install#save
How can we use the https://www.npmjs.com/ web site?
To find packages or to read the docs.
We use the package uuid that seems compliant with the RFC4122.
With setInterval(callback, interval)
that execute the callback function each ìnterval milliseconds.
We can use sockets from dgram.
We can acces the command line arguments with process.argv[<idx>]
.
Please note that
<idx> = 0
is the path to nodejs<idx> = 1
is the file ran<idx> > 1
are the arguments So, if we runnode musician.js didgeridoo
, process.argv contains the following sequence :
0: /usr/local/bin/node
1: path/to/musician.js
2: didgeridoo
We use Dockerfiles to build images and run them as container.
To specify the command to launch when the docker container is ready. For instance, if we want to run our node app. We can do ENTRYPOINT ["node", "/opt/app/app.js"]
.
We can use the command $ docker run <image/name>
.
We can use the command $ docker ps
We can kill a container by using the command $ docker kill <container/name>
the container name can be found by using the $ docker ps
command
We can open Wireshark and to read the sent packets in the docker network. . As we can see in the highlighted part (bottom of the image), the data of the musician are present.
We can use where udpSocker has been created with dgram.createSocket("udp4")
udpSocket.bind(NETWORK.udp.port, () => {
udpSocket.addMembership(NETWORK.udp.ip);
});
We use the Map object :
new Map([iterable])
.
MomentJs provides useful methods to compare/manipulate date. For example, we can subtract 5 seconds to a date and then compare with another date (to see if which date is the oldest) with the following code:
var aDatetime = moment(new Date("<add a datetime>"));
var fiveSecondAgo = moment(new Date()).add(-5, 'seconds');
fiveSecondAgo.isBefore(aDatetime)
Note: we didn't use momentjs
because we think it's overkill just two compare two date + the js Date class seems to do the work without any other lib.
We itereate each second on our map of musician and we remove the musicians that didn't emit any sound int the last 5 second. In our case, we overwrite our map with a new filtered map, where silent musicians are removed.
We can implement a simple TCP server with the help of the net module by using the function :
const tcpServer = net.createServer();
tcpServer.listen(1234); // Port
And then by using the diffrent events provided by the server to perform an action to a specific time. eg: when a client has been connected.
tcpServer.on("connection", (tcpSocket) => {
tcpSocket.write(JSON.stringify({quote: "Omelette du fromage"}));
});