ZeroMQ/0MQ Push/Pull memory and routing issues - c#

I have played a while with ZeroMQ and have couple questions/problems that I came up with. Would appreciate if any contributer to ZeroMQ could chime in or anyone who has used or currently uses the library.
* Let's say I have one router/forwarder and 2 different clients(c1,c2). I want to push messages from client1 to client2 through the routing device. The router pulls messages from whichever client (here client1) and publishes them to any subscribed client (here client2). I currently the only way to route such messages to the appropriate client is through pub/sub, however , a) I want to decide how to route at runtime by sending a routingTo tag along with the message body, b) I want to use push/pull to forward to clients, not pub/sub because I want to implement blocking functionality when setting the high water mark property, c) I want to have c1 and c2 connect on exactly 1 port for pushing and 1 port for subscribing. Can I somehow make changes on the router side in order to not having to use pub/sub or is pub/sub the only way to route to clients even I know on the routing side where a message is supposed to be forwarded to? I read that pub/sub drops messages when queue size exceeds the hwm which I dont want. I also do not want to implement the request/reply patters because it adds unnecessary overhead as I do not need replies.
* After running below code (Push/Pull -> Pub/Sub) and sent all messages and have received confirmation that all messages were received the client that pushed messages out still displays a huge memory footprint, apparently there are still huge amounts of messages in the Push socket's queue. Why is that and what can I do to fix that?
Here is my code:
ROUTER:
class Program
{
static void Main(string[] args)
{
using (var context = new Context(1))
{
using (Socket socketIn = context.Socket(SocketType.PULL), socketOut = context.Socket(SocketType.XPUB))
{
socketIn.HWM = 10000;
socketOut.Bind("tcp://*:5560"); //forwards on this port
socketIn.Bind("tcp://*:5559"); //listens on this port
Console.WriteLine("Router started and running...");
while (true)
{
//Receive Message
byte[] address = socketIn.Recv();
byte[] body = socketIn.Recv();
//Forward Message
socketOut.SendMore(address);
socketOut.Send(body);
}
}
}
}
}
CLIENT1:
class Program
{
static void Main(string[] args)
{
using (var context = new Context(1))
{
using (Socket socketIn = context.Socket(SocketType.SUB), socketOut= context.Socket(SocketType.PUSH))
{
byte[] iAM = Encoding.Unicode.GetBytes("Client1");
byte[] youAre = Encoding.Unicode.GetBytes("Client2");
byte[] msgBody = new byte[16];
socketOut.HWM = 10000;
socketOut.Connect("tcp://localhost:5559");
socketIn.Connect("tcp://localhost:5560");
socketIn.Subscribe(iAM);
Console.WriteLine("Press key to kick off Test Client1 Sending Routine");
Console.ReadLine();
for (int counter = 1; counter <= 10000000; counter++)
{
//Send Message
socketOut.SendMore(youAre);
socketOut.Send(msgBody);
}
Console.WriteLine("Client1: Finished Sending");
Console.ReadLine();
}
}
}
}
CLIENT2:
class Program
{
public static int msgCounter;
static void Main(string[] args)
{
msgCounter = 0;
using (var context = new Context(1))
{
using (Socket socketIn = context.Socket(SocketType.SUB), socketOut = context.Socket(SocketType.PUSH))
{
byte[] iAM = Encoding.Unicode.GetBytes("Client2");
socketOut.Connect("tcp://localhost:5559");
socketIn.Connect("tcp://localhost:5560");
socketIn.Subscribe(iAM);
Console.WriteLine("Client2: Started Listening");
//Receive First Message
byte[] address = socketIn.Recv();
byte[] body = socketIn.Recv();
msgCounter += 1;
Console.WriteLine("Received first message");
Stopwatch watch = new Stopwatch();
watch.Start();
while (msgCounter < 10000000)
{
//Receive Message
address = socketIn.Recv();
body = socketIn.Recv();
msgCounter += 1;
}
watch.Stop();
Console.WriteLine("Elapsed Time: " + watch.ElapsedMilliseconds + "ms");
Console.ReadLine();
}
}
}
}

I'm going to suggest that your architecture may be a bit off here.
1) If you need exactly one PUSH and exactly one PULL, remove the device from the middle. Devices are added to an architecture explicitly to mange multiple consumers so that you don't have to update producers each time you add a node. When/If you do get to where you need multiple consumers and/or producers, you're going to need a connection to each node on your device - that's just how they work. In this case, it sounds as though the device is overly complicating your solution.
2) The idea of having the "route to" tag really boggles my mind. Probably the biggest reason to choose messaging over other integration options is to decouple your producers and consumers so that neither side has to know anything about the other (other than where to send the messages in the case of broker-less designs). Adding routing information directly to your logic breaks this.
As to the overhead, I've never experienced this. But then, I've never used the .Net driver for ZeroMQ before and so an uneducated guess would be to look at the .Net driver itself.

