ZeroMQ: Many-to-one no-reply aynsc messages - zeromq

I have read through the zguide but haven't found the kind of pattern I'm looking for:
There is one central server (with known endpoint) and many clients (which may come and go).
Clients keep sending hearbeats to the server, but they don't want the server to reply.
Server receives heartbeats, but it does not reply to clients.
Hearbeats sent when clients and server are disconnected should somehow be dropped to prevent a heartbeat flood when they go back online.
The closet I can think of is the DEALER-ROUTER pattern, but since this is meant to be used as an async REQ-REP pattern (no?), I'm not sure what would happen if the server just keep silent on incoming "requests." Also, the DEALER socket would block rather then start dropping heartbeats when the send High Water Mark is reached, which would still result in a heartbeat flood.

The PUSH/PULL pattern should give you what you need.
# Client example
import zmq
class Client(object):
def __init__(self, client_id):
self.client_id = client_id
ctx = zmq.Context.instance()
self.socket = ctx.socket(zmq.PUSH)
self.socket.connect("tcp://localhost:12345")
def send_heartbeat(self):
self.socket.send(str(self.client_id))
# Server example
import zmq
class Server(object):
def __init__(self):
ctx = zmq.Context.instance()
self.socket = ctx.socket(zmq.PULL)
self.socket.bind("tcp://*:12345") # close quote
def receive_heartbeat(self):
return self.socket.recv() # returns the client_id of the message's sender
This PUSH/PULL pattern works with multiple clients as you wish. The server should keep an administration of the received messages (i.e. a dictionary like {client_id : last_received} which is updated with datetime.utcnow() on each received message. And implement some housekeeping function to periodically check the administration for clients with old timestamps.

Related

ZeroMQ Pub-Sub implement privileges for specific topics for specific subscribers

Is it possible, in ZeroMQ Pub-Sub model, to filter out (disallow) some topic for specific subscribers for security reasons? If not, what other pattern could match such architecture?
I think the only way to get what you want -- while still using pub/sub sockets -- would be to create an xpub/xsub proxy server. You'd have a structure something like:
Privileged clients connecto the upstream publisher, while "restricted" clients connect through the proxy.
Here's an example proxy implementation in Python; a C/C++ solution would use similar logic:
import zmq
import random
import time
ctx = zmq.Context()
upstream = ctx.socket(zmq.XSUB)
downstream = ctx.socket(zmq.XPUB)
upstream.connect("tcp://localhost:3000")
downstream.bind("tcp://127.0.0.1:3001")
poller = zmq.Poller()
poller.register(upstream, zmq.POLLIN)
poller.register(downstream, zmq.POLLIN)
secret_topics = ["topic3"]
while True:
socks = dict(poller.poll())
if upstream in socks and socks[upstream] == zmq.POLLIN:
msg = upstream.recv_multipart()
# We've received a messages from the upstream
# publisher. Let's see if we should block it...
if msg[0].decode() in secret_topics:
# It's a secret message, don't publish it to
# our subscribers!
print("upstream !!", msg)
continue
# If we get this far, publish the message to our
# subscribers.
print("upstream ->", msg)
downstream.send_multipart(msg)
elif downstream in socks and socks[downstream] == zmq.POLLIN:
# This is a message FROM the subscibers TO the
# publisher (i.e., a subscription message)
msg = downstream.recv_multipart()
print("downstream ->", msg)
upstream.send_multipart(msg)
A SUB socket client will connect to this instead of the publisher, and the proxy will filter out messages that have topics matching an item in secret_topics.
The next question becomes, "how do I prevent the client from connecting to the upstream publisher?", to which the answer is probably to implement authentication so that only authorized clients can connect to the upstream publisher, and everything else connects to the filtering proxy (or require different auth for the proxy).

Broadcasting message from grpc server to all/some connected clients in python

i am learning how to use grpc streams to exchange messages between clients and server in python. I found a base example that enables the simple message sending between server and client. I am trying to modify it so that i could keep track of all the clients connected to the grpc server (on the server side) and could do two things: 1) broadcast from server to all clients, 2) send message to a particular connected client.
Here is the .proto file
syntax = 'proto3';
service Scenario {
rpc Chat(stream DPong) returns (stream DPong) {}
}
message DPong {
string name = 1;
}
And here is the client.py that creates a daemon process to listen for incoming messages and waits for stdin for any outgoing messages
import threading
import grpc
import time
import scenario_pb2_grpc, scenario_pb2
# new changes
msgQueue = queue.Queue()
def run():
channel = grpc.insecure_channel('localhost:50052')
stub = scenario_pb2_grpc.ScenarioStub(channel)
print('client connected')
global queue
def inputStream():
while 1:
msg = input('>>Enter message\n>>')
yield scenario_pb2.DPong(name=msg)
input_stream = stub.Chat(inputStream())
def read_incoming():
while 1:
print('receivedFromServer: {}\n>>'.format(next(input_stream).name))
thread = threading.Thread(target=read_incoming)
thread.daemon = True
thread.start()
while 1:
time.sleep(1)
if __name__ == '__main__':
print('client starting ...')
run()
Below is the server.py
import random
import string
import threading
import grpc
import scenario_pb2_grpc
import scenario_pb2
import time
from concurrent import futures
clientList = []
class Scenario(scenario_pb2_grpc.ScenarioServicer):
def Chat(self, request_iterator, context):
clients = []
def stream():
while 1:
time.sleep(1)
msg = input('>>Enter message\n>>')
for i in clientList:
yield msg
output_stream = stream()
def read_incoming():
while 1:
received = next(request_iterator).name
if (context,request_iterator) not in clientList:
clientList.append((context, request_iterator))
print('receivedFromClient: {}'.format(received), len(clientList))
thread = threading.Thread(target=read_incoming)
thread.daemon = True
thread.start()
while 1:
msg = output_stream
yield scenario_pb2.DPong(name=next(msg))
if __name__ == '__main__':
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
scenario_pb2_grpc.add_ScenarioServicer_to_server(
Scenario(), server)
server.add_insecure_port('[::]:50052')
server.start()
print('listening ...')
while 1:
time.sleep(1)
So far, i have tried to maintain a list object clientList that contains the context & request_iterator object of the client, and is updated every time a new client joins the server. But how do i set these object from the clientList before sending out an outgoing message? I have tried to iterate the list but the server sends the message to the same client (the last client heard from) a number of times instead of sending it to all the clients once.
Any help is highly appreciated!
This is certainly possible. The problem that you're running into here is that each call to Scenario.Chat on the server side corresponds to a single client connection. That is, this function is called when the streaming RPC starts and as soon as the function exits, the RPC ends.
So if you want n connected clients, you'll need n instances of Scenario.Chat running concurrently, each on its own thread. This does mean that the number of concurrently connected clients is limited by the size of the threadpool with which you instantiate your server.
So, let's say you have n threads in your server process dedicated to maintaining client connections. Then you need another n+1th thread (perhaps the main thread) determining when the server will broadcast a message to all clients (maybe by looking for input from STDIN?). When this extra thread determines that a message should be broadcast, it needs to communicate this intent to all of the threads maintaining connections to a client. There are many ways to make this happen. A threading.Condition and a global collections.deque, or a collections.deque per client connection (somewhat like channels between goroutines) would be two ways. The tricky bit here is ensuring that each client connection will receive the message regardless of how long the client connection thread takes to wake up and how many messages the n+1th thread decides to send in the interim.
If this is still unclear, I can follow up with some actual code demonstrating the idea.
You can spin up multiple ports in one application.
gRPC can be running in port 50011 and flask with socket.io can be running in port 8080
with python, you can use the flask framework and flask_socketio library in your server.py
eg server.py
from flask import Flask
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app)
#app.route('/')
def index():
return "Hello, World!"
if __name__ == '__main__':
app.run(port=8080)
app.run(debug=True)
socketio.run(app)
instead of using gRPC streaming API, use WebSocket to broadcast to all connected clients and specific/selected clients using rooms.
eg
#socketio.on('message')
def handle_message(data):
// logic to send large data in chunks the logic should call the
// emit function in socket.io and emit an event that send the large
// data in chunks eg emit('my response', chunkData)
gRPC is primarily built for one client request and response and WebSocket is for multiple clients.

zeromq pattern for multi client pull receive from one server pushing socket

I want to use zeromq for several clients labeled as 1...n to pull receive from one same socket in server pushing messages containing one field as client id. When server push message labeled 1, only client 1 receives it.
one way is to generate the same number of server sockets for clients to one by one connect, which I think there may be a better solution. Thanks a lot.
Use "Publish/Subscribe" pattern and set socket options for filtering messages.
Code for SUB side:
context = zmq.Context()
socket = context.socket(zmq.SUB);
socket.connect ("tcp://localhost:%s" % port)
topicfilter = "10001"
socket.setsockopt(zmq.SUBSCRIBE, topicfilter)
string = socket.recv()
Code for PUB side:
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://*:%s" % port)
topic = 10001
messagedata = random.randrange(1,215) - 80
socket.send("%d %d" % (topic, messagedata))
Examples and pattern describe here
Update
Another pattern which you may use Router-Req.
Common idea:
Examples for python here

Ready to reuse WebSocket connection server with Redis/ZeroMQ backend wanted

I need horizontally scalable WebSocket connection server for chat like system, where browser clients connected to different WebSocket servers coould exchange messages within separate chat rooms.
Clients HaProxy WebSocket server1 WebSocket server2 Redis/ZeroMQ
| | | |
client A ----=------------>o<----------------|------------------>|
| | | |
client B ----=-------------|---------------->o<----------------->|
| | | |
Here client A and client B are connected through HaProxy to two different WebSocket servers, which exchange messages through Redis/ZeroMQ backend, like in that and that questions.
Thinking of building that architecture I wonder if already there is an opensource analog. What such a project would you suggest to look at?
Look into the Plezi Ruby framework. I'm the author and it has automatic Redis scalability built in.
(you just setup the ENV['PL_REDIS_URL'] with the Redis URL)
As for the architecture to achieve this, it's fairly simple... I think.
Each server instance "subscribes" to two channels: a global channel for "broadcasting" (messages sent to all users or a large "family" of users) and a unique channel for "unicasting" (messages intended for a specific user connected to the server).
Each server manages it's internal broadcasting system, so that messages are either routed to a specific user, to a family of connections or all users, as par their target audience.
You can find the source code here. The Redis integration is handled using this code together with the websocket object code.
Web socket broadcasts are handled using both the websocket object on_broadcast callback. The Iodine server handles the inner broadcasting within each server instance using the websocket implementation.
I already posted the inner process architecture details as an answer to this question
I think socket.io has cross server support as well.
Edit (some code)
Due to the comment, I thought I'd put in some code... if you edit your question and add more specifications about the feature you're looking for, I can edit the code here.
I'm using the term "room" since this is what you referred to, although I didn't envision Plezi as just a "chat" framework, it is a very simple use case to demonstrate it's real-time abilities.
If you're using Ruby, you can run the following in the irb terminal (make sure to install Plezi first):
require 'plezi'
class MultiRoom
def on_open
return close unless params[:room] && params[:name]
#name = params[:name]
puts "connected to room #{params[:room]}"
# # if you use JSON to get room data,
# # you can use room arrays like so:
# params[:room] = params[:room].split(',') unless params[:room].is_a?(Array)
end
def on_message data
to_room = params[:room]
# # if you use JSON you can try:
# to_room = JSON.parse(data)['room'] rescue nil
# # we can use class `broadcast`, to broadcast also to self
MultiRoom.broadcast :got_msg, to_room, data, #name if to_room
end
protected
def got_msg room, data, from
write ::ERB::Util.html_escape("#{from}: #{data}") if params[:room] == room
# # OR, on JSON, with room arrays, try something like:
# write data if params[:room].include?(room)
end
end
class EchoConnection
def on_message data
write data
MultiRoom.broadcast "myroom", "Echo?", "system" if data == /^test/i
end
end
route '/echo', EchoConnection
route '/:name/(:room)', MultiRoom
# # add Redis auto-scaling with:
# ENV['PL_REDIS_URL'] = "redis://:password#my.host:6389/0"
exit # if running in terminal, using irb
You can test it out by connecting to: ws://localhost:3000/nickname/myroom
to connect to multiple "rooms" (you need to re-write the code for JSON and multi-room), try: ws://localhost:3000/nickname/myroom,your_room
test the echo by connecting to ws://localhost:3000/echo
Notice that the echo acts differently and allows you to have different websockets for different concerns - i.e., having one connection for updates and messages using JSON and another for multiple file uploading using raw binary data over websockets.

Publisher finishes before subscriber and messages are lost - why?

Fairly new to zeromq and trying to get a basic pub/sub to work. When I run the following (sub starting before pub) the publisher finishes but the subscriber hangs having not received all the messages - why ?
I think the socket is being closed but the messages have been sent ? Is there a way of ensuring all messages are received ?
Publisher:
import zmq
import random
import time
import tnetstring
context=zmq.Context()
socket=context.socket(zmq.PUB)
socket.bind("tcp://*:5556")
y=0
for x in xrange(5000):
st = random.randrange(1,10)
data = []
data.append(random.randrange(1,100000))
data.append(int(time.time()))
data.append(random.uniform(1.0,10.0))
s = tnetstring.dumps(data)
print 'Sending ...%d %s' % (st,s)
socket.send("%d %s" % (st,s))
print "Messages sent: %d" % x
y+=1
print '*** SERVER FINISHED. # MESSAGES SENT = ' + str(y)
Subscriber :-
import sys
import zmq
import tnetstring
# Socket to talk to server
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://localhost:5556")
filter = "" # get all messages
socket.setsockopt(zmq.SUBSCRIBE, filter)
x=0
while True:
topic,data = socket.recv().split()
print "Topic: %s, Data = %s. Total # Messages = %d" % (topic,data,x)
x+=1
In ZeroMQ, clients and servers always try to reconnect; they won't go down if the other side disconnects (because in many cases you'd want them to resume talking if the other side comes up again). So in your test code, the client will just wait until the server starts sending messages again, unless you stop recv()ing messages at some point.
In your specific instance, you may want to investigate using the socket.close() and context.term(). It will block until all the messages have been sent. You also have the problem of a slow joiner. You can add a sleep after the bind, but before you start publishing. This works in a test case, but you will want to really understand what is the solution vs a band-aid.
You need to think of the PUB/SUB pattern like a radio. The sender and receiver are both asynchronous. The Publisher will continue to send even if no one is listening. The subscriber will only receive data if it is listening. If the network goes down in the middle, the data will be lost.
You need to understand this in order to design your messages. For example, if you design your messages to be "idempotent", it doesn't matter if you lose data. An example of this would be a status type message. It doesn't matter if you have any of the previous statuses. The latest one is correct and message loss doesn't matter. The benefits to this approach is that you end up with a more robust and performant system. The downsides are when you can't design your messages this way.
Your example includes a type of message that requires no loss. Another type of message would be transactional. For example, if you just sent the deltas of what changed in your system, you would not be able to lose the messages. Database replication is often managed this way which is why db replication is often so fragile. To try to provide guarantees, you need to do a couple things. One thing is to add a persistent cache. Each message sent needs to be logged in the persistent cache. Each message needs to be assigned a unique id (preferably a sequence) so that the clients can determine if they are missing a message. A second socket (ROUTER/REQ) needs to be added for the client to request the missing messages individually. Alternatively, you could just use the secondary socket to request resending over the PUB/SUB. The clients would then all receive the messages again (which works for the multicast version). The clients would ignore the messages they had already seen. NOTE: this follows the MAJORDOMO pattern found in the ZeroMQ guide.
An alternative approach is to create your own broker using the ROUTER/DEALER sockets. When the ROUTER socket saw each DEALER connect, it would store its ID. When the ROUTER needed to send data, it would iterate over all client IDs and publish the message. Each message should contain a sequence so that the client can know what missing messages to request. NOTE: this is a sort of reimplementation of Kafka from linkedin.

Resources