For a server that is behind a firewall and cannot accept incoming connections but can dial outgoing connections to a client that is not blocked by a firewall, it is useful to be able to start a gRPC server and provide it with an existing net.Conn rather than going through the listen/accept process. Same on the other side, the client should be able to just use an existing net.Conn
The server behind the firewall starts life as a client and the client outside the firewall starts as a server but once connected they need to switch roles.
Perhaps this is already possible today, if so, could you please point me in the right direction?
You can do it in a somewhat-hacky manner by using grpc.WithDialer to return an arbitrary net.Conn.
That could work on the client side, but what about the server side? I don't see any useful ServerOptions or another way to bypass the listen/accept.
grpc.NewServer().Serve takes a net.Listener. You can write a trivial implementation of that that'll yield a net.Conn.
Ah good point, just realized it's an interface. Thanks!
Any other solutions for the problem I'm trying to solve? Both sides need to act as both server and client at the same time. Ideally I'd like to use a single connection (initiated from the side that can dial out) and do bi-directional gRPC on it but I'm not sure it's possible even with streams. Two connections (both started from the side that can dial out) is the next best thing if I can get it to work with the above hack-ish way.
gRPC supports bidirectional streams, but isn't really for bidirectional connections. One side is meant to dial the other side. It sounds like the things I pointed you at satisfy your requirements, though. If you want extra things on top of that, you could have an RPC method that is merely a byte transport, and do a connection over that, but there'd be a lot of overhead.
On Wed, Jan 6, 2016 at 9:32 PM, bits01 [email protected] wrote:
Ah good point, just realized it's an interface.
Any other solutions for the problem I'm trying to solve? Both sides need
to act as both server and client at the same time. Ideally I'd like to use
a single connection (initiated from the side that can dial out) and do
bi-directional gRPC on it but I'm not sure it's possible even with streams.
Two connections (both started from the side that can dial out) is the next
best thing if I can get it to work with the above hack-ish way.Note that RPC basically falls into client-server communication model
instead of peer-peer model. I think most of your requirements can be
satisfied using a bi-directional streaming rpc by tuning your wire protocol
a bit.—
Reply to this email directly or view it on GitHub
https://github.com/grpc/grpc-go/issues/484#issuecomment-169557041.
Any good examples you could point to, please?
Your server behind the firewall dials the client and initiates a
bi-directional streaming rpc in which both sides can send arbitrary numbers
of messages to their peer.
For an example of bi-directional streaming, please refer to routechat at
https://github.com/grpc/grpc-go/blob/master/examples/route_guide/routeguide/route_guide.proto#L62
.
On Thu, Jan 7, 2016 at 11:20 AM, bits01 [email protected] wrote:
Any good examples you could point to, please?
—
Reply to this email directly or view it on GitHub
https://github.com/grpc/grpc-go/issues/484#issuecomment-169779217.
I see. But then there's only one message type for the stream so you'd have to encode multiple message types into a single type, you lose the RPC-ness, dispatching to multiple functions that can take various inputs and return various values.
On Thu, Jan 7, 2016 at 11:53 AM, bits01 [email protected] wrote:
I see. But then there's only one message type for the stream so you'd have
to encode multiple message types into a single type, you lose the RPC-ness,
dispatching to multiple functions that can take various inputs and return
various values.Even though I stated one streaming rpc in the previous email, you can feel
free to extend it to multiple ones (e.g., 1 stream <-> (request type, reply
type)).—
Reply to this email directly or view it on GitHub
https://github.com/grpc/grpc-go/issues/484#issuecomment-169788013.
Can the server initiate a new stream on an existing connection? From looking at the examples it seems the client makes a call potentially with multiple streams and then can't make another call until that call is completed. I'm not sure I understand the multiple streams part. In my scenario, the server needs to tell the client (behind the firewall) to do a various things, e.g. doX(x,y) retdata1, doY(z) retdata2, etc. all inputs and returns are of different types. Not sure how the server can make such calls or start streams with the correct data type. Are there any examples?
This is turning more into a discussion thread rather than a real issue. Is there a forum or mailing list specific to Go gRPC where we can discuss such things?
There is https://gophers.slack.com/messages/grpc/ but it doesn't seem to be very active.
[email protected] is the email address you want.
I think you need to refactor your service model somehow to fit into the client-server model of rpc. For example, your client (behind the firewall) can setup the connection and initiate a streaming rpc. Then your server can response with x in your example and eventually your client sends retdata1 to the server after processing it (i.e., doX).
Is there any new information on this? @bits01 - did you find a satisfactory solution?
The Client/Server duality concept would be very useful - especially in IOT applications.
For example, imagine a IOT Thermostat that we can control remotely through a cloud service. Its plugged in and connected to a home network, thus behind a NAT and Firewall.
1: Thermostat (client) dials the cloud service (server) via gRPC to create a channel. This can't go the other way around as the cloud service has no ability to dial the thermostat behind the NAT+FIREWALL.
Great - so far so good. Thermostat=Client, Cloud Service=Server
2: Now the cloud service needs to send the Thermostat a command to change its temperature. I would love to have some RPC services registered on the Thermostat such as
rpm ChangeTemperature (ChangeTemperatureAsk) returns (ChangeTemperatureResult) {}rpm EnableAwayMode (EnableAwayModeAsk) returns (EnableAwayModeResult) {}but now the Thermostat=Server and the Cloud Service=Client... gRPC does not handle this without some implementation burden on both client and server sides like @dsymonds suggested.
The only solution to avoid running a client and server on both ends and instead created a bidirectional stream:
rpc Link (stream UpLink) returns (stream DownLink) {}where the UpLink and DownLink message just basically have a string in which I parse on the client: {changeTemp: 55}. Pretty bad code smell in gRPC.
@codeitcody My use case was exactly like yours. I ended up not using gRPC and instead I just did straight up net/rpc with https://github.com/hashicorp/yamux for the multiplexed streams. Worked out quite well for me, less overhead in not having to maintain the proto files, I could share the Go structs both on the client and server directly.
But I do agree, it would be nice for gRPC to support bidirectional RPC where either side can initiate the connection.
@codeitcody @bits01 I am facing the same issue, mainly with nodes behind a firewall that can connect to the server but I would like to use that same connection to send back data.
I am thinking about using zeromq - what else have you found to solve this?
@nbari see my comment above yours. Using https://github.com/hashicorp/yamux you could probably do the same thing and use gRPC instead of net/rpc, but I have not tried it.
@nbari I ended up just creating my own channel using HTTP/2 based transport. I did a pub/sub thing instead of rpc. As @bits01 suggested I also think you could coerce gRPC into doing this with some work. :/
I have gRPC servers behind firewalls that I'd like to subscribe to events on from a publicly routable gRPC client. I'd like to do this as simply and lightweight as I know possible by having the server make a TLS TCP connection to the publicly routable client listener, and then start gRPC communication on that established net.Conn.
Dialing from the client with a custom grpc.WithDialer() that returns the provided net.Conn seems pretty straight forward. I don't see an easy way to use a provided net.Conn for the server though.
Could someone elaborate on how to get grpc.Server to use a provided net.Conn? Maybe provide an example? Sorry if I missed something obvious, and thanks.
@dustin-decker
For the question on using net.Conn in a grpc.Server:
server.Serve() takes a listener as input, you can make a listener whose Accept() returns the net.Conn and then blocks forever.
Thanks for the tip @menghanl.
I've uploaded some example client/server code that will do what I need here: https://github.com/dustin-decker/grpc-firewall-bypass
client initiates a TCP connection to server with client's gRPC server listening on the connection, and then then the server's gRPC client connects to the client's gRPC server over the incoming TCP connection.
I wish it wasn't required to do like that, but there it is.
Additionally I found a different and more complicated route someone took of sending binary messages over a websocket: https://github.com/glerchundi/grpc-boomerang
Is it possible to reuse the same connection with C++ backend?
Most helpful comment
@codeitcody My use case was exactly like yours. I ended up not using gRPC and instead I just did straight up net/rpc with https://github.com/hashicorp/yamux for the multiplexed streams. Worked out quite well for me, less overhead in not having to maintain the proto files, I could share the Go structs both on the client and server directly.
But I do agree, it would be nice for gRPC to support bidirectional RPC where either side can initiate the connection.