Related

Kafka very high latency C#

I am doing some performance tests on Apache Kafka to compare it with others like RabbitMQ and ActiveMQ. The idea is to use it on a messaging system for agents' communication.
I am testing multiple scenarios (one to one, broadcast and many to one) with different numbers of publishers and subscribers and so different loads. Even in the lowest load scenario of one to one with 10 pairs of agents sending 500 messages with 1ms delay between sends I am experiencing very high latencies (average of ~200ms). And if we go to 100 pairs the numbers rise to ~1500ms. The same thing happens on broadcast and many to one.
I am using Windows with Kafka 2.12-2.5.0 and zookeeper 3.6.1 with C# .Net client Confluent.Kafka 1.4.2. I have already tried some properties like LingerMs = 0 according to some posts I found. I have both Kafka and zookeeper with default settings.
I made a simple test code in which the problem happens:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Confluent.Kafka;
namespace KafkaSetupAgain
{
class Program
{
static void Main(string[] args)
{
int numberOfMessages = 500;
int numberOfPublishers = 10;
int numberOfSubscribers = 10;
int timeOfRun = 30000;
List<MCVESubscriber> Subscribers = new List<MCVESubscriber>();
for (int i = 0; i < numberOfSubscribers; i++)
{
MCVESubscriber ZeroMqSubscriber = new MCVESubscriber();
new Thread(() =>
{
ZeroMqSubscriber.read(i.ToString());
}).Start();
Subscribers.Add(ZeroMqSubscriber);
}
Thread.Sleep(10000);//to make sure all subscribers started
for (int i = 0; i < numberOfPublishers; i++)
{
MCVEPublisher ZeroMqPublisherBroadcast = new MCVEPublisher();
new Thread(() =>
{
ZeroMqPublisherBroadcast.publish(numberOfMessages, i.ToString());
}).Start();
}
Thread.Sleep(timeOfRun);
foreach (MCVESubscriber Subscriber in Subscribers)
{
Subscriber.PrintMessages("file.csv");
}
}
public class MCVEPublisher
{
public void publish(int numberOfMessages, string topic)
{
var config = new ProducerConfig
{
BootstrapServers = "localhost:9092",
LingerMs = 0,
Acks = 0,
};
var producer = new ProducerBuilder<Null, string>(config).Build();
int success = 0;
int failure = 0;
Thread.Sleep(3500);
for (int i = 0; i < numberOfMessages; i++)
{
Thread.Sleep(1);
long milliseconds = System.Diagnostics.Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond;
var t = producer.ProduceAsync(topic, new Message<Null, string> { Value = milliseconds.ToString() });
t.ContinueWith(task => {
if (task.IsFaulted)
{
failure++;
}
else
{
success++;
}
});
}
Console.WriteLine("Success: " + success + " Failure:" + failure);
}
}
public class MCVESubscriber
{
private List<string> prints = new List<string>();
public void read(string topic)
{
var config = new ConsumerConfig()
{
BootstrapServers = "localhost:9092",
EnableAutoCommit = false,
FetchErrorBackoffMs = 1,
};
var consumerConfig = new ConsumerConfig(config);
consumerConfig.GroupId = Guid.NewGuid().ToString();
consumerConfig.AutoOffsetReset = AutoOffsetReset.Earliest;
consumerConfig.EnableAutoCommit = false;
using (var consumer = new ConsumerBuilder<Ignore, string>(config).Build())
{
consumer.Subscribe(new[] { topic });
while (true)
{
var consumeResult = consumer.Consume();
long milliseconds = System.Diagnostics.Stopwatch.GetTimestamp() / TimeSpan.TicksPerMillisecond;
prints.Add(consumeResult.Message.Value + ";" + milliseconds.ToString());
}
consumer.Close();
}
}
public void PrintMessages(string path)
{
Console.WriteLine("printing " + prints.Count);
File.AppendAllLines(path, prints);
}
}
}
}
Does someone what can be the problem? What configs can I change to
improve latency?
Thanks,
Davide Costa
Kafka is not really built for low latency message distribution, but for high availability. It can be configured to have lower latency, but you start losing a lot of the advantages Kafka offers.
A few tips/comments below:
On the KafkaProducer side, in general, you want to wait until there's enough messages to send, to as to batch messages more efficiently. That's the linger.ms property you already mentioned. Typically that is set to something like 50ms, so by setting it to zero, you're effectively telling the producer to send data as fast as it gets it. This may make the producer more "chatty", but you have the assurance it will send the data to the cluster as soon as it gets it.
However, once a message is "produced" into Kafka, it waits until it gets an ACK from the lower layer that the broker has received the message successfully. There's multiple options here:
Consider a message as "received" once the message has been sent by the producer. That is, locally, once the network layer has finished sending it, the producer will consider it "sent and acknowledged"
Wait for an ACK from the leader broker which you're sending the message to, depending on which partition it gets assigned, so you at least know one broker has it. THIS IS THE DEFAULT.
Wait for an ACK from the leader broker which you're sending the message to, PLUS an ACK from each of that partitions' replicas on the other brokers. This means, if your cluster has a replication factor of 3, that the message is sent to broker 1 for example, it then replicates that to brokers 2 and 3, which have copies of the same partition, waits for those brokers to reply back saying they got the message, and only THEN reply back to the producer saying the message has been ACK'd. This is typically used in environments where you never want the possibility of losing a single message, so you always guarantee that there will be three copies of your message before the producer moves on.
Official acks explanation from the Kafka docs:
https://kafka.apache.org/25/documentation.html#acks
There are other settings to consider like kafka producer compression and broker compression settings that might add more latency/overhead, but if you're using the defaults (no producer compression and producer option in the broker compression), there should be no additional latency in those steps.
Having said all that, I would suggest you try to set the acks option in the producer to 0, and see how your latency changes. My guess is you will get much better latency, BUT also understand that there are no guarantees your messages are actually being received and stored correctly. A flaky network, a network partition, etc, could cause you to lose data. That might be ok for your use case, but just make sure you're aware of it.

