Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client/server architecture #652

Closed
alexnixon opened this issue Mar 29, 2019 · 5 comments
Closed

Client/server architecture #652

alexnixon opened this issue Mar 29, 2019 · 5 comments
Labels

Comments

@alexnixon
Copy link

Hi there,

Apologies if the answer to this is out there already. I can see several related issues but I've read them over and am still not sure how to handle this.

I'd like to create a regular client<->server setup with Aeron. Clients should be able to send to/receive from the server, not be aware of each other, and this should work over both UDP and IPC.

I'm struggling to figure out exactly how to manage subscriptions and publications to make this happen. What I have now is akin to (for IPC, but could tweak the channel URIs for UDP):

String serverUri = "aeron:ipc";
int serverStreamId = 1;

Server

// Create a subscription to receive msgs. Could be UDP like "aeron:udp?endpoint=localhost:4444".
aeron.addSubscription(serverUri, serverStreamId);

// Whenever a client spins up it will immediately send a message to the server with a
// [URI, streamId] pair which it's listening on. When the server receives that, it creates
// a publication to the client like this:
aeron.addExclusivePublication(clientUri, clientStreamId)

Client

// Hard-code our *incoming* URI and stream. Could be UDP like "aeron:udp?endpoint=localhost:4445".
String clientUri = "aeron:ipc";
int clientStreamId = 2;

// Publish to the server's URI and stream
Publication pub = aeron.addExclusivePublication(serverUri, serverStreamId);

// Start listening on our URI and stream
aeron.addSubscription(clientUri, clientStreamId);

// Tell the server about our URI and stream so it can respond to us
send(pub, clientUri, clientStreamId);

However, I have two clients running on the same machine, both using IPC, and they see each other's messages because they're using the same stream ID.

My best guess of a solution is for each client to somehow ask Aeron for a "free" stream ID, so they can listen without interference. Is that sensible and something which Aeron supports, or if not could you suggest an alternative?

Thanks in advance - I've spent some time understanding Aeron over the past couple of weeks and it looks like a absolutely fantastic piece of software. Excited to get this ironed out and start using it in anger!

@tmontgomery
Copy link
Contributor

tmontgomery commented Mar 29, 2019

You have the basics down. But one slight addition is needed. If you look at both the Archive and Cluster, the clients have a control session Id that they manage. It can be sent back in the "connect" message. And each response (from server back to client) and subsequent requests (from client to server after the connect) can use it for filtering. This is important for IPC in that you can simply ignore any messages that don't include the clients expected control session Id. But it also serves as a means for the server to know which client is sending a request.

@alexnixon
Copy link
Author

Ah I see, thanks.

Using the comment here as a breadcrumb, it looks like the archiver might use a session ID as a control ID.

In which case I think I can do the same, i.e. use the sessionId() of the publication I just created which I guess is guaranteed not to clash with others sharing the same media driver.

Would you be interested in a PR to add basic RPC as a sample? I can't guarantee I'll find time to put one together but I would have found one useful when starting out.

@tmontgomery
Copy link
Contributor

That is the control session Id.... not the Publication session Id. Yes, somewhat confusing. Naming is tough. But, those are two different Ids. The control session Id is Archive specific and assigned when an archive client connects. The Publication session Id is assigned by the media driver for the Publication specifically. While you could use that session Id, I would suggest instead grabbing a new Id via Aeron.nextCorrelationId when a new client does a connect on the server side. i.e. have the server assign it. Thus it can be unique.

In terms of a PR, we do have some examples in the archive and cluster. However, a generalized and concise mechanism that others could use and integrate or could use as an example would be pretty useful, yes. :)

@alexnixon
Copy link
Author

Hmm but we can't just rely on the server to generate the unique ID because the purpose of the ID is to allow the server -> client messages to be sent, i.e. at the time of its creation there is no server -> client channel to allow the server-generated ID to be sent back to the client.

Looking more closely at the Archiver, I think that's solved with a client-generated correlationId, so I think the overall setup/flow is:

  1. There is a single, statically-configured publication from the server (which all clients subscribe to), and the server has a single, statically-configured subscription (which all clients publish to, though the server can distinguish these as different Images).
  2. A client starts up and wants to establish a bidirectional client<-> server channel. Client->server is easy, because we have Images. Server->client needs some work, because all IPC clients "see" the same data sent down the subscription. So:
  3. The client generates an ID with Aeron.nextCorrelationId. This is unique per media driver so won't clash if there are multiple IPC clients
  4. The client sends this ID to the server in an "establish connection" request and starts listening for responses which contain this correlation ID, ignoring everything else.
  5. The server receives the request, notes this correlation ID, and pairs it with a uniquely generated ID called the controlSessionId (in ArchiveConductor.java).
  6. The server sends this controlSessionId repeating the correlation ID back to the client.
  7. The client receives that message, notes the controlSessionId, and from now on ignores any messages which don't have that control session ID.

@tmontgomery
Copy link
Contributor

Yes. Pretty much spot on.

The cluster and archive use a correlationId on each request/response. So, matching things up is easy. This allows each individual response to be matched with its request. Including the connect.

In addition, most of the requests need to have a corresponding response. So, the correlationId does duty on that aspect also. If you notice how the pollForResponse on the Archive API works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants