ZeroMQ is described on wikipedia as a high-performance asynchronous messaging library, aimed at use in distributed or concurrent applications.
It has features of a networking library and touches a lot on concurrency, making it a bit difficult to categorize. I think one can define it using the utility to which it serves the person using it, the authors of the library do it more justice in their definition on their site in a hundred words.
The library has ports to many languages and the site has plenty of examples. For learning purposes i wanted to explore how to implement it in Kotlin which am learning as well. Fortunately for us ZeroMQ has a nice port in Java called JeroMQ
We'll create a basic example that creates a server and client using Kotlin. We begin by creating a kotlin Gradle project using our IDE
We'll import JeroMQ from maven repository using
https://gist.github.com/4c21b21ee3cbc6f17645c749ca80d2fb
It's the only library i'll be using and my complete build.gradle file looks like this
https://gist.github.com/f2c884d86ea3822891f65318a4a1b9ea
We'll create two files MyServer.kt for the server logic and MyClient.kt for the client logic.
MyServer.kt
https://gist.github.com/99d4041ce1ebf3eb78e133e51cd00c06
We'll not create a class for our example but use a simple top level declaration of the main method.
ZMQ.context(1)
We create ZeroMQ Context which takes in the number of io Threads you want the context to use. As ZeroMQ isn't thread-safe, sticking to 1 feels like a safe bet until we're experienced enough to safely use multiple threads.
In this example we'll be using two sockets that go together which will be REQUEST and REPLY sockets (ZMQ.REP and ZMQ.REQ). There are more socket pairs like PUSH & PULL, DEALER & ROUTER, PAIR and some more all of which highlight the versatile scenarios ZeroMQ can be used.
Our server will Reply to the Requests from the client, so it will implement the Reply Socket
val socket = context.socket(ZMQ.REP)
We'll then bind to a free socket of our choice
https://gist.github.com/a7c78065c1a1ca3f4634e59cabc70487
In our case we choose 5879 but in case this socket is being by another program we would get this kind of error when we run the program at the end of our example.
Exception in thread "main" org.zeromq.ZMQException: Errno 48 : Address already in use
. We can simply add one to the last number so if you chose a socket like 5555 and found it was in use by another program, next try 5556 and 5557 and so on, and don't forget to also match the change of the port on the client side as well, to ensure that they communicate with each other.
As we want our server to run non-stop, we create an infite loop, inside this loop is where the server will receive request.
https://gist.github.com/f2946da26a7b80cb557829065347dd9b
In ZeroMQ, Request and Reply are sent as length-specified binary data. So the rawRequest
type is Array[Byte] which can easily be converted back into a String for our usage.
We convert to String as follows and print to the console to see the requests as they come in:
https://gist.github.com/0d884030288056c4315ee8a1c531a6b1
What we're doing above is based on the fact that messages sent on zeromq are zero terminated. When converting to a string we omit this last zero by ommiting the last byte from the request.
https://gist.github.com/e673a41167b4ada494b92115fe5b813d
After we print the Request received, we send a Reply back to the client. In our case the client will be sending in "Hello" and we'll be responding back with "World". We store the string in a variable and then convert it to a ByteArray that can be sent over the wire.
https://gist.github.com/b864b0e36d81b4f67b6228d714b7ea73
We set the last byte of rawReply to 0, this is used as a delimiter between messages. This is used by ZeroMQ to differentiate where one message ends and the other begins
https://gist.github.com/69acc0279d6efec24f6594fa3f8dde47
After that we're ready to send the reply back to the client
https://gist.github.com/747415eba589049895fba677f7a8ec0a
The complete MyServer.kt looks like this
https://gist.github.com/dafc0346083f549f248841e1f1a668db
The client side of the app looks the same with few differences. We create MyClient.kt file with a single top level main method as well. One key this that our socket in this case will not be a Reply socket but a Request one ZMQ.REQ
MyClient.kt
https://gist.github.com/0f9c89f6ce071941ae4141199d768d88
Also note we connect to the same port used by the server. For our client, we'll send 10 requests with the string "Hello" to the server by looping using a range
https://gist.github.com/7cb1b4f3b8bd07a68378ef2fae530e9b
We do the same thing as before with the server and convert our String to ByteArray and then add a 0 delimiter for ZeroMQ before sending it to the socket.
Once it is sent, we'll want to listen for a Reply. We do this immediately after sending the request.
https://gist.github.com/f4e580c8ea40043589b63633d86553f2
We convert the ByteArray reply we receive to a string and print it to the stdout.
The complete MyClient.kt looks as follows.
MyClient.kt
https://gist.github.com/16b1ce2af8dac23a87a070a663c7d622
Once we have both files in place. We'll need to first start the server which will run continously due to the infinite loop then start the client. We first move to the MyServer.kt and press 'run' in our IDE, then move to MyClient.kt and run it -sending the 10 "Hello " requests to the server and receive "World" requests for each.
If anything is unclear or you have any advise on the article please let me know i'll greatly appreciate it. Here's the link to the github repo that contains the project.