Performance limits for NetMQ

I am sending messages from various places on my 10G network. I am using a Pub/Sub pattern.
The messages I send are serialized using zeroFormatter and have length of approx 270 bytes.
Once I start to send over 150K message per sec, I notice the subscriber starting to miss messages.
How do I work out what the limits I can expect to be able to send are?
EDIT 1:
I am sending just under 1 billion bits/sec, which is a 10th of the capacity of my network. After this point I start to miss messages. Would this be due to CPU issues? Neither sender or receiver seem highly utilized...
private void BackgroundProcess()
{
int msgSeqNum = 0;
using (var server = new PublisherSocket())
{
server.Options.SendHighWatermark = 1000;
server.Bind(Connection);
var address = Key;
FastTickData fastTickData;
while (true)
{
if (O.TryTake(out fastTickData, 60000))
{
msgSeqNum++;
server.SendMoreFrame(address).SendMoreFrame(msgSeqNum.ToString()).SendMoreFrame(DateTime.UtcNow.ToString("yyyyMMddTHHmmssffffff")).SendFrame(ZeroFormatterSerializer.Serialize(fastTickData));
}
}
}
}

Correct pattern for ZeroMQ Server/Client type

I have a server that needs to get instructions to run processes for clients on another machine.
The clients send a job message, the Server processes the job and later sends the back results.
I tried using the NetMQ Request-Response pattern (see below)
This works nicely for 1 client, BUT if a second client sends a request before previous client job is finished - I get an error.
I really need to be able to receive ad-hoc messages from clients, and send results when they are completed. Clearly, I am using the wrong pattern, but reading the ZeroMQ docs has not highlighted a more appropriate one.
namespace Utils.ServerMQ
{
class ServerMQ
{
public static void Go()
{
using (var responseSocket = new ResponseSocket("#tcp://*:393"))
{
while (true)
{
Console.WriteLine("Server waiting");
var message = responseSocket.ReceiveFrameString();
Console.WriteLine("Server Received '{0}'", message);
//System.Threading.Thread.Sleep(1000);
var t2 = Task.Factory.StartNew(() =>
{
RunProcMatrix(message, responseSocket);
});
}
}
}
public static void RunProcMatrix(object state, ResponseSocket responseSocket)
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = Path.Combine(#"H:\Projects\Matrix\Matrix\bin\Debug\", "Matrix001.exe"),
Arguments = (string)state,
WindowStyle = ProcessWindowStyle.Normal,
CreateNoWindow = false
}
};
process.Start();
process.WaitForExit();
responseSocket.SendFrame((string)state);
}
}
}
You want a ROUTER socket on the server side, so it can receive multiple requests at a time. (Guide) REQ sockets on the client side are still fine unless the server may arbitrarily push data to them, then they need to be DEALER sockets.
Note that for sockets beyond REQ/RESP you need to manually handle the message envelope (the first frame of the message indicating its destination). Guide
The 0MQ docs are incredibly dense... I don't blame you for not intuiting this from them :)
This example from the NetMQ docs is full ROUTER-DEALER: https://netmq.readthedocs.io/en/latest/router-dealer/#router-dealer, you can take just the router side and it should work the same though.

