-
Notifications
You must be signed in to change notification settings - Fork 578
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
Is it possible to pass a socket handle directly to uWS? #766
Comments
That's what it looked like to me too, just wanted to check. Thanks for the quick reply @e3dio! |
It's still a good feature request |
I'm closing this, it's not a bad idea but it is a too complex idea for the gain - just let sockets fall wherever they fall and use the pub/sub or direct connections based on some URL query value or something like that. Point is - you can achieve pretty good results without this kind of low level hackery. But yes it is a good idea. But no, I have no time to implement and maintain this edge case (and it kind of goes against the simple nature of uWS) |
Wondering if you'll reconsider this. My use-case here is graceful deployments for an application with long-lived websockets. I help maintain a game server that uses websockets to communicate. Since interrupting game sessions is very painful to the players, deployments are done by launching a new instance of the server and sending new connections there, which allows the old instance to remain running while existing players finish their games. It's not perfect, since if players get disconnected they can't reconnect. What I'm looking for is the ability to route connections based on ... anything I can control. The hostname (via SNI), a header, a path or query component. This would allow me to tell the game which address to connect to and route it to the same process every time, as well as change the "active" server to execute deployments, without needing proxy infrastructure. I've tried various solutions.
There are still solutions available, such as standing up another server to act as a reverse proxy, but the game is too niche to justify the extra operational overhead and it's undesireable to double the infrastructure cost when a software solution is available. I'm considering just switching back to I understand the perspective of "we have defined the scope of our library and are holding to it", but it's a bit of a shame whenever a great library exists and I can't use it to meet my goals :( |
But we need to make a mapping of Then the easiest option is to make a cluster (as in your example) and the main process will store FD and mapping sessions, child workers of this process can be rebooted, when the worker is loaded it will take open FD and mapping sessions from the main parent process |
Have you looked at the very latest release which changes approach for worker threads? See https://github.com/uNetworking/uWebSockets.js/releases/tag/v20.48.0 This release adds App.getDescriptor() and App.addChildAppDescriptor What you need, if I understand, is exactly this but you also need App.removeChildAppDescriptor so that you can unregister a child (worker) instance when swapping. Alternatively we just expose App.adoptSocket(fd) and you do the distribution yourself (not as efficient). With adoptSocket(fd) you should be able to do whatever you want, really. Using the Node.js net.Server as listener. |
Oh, thanks, I didn't know about these methods. Instead of server nodes, to accept sockets, it's better to use Thanks! |
Adding Exposing It remains unclear, though, whether it's possible to gain any information about the socket before distributing it to the worker. If I were to use SNI for example, I still have to accept the TCP connection and negotiate the TLS handshake to know which host the connection was intended for. So the socket would need to be handed over once the connection is established. Using HTTP data (path, query string, header...), the initial request would have to be parsed before selecting a worker, handing the socket over before handling the websocket upgrade. |
I can add both adoptSocket(fd) and removeChildAppDescriptor. SNI sounds like a nightmare. |
If you let latest binaries build it should have App.adoptSocket(fd) that should make it work for you |
It's strange, in FD you store context, usually context specific to all applications and can be expanded as you like, therefore mapping FD and context is a more scalable solution |
Just to be clear, SNI is not acutally my preferred solution, it just allows routing "earlier" in the request lifecycle. My preferred solution would be to hand off before websocket upgrade, but I'm not sure how viable that one is. They both sound like they could be relatively complex since they're handing off a live socket rather than a socket that hasn't been accepted yet :\ |
adoptSocket should be called right after accepting the socket. So immediately you get it, just hand it over to whatever process you want. |
Took me a while to get around to digging into this in detail; after managing to reproduce the build process and modifying the typedefs, (I may not be interpreting this correctly, unfamiliar with the native module API) However, I can't find any way (publicly exposed or otherwise) to actually get the underlying file descriptor for the socket. The thing that gets passed between Node processes is an instance of Socket (I haven't tried with TLS yet, this was just a basic attempt). If I'm understanding the expectations correctly, there doesn't appear to be any way to acquire the argument that the library expects, so it can never work :\ I'm not sure what would be required to get at the actual underlying fd from the Socket instance, though presumably it's achievable on the C++ side somehow(?) |
I don't follow. What do you mean by "the Socket instance". |
In Node.js, a socket is an instance of a Javascript "class": https://nodejs.org/api/net.html#class-netsocket This is what authors are working with in code, not file descriptors for sockets at the OS level. This object is also what gets transferred by the IPC mechanism. Obviously it must do the kernel magic to hand over the FD too, but inspecting the contents of the Socket, I do not see a direct reference to the FD, so I'm guessing it's held on the C++ side of Node's stdlib. |
This is kind of why adoptSocket is a bad idea. You can get the FD from net.Socket (at least you could 8 years ago last time I checked). But then you need to dup2 it and destroy the Node.js counterpart. This is what early versions of uWS did when there was no HTTP server in uWS. So if you think this is messy, I would go with the childApp approach instead. |
I cannot see any way to get the FD from the net.Socket period. I inspected the I was informing you so you can either find a way to support it or revert the change, since the change as-is seems to not be usable |
uWS has no compatibility with Node.js types other than FD. adoptSocket obviously will only work if you have an FD. You have, in last commit, addChildAppDescriptor and removeChildAppDescriptor. Those are all you need to migrate players |
You can get it with socket._handle.fd
// or for TLS sockets.
socket._parent._handle.fd |
@uasan thanks. Don't know why it didn't seem to show up when I logged the data. Maybe non-enumerable property and it was nested inside. Doesn't seem to work anyway though. I get a connection reset by peer instead of a stalled socket when passing that value to adoptSocket. So I guess exposing this method is not useful* * at least for porting a socket from one process to another |
adoptSocket is the base for addChildApp, removeChildApp. So it's known to work. When you close your Node.js socket it will close the FD so you need to dup2 it. But like said, this is unnecessarily complex path to go. Latest commit as of today should have both addChildAppDescriptor, removeChildAppDescriptor working so using that approach is way simpler. |
By "doesn't work", I meant only that passing the FD obtained from the Socket instance does not succeed. I assume that the underlying C++ code in uWS works, just the interop between it and Node doesn't work in this way. addChildAppDescriptor solves half of what I want to do, but not the other half (the ability to be selective in which app receives a connection), which is why I was exploring if adoptSocket could be made to work. |
Yes addChildAppDescriptor is half. The other half is removeChildAppDescriptor which was added just now in latest release today. Whatever child apps you have, will be round robin used for new connections. So by changing this set with the 2 functions, you have full control of how new connections migrate. |
That is, the concept is simple, we create workers in each worker an instance of a child uWS application, when we want to update the code, we delete the child application (call removeChildAppDescriptor), turn off the worker and start a new worker with new code that creates a new child application (call addChildAppDescriptor), did I understand correctly? Then the question is, how will the new application receive sockets from the old application and how will these sockets correspond to their user sessions? |
Existing sockets will not move. Only newly accepted ones will take the new
worker. Once accepted a socket lives with one App.
Den tis 24 sep. 2024 15:26S.A.N ***@***.***> skrev:
… That is, the concept is simple, we create workers in each worker an
instance of a child uWS application, when we want to update the code, we
delete the child application (call removeChildAppDescriptor), turn off the
worker and start a new worker with new code that creates a new child
application (call addChildAppDescriptor), did I understand correctly?
Then the question is, how will the new application receive sockets from
the old application and how will these sockets correspond to their user
sessions?
—
Reply to this email directly, view it on GitHub
<#766 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A2NMOMPLAHLMSMO45RKWXRLZYFSATAVCNFSM6AAAAABNHNUKSKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNZRGI3TGNBUGA>
.
You are receiving this because you modified the open/close state.Message
ID: ***@***.***>
|
Okay, these are good functions for multithreading, but to migrate sockets you still need adoptSocket. |
You can't migrate existing sockets. Once a socket has an App it stays there
forever.
Den tis 24 sep. 2024 19:20S.A.N ***@***.***> skrev:
… Okay, these are good functions for multithreading, but to migrate sockets
you still need adoptSocket.
—
Reply to this email directly, view it on GitHub
<#766 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A2NMOMN2PFECBT2ZPUMLCO3ZYGNNTAVCNFSM6AAAAABNHNUKSKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNZRHA3TQOBVGI>
.
You are receiving this because you modified the open/close state.Message
ID: ***@***.***>
|
eg, something equivalent to handleUpgrade.
The reason this is useful is that, combined with passing socket handles across processes, you can route all sockets based on the same topic to the same process in a node cluster:
The text was updated successfully, but these errors were encountered: