I'm very new to ZeroMQ. I've read the guide and am currently going through the examples as well as looking at other relevant info around the web. I'm having some indecision with what message pattern(s) to use or if I should use a combination of 2 patterns.
I have an existing software application that has a home grown messaging system that is in need of replacement. I have a fairly simple architecture:
|Client|<----->|driver1|
|
|---|driverN|
Only one "client" connects to a driver at a time currently, and there may be many drivers.
(in actuality, the client, in this case, isn't truly my client application, but a middleman of sorts. For this discussion, it can be treated as a client)
Messaging:
The client issues commands to the driver.
The drivers return status/state information in response to commands.
The drivers produce data elements (i.e. Not status/state information)
Some Client messages go to all connected devices, some are directed only to a single driver.
Drivers may exist on the same system or remotely on a LAN. This is not a public network.
I'm currently thinking that I would have a pub and sub socket on each driver and a sub/pub socket on the client. Messages shouldn't be dropped once a connection is made. I assume that the client would subscribe to the different driver data types and the driver would then subscribe to the clients command messages.
Important considerations: low latency, lowest possible bandwidth overhead.
I would appreciate any suggestions or recommendations! Thanks in advance!
You picked a great learning exercise, that's for sure!
Read up on these, they provide the basic implementation for request/reply using a custom router-to-router proxy with polling and should address your client-to-device problem.
https://github.com/imatix/zguide/blob/master/examples/C/lbbroker.c
https://github.com/imatix/zguide/blob/master/examples/C/lbbroker2.c
The solution is synchronous, so any request sent from the client blocks until it gets a response. Personally I would use async for both request and reply for total flexibility, but that solution is way more complex. There are, however, examples in the book called Freelance and Dealer/Router that illustrate async request/reply.
Here's an example for synchronous many-to-many request/reply. You MUST know how ZeroMq enveloping works to fully understand the mechanics of this approach; see example lbbroker1.
Client
Set the client identity with setIdentity(); important for response routing.
Client sends requests for device1, device2, etc, in a loop; if device exists, status messages returned from specific device, otherwise, "no device" returned to client.
Socket client = context.socket(ZMQ.REQ);
client.setIdentity("client1".getBytes());
client.connect("tcp://localhost:5550");
for( int i = 0; i < 5; i++){
client.send("device" + i);
String reply = client.recvStr();
log("Received message: " + reply);
Thread.currentThread().sleep(1000);
}
Device
Device sets id just like client for unique routing.
Device sends device.send("DEVICEREADY") to server to signal online availability.
Device does recvStr() three times to read full envelope from server.
String deviceId = "device1"
Socket device = context.socket(ZMQ.REQ);
device.setIdentity(deviceId.getBytes());
device.connect( "tcp://localhost:5560");
device.send( "DEVICEREADY");
while (!Thread.currentThread().isInterrupted()) {
String clientAddress = device.recvStr();
String empty = device.recvStr();
String clientRequest = device.recvStr();
//create envelope to send reply to same client who made request
device.sendMore(clientAddress);
device.sendMore("");
device.send( "stauts on " + deviceId + " is ok");
}
Server (Router/Router)
A custom proxy using ROUTER sockets; clients connect to frontend ROUTER socket while devices connect to backend router. Server polls on both sockets for messages.
Context context = ZMQ.context(1);
Socket frontend = context.socket(ZMQ.ROUTER);
Socket backend = context.socket(ZMQ.ROUTER);
frontend.bind( "tcp://localhost:5550");
backend.bind( "tcp://localhost:5560");
Poller poller = new Poller(2);
poller.register(frontend, Poller.POLLIN);
poller.register(backend, Poller.POLLIN);
while (!Thread.currentThread().isInterrupted()) {
poller.poll();
//frontend poller
if (poller.pollin(0)) {
String clientId = frontend.recvStr();
String empty = frontend.recvStr(); //empty frame
String deviceId = frontend.recvStr();
//if client is requesting to talk to nonexistent deviceId,
//return message "no device", otherwise, create envelope and send
//request on backend router to device.
if( deviceMap.get( deviceId) == null ){
frontend.sendMore(clientId);
frontend.sendMore("");
frontend.send("no deviceId: " + deviceId);
} else {
//request envelope addressed to specific device
backend.sendMore(deviceId);
backend.sendMore("");
backend.sendMore(clientId);
backend.sendMore("");
backend.send("hello from " + clientId);
}
}
//backend poller
if(poller.pollin(1)){
String deviceId = backend.recvStr();
String empty = backend.recvStr();
String clientId = backend.recvStr();
//device signaling it's ready
//store deviceId in map, don't send a response
if( clientId.equals("DEVICEREADY"))
deviceMap.put(deviceId, deviceId);
else {
//the device is sending a response to a client
//create envelope addressed to client, send on frontend socket
empty = backend.recvStr();
String reply = backend.recvStr();
frontend.sendMore(clientId);
frontend.sendMore("");
frontend.send(reply);
}
}
}
Related
I have a gRPC service simmilar to below
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
I need the client to maintain a long living gRPC connection to the server so that if the server goes down, the client can reconnect and issue SayHello() again.
Based on my understanding there are a few options:
Pass in a statsHandler to grpc.Dial and add retry logic in HandleConn()
Add a new ClientStreaming API that maybe sends a message every few seconds. Check for server side stream close errors and implement retry logic.
Not sure if there is a recommended way for my use case and would appreciate any help.
I am using jpos client (In one of the class of java Spring MVC Program) to connect the ISO8585 based server, however due to some reason server is not able to respond back, due to which my program keeps waiting for the response and results in hanging my program. So what is the proper way to implement connection timeout?
My client program look like this:
public FieldsModal sendFundTransfer(FieldsModal field){
try {
JposLogger logger = new JposLogger(ISO_LOG_LOCATION);
org.jpos.iso.ISOPackager customPackager = new GenericPackager(ISO_PACKAGER);
ISOChannel channel = new PostChannel(ISO_SERVER_IP, Integer.parseInt(ISO_SERVER_PORT), customPackager);// live
logger.jposlogconfig(channel);
channel.connect();
log4j.info("Connection established using PostChannel");
ISOMsg m = new ISOMsg();
m.set(0, field.getMti());
//m.set(2, field.getField2());
m.set(3, field.getField3());
m.set(4, field.getField4());
m.set(11, field.getField11());
m.set(12, field.getField12());
m.set(17, field.getField17());
m.set(24, field.getField24());
m.set(32, field.getField32());
m.set(34, field.getField34());
m.set(41, field.getField41());
m.set(43, field.getField43());
m.set(46, field.getField46());
m.set(49, field.getField49());
m.set(102,field.getField102());
m.set(103,field.getField103());
m.set(123, field.getField123());
m.set(125, field.getField125());
m.set(126, field.getField126());
m.set(127, field.getField127());
m.setPackager(customPackager);
System.out.println(ISOUtil.hexdump(m.pack()));
channel.send(m);
log4j.info("Message has been send");
ISOMsg r = channel.receive();
r.setPackager(customPackager);
System.out.println(ISOUtil.hexdump(r.pack()));
channel.disconnect();
}catch (Exception err) {
System.out.println("sendFundTransfer : " + err);
}
return field;
}
Well the real proper way would be to use Q2. Given you don't need a persistent connection you coud just set a timeout for the channel.
PostChannel channel = new PostChannel(ISO_SERVER_IP, Integer.parseInt(ISO_SERVER_PORT), customPackager);// live
channel.setTimeout(timeout); //timeout in millies.
This way channel will autodisconnect if nothing happens during the time specified by timeout , and your call to receive will throw an exception.
The alternative is using Q2 and a mux (see QMUX, for which you need to run Q2, or ISOMUX which is kind of deprecated).
I am writing some websocket code and I have this so far:
window.onload = function() {
// Get references to elements on the page.
var form = document.getElementById('message-form');
var messageField = document.getElementById('message');
var messagesList = document.getElementById('messages');
var socketStatus = document.getElementById('status');
var closeBtn = document.getElementById('close');
var socket = new WebSocket('ws://echo.websocket.org');
// Show a connected message when the WebSocket is opened.
socket.onopen = function(event) {
socketStatus.innerHTML = 'Connected to: ' + event.currentTarget.url;
socketStatus.className = 'open';
};
// Handle any errors that occur.
socket.onerror = function(error) {
console.log('WebSocket Error: ' + error);
};
form.onsubmit = function(e) {
e.preventDefault();
// Retrieve the message from the textarea.
var message = messageField.value;
// Send the message through the WebSocket.
socket.send(message);
// Add the message to the messages list.
messagesList.innerHTML += '<li class="sent"><span>Sent:</span>' + message +
'</li>';
// Clear out the message field.
messageField.value = '';
return false;
};
socket.onmessage = function(event) {
var message = event.data;
messagesList.innerHTML += '<li class="received"><span>Received:</span>' +
message + '</li>';
};
closeBtn.onclick = function(e) {
e.preventDefault();
// Close the WebSocket.
socket.close();
return false;
};
socket.onclose = function(event) {
socketStatus.innerHTML = 'Disconnected from WebSocket.';
socketStatus.className = 'closed';
};
};
What is this code doing:
var socket = new WebSocket('ws://echo.websocket.org');
What url is that? When I visit there with my browser it does not exist but it seems to be important as I can't simply jus replace that url with random strings. What does it do? Is Websocket an external API?
I'm looking at the network tab and I see this:
Request URL: ws://echo.websocket.org/
Request Method: GET
Status Code: 101 Web Socket Protocol Handshake
conceptually, what is going on? Why do I need to make a request to an external site to use Websockets?
echo.websocket.org provides a webSocket server that lets you make a webSocket connection to it and then it simply echos back to you anything that you send it. It's there primarily for testing and demo purposes.
The code you show looks like a demo/testing app designed to run in a browser web page for a webSocket connection which you can access something similar here: https://websocket.org/echo.html.
A URL starting with ws:// indicates a connection that intends to use the webSocket protocol.
What is this code doing:
var socket = new WebSocket('ws://echo.websocket.org');
It is making a webSocket connection to a webSocket server at echo.websocket.org.
What url is that?
That is a webSocket URL that indicates the intent to use the webSocket protocol to connect and talk to that host. This is not something you type in the URL bar of a browser. It's something that is used by a programming language (such as Javascript in your browser).
Is Websocket an external API?
It's a protocol that specifies a means of connecting, a security scheme, a packet data format, etc... You could say that the http protocol is to the webSocket protocol as the English language is to Japanese. They are different means of communicating. The specification for the webSocket protocol is here: https://www.rfc-editor.org/rfc/rfc6455.
It's also designed to fit well into the http/browser world and to be friendly with infrastructure that was originally designed for http requests. Just searching for "what is websocket" on Google will turn up all sorts of descriptive articles. The Wikipedia page for webSocket provides a pretty good overview.
There is tons written on the web about what the webSocket protocol is and is useful for so I won't repeat that here. You can see a tutorial on webSocket clients here and a tutorial on webSocket servers here.
In a nutshell, it's designed to be a long lasting, continuous connection (supported in all modern browsers) that allows a client to connect to a server and then maintain a continuous connection for (potentially) a long duration. While that connection is open, data can be easily sent both ways over the webSocket. The primary reason people use it is when they want the server to be able to send data directly to the client in a timely fashion without making the client continuously ask the server over and over again if it has any new data. Once a webSocket connection is established, the server can just "push" data to the client at any time.
I'm looking at the network tab and I see this. Conceptually, what is going on?
Request URL: ws://echo.websocket.org/
Request Method: GET Status Code:
101 Web Socket Protocol Handshake
Those are the first steps of establishing a webSocket connection. You can see a more complete description of how that connection works here: How socket.io works. That post talks about socket.io which is another layer built on top of webSocket, but the underlying protocol is webSocket.
Why do I need to make a request to an external site to use Websockets?
A webSocket's purpose is to connect a client to a server (so data can then be sent between them) so it would only be used when connecting to some server somewhere.
How can I block netty to send ACK responese to client in netty 4.x ?
I'm trying to control TCP packet receive speed in netty in order to forward these packet to another server . Netty receive all client packets immediately ,but netty need more time send them out , so client think it finished after sending to netty .
So , I want to know how to block received packets when netty forwarding packets which are received before to another server .
Not sure to really understand your question. So I try to reformulate:
I suppose that your Netty server is acting as a Proxy between clients and another server.
I suppose that what you want to do is to send the ack back to the client only once you really send the forwarded packet to the final server (not necesseraly received by the final server, but at least send by Netty proxy).
If so, then you should use the future of the forwarded packet to respond back with the ack, such as (pseudo code):
channelOrCtxToFinalServer.writeAndFlush(packetToForward).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
// Perform Ack write back
ctxOfClientChannel.writeAndFlush(AckPacket);
}
});
where:
channelOrCtxToFinalServer is one of ChannelHandlerContext or Channel connected to your remote final server from your Netty proxy,
and ctxOfClientChannel is the current ChannelHandlerContext from your Netty handler that receive the packet from the client in public void channelRead(ChannelHandlerContext ctxOfClientChannel, Object packetToForward) method.
EDIT:
For the big file transfer issue, you can have a look at the Proxy example here.
In particular, pay attention on the following:
Using the same logic, pay attention on receiving data one by one from client:
yourServerBootstrap..childOption(ChannelOption.AUTO_READ, false);
// Allow to control one by one the speed of reception of client's packets
In your frontend handler:
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
if (outboundChannel.isActive()) {
outboundChannel.writeAndFlush(msg).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
// was able to flush out data, start to read the next chunk
ctx.channel().read();
} else {
future.channel().close();
}
}
});
}
}
And finally add, using the very same logic, the final ack to your client (ack depending of course on your protocol): (see here and here)
/**
* Closes the specified channel after all queued write requests are flushed.
*/
static void closeOnFlush(Channel ch) {
if (ch.isActive()) {
ch.writeAndFlush(AckPacket).addListener(ChannelFutureListener.CLOSE);
}
}
I'm new to ZeroMQ (and to networking in general), and have a question about using ZeroMQ in a setup where multiple clients connect to a single server. My situation is as follows:
--1 server
--multiple clients
--Clients send messages to server: I've already figured out how to do this part.
--Server sends messages to a specific client: This is the part I'm having trouble with. When certain events get handled on the server, the server will need to send a message to a specific client -- not all clients. In other words, the server will need to be able to choose which client to send a given message to.
Right now, this is my server code:
using (NetMQContext ctx = NetMQContext.Create())
{
using (var server = ctx.CreateResponseSocket())
{
server.Bind(#"tcp://127.0.0.1:5555");
while (true)
{
string fromClientMessage = server.ReceiveString();
Console.WriteLine("From Client: {0}", fromClientMessage);
server.Send("ack"); // There is no overload for the 'Send'
method that takes an IP address as an argument!
}
}
}
I have a feeling that the problem is that my design is wrong, and that the ResponseSocket type isn't meant to be used in the way that I want to use it. Since I'm new to this, any advice is very much appreciated!
when using the Response socket you always replying to the client that sent you the message. So the Request-Response socket types together are just simple request response.
To more complicated scenarios you probably want to use Dealer-Router.
With router the first frame of each message is the routing id (the identity of the client that sent you the message)
so your example with router will look like:
using (NetMQContext ctx = NetMQContext.Create())
{
using (var server = ctx.CreateRouterSocket())
{
server.Bind(#"tcp://127.0.0.1:5555");
while (true)
{
byte[] routingId = server.Receive();
string fromClientMessage = server.ReceiveString();
Console.WriteLine("From Client: {0}", fromClientMessage);
server.SendMore(routingId).Send("ack");
}
}
}
I also suggest to read the zeromq guide, it will probably answer most of your questions.