C# thread not listening for incoming messages from server

Every time a client connects to my TCP server, it creates a new thread specifically for that client (there are few clients so there's no worry about overflow of threads). However, after lots of debugging, I have found that once 2 clients join, the first client can send about ~3 messages and then will no longer function, however the second will keep functioning no matter how many times you send a message (and the first client will receive it). If a 3rd client joins the party, then the second client will have the same fate as the first one, will be able to send about ~3 messages, and then suddenly stop working without explanation. If someone could explain why this is happening, it would be much appreciated! Here's the code for the server (which I think is where the error is occuring):
s = listener.AcceptSocket();
clients.Add(s);
NewListener();
byte[] b = new byte[100];
while (running)
{
try
{
int k = s.Receive(b);
string message = "";
for (int i = 0; i < k; i++)
{
message += Convert.ToChar(b[i]);
}
if (message == "exitprogram") //break loop
{
break;
}
ASCIIEncoding asen = new ASCIIEncoding();
foreach (Socket client in clients)
{
client.Send(asen.GetBytes(message));
}
} catch
{
Console.WriteLine("Error occured on thread " + Thread.CurrentThread.Name);
break;
}
}

How can I both Send and Receive from a Router socket in ZeroMQ or NetMQ?

I have a Dealer <--> Router setup in NetMQ v4 where I can asynchronously send and receive messages in any direction with no problems.
I now want to formalize that into an abstraction where the server (Router) listens for any incoming message but it also need to on demand broadcasts messages to any of the connected clients (Dealers).
I am trying to avoid using Pub <--> Sub sockets as I need the subscribers to also send messages to the server. The closest pattern to what I am trying to achieve is a WebSocket client-server communication.
The first part of listening to the client messages are done in something like:
using (var server = new RouterSocket("#tcp://*:80"))
{
var addresses = new HashSet<string>();
while (true)
{
var msg = server.ReceiveMultipartMessage();
var address = Encoding.UTF8.GetString(msg[0].Buffer);
var payload = Encoding.UTF8.GetString(msg[2].Buffer);
Console.WriteLine("[Server] - Client: {0} Says: {1}", address, payload);
var contains = addresses.Contains(address);
if (!contains) { addresses.Add(address); }
msg.Clear();
msg.Append(address);
msg.AppendEmptyFrame();
msg.Append("Reply for: " + address);
server.SendMultipartMessage(msg);
}
}
Now given that the sockets are not Thread-Safe, I am stuck on finding a way to broadcasts messages (coming from a different thread on demand) to all the clients.
I can probably use the TryReceiveMultipartMessage method in the loop instead with a set timeout after which I can check a queue for any broadcast messages and then loop through each client sending such message. Something like:
using (var server = new RouterSocket("#tcp://*:80"))
{
var addresses = new HashSet<string>();
var msg = new NetMQMessage();
while (true)
{
var clientHasMsg = server.TryReceiveMultipartMessage(TimeSpan.FromSeconds(1), ref msg);
if (!clientHasMsg)
{
// Check any incoming broacast then loop through all the clients
// sending each the brodcast msg
var broadMsg = new NetMQMessage();
foreach (var item in addresses)
{
broadMsg.Append(item);
broadMsg.AppendEmptyFrame();
broadMsg.Append("This is a broadcast");
server.SendMultipartMessage(broadMsg);
broadMsg.Clear();
}
// Go back into the loop waiting for client messages
continue;
}
var address = Encoding.UTF8.GetString(msg[0].Buffer);
var payload = Encoding.UTF8.GetString(msg[2].Buffer);
Console.WriteLine("[Server] - Client: {0} Says: {1}", address, payload);
var contains = addresses.Contains(address);
if (!contains) { addresses.Add(address); }
msg.Clear();
msg.Append(address);
msg.AppendEmptyFrame();
msg.Append("Reply for: " + address);
server.SendMultipartMessage(msg);
}
}
This somehow does not feel right mainly due to:
What value for the timeout is a good value? 1 sec, 100 ms etc;
Is this the most efficient/performing solution as this program will be used to have 100k+ clients connected with each sending thousands of messages per second.
Any pointers on what's the best approach to this is very much appreciated.
You can use netmqqueue, it multi producer single consumer queue. You can add it to NetMQPoller and enqueue from multiple threads without lock.
I think PUB/SUB is a proper approach for your requirements of 100k+ clients. Nevertheless, it doesn't mean that you can't communicate back to server: use DEALER/ROUTER. Why do you think this solution is not acceptable?

Categories

Resources