node.js multiplexing client channels, [is this hack safe?]

473 views
Skip to first unread message

Francesco Lazzarino

unread,
Feb 14, 2017, 10:36:15 PM2/14/17
to grpc.io
Hi,

The current node.js module doesn't allow one to pass a Channel object to build a client, but it does allow one to access a channel via grpc.getClientChannel(…) and .$channel on a client object.

In lieu of an exposed interface to pass a connection or channel, I tried out the following hack to allow two clients to share the same channel.

function combineChannels(aClient, bClient) {
    var aChan = grpc.getClientChannel(aClient);
    var bChan = grpc.getClientChannel(bClient);
    if (aChan.getTarget() != bChan.getTarget()) {
        throw "different targets: " + " (a) " + aChan.getTarget() + "; (b) " + bChan.getTarget();
    }

    if (aChan.getConnectivityState() != 0) {
        throw "bad connectivity state (a): " + aChan.getConnectivityState();
    }

    if (bChan.getConnectivityState() != 0) {
        throw "bad connectivity state (b): " + bChan.getConnectivityState();
    }    

    bChan.close();
    bClient.$channel = aChan;
}

Observing with nettop (macOS) shows only 1 connection when hack is in use, and 1 per client otherwise. It seems to work as expected, but is any danger in doing so? Is there a better way to accomplish this in node.js?


FWIW my goal is to have a healthcheck client keep the TCP socket alive in case a LB or FW wants to close idle-long-running connections.

Thanks in advance,

– Franco

Michael Lumish

unread,
Feb 16, 2017, 9:25:12 PM2/16/17
to Francesco Lazzarino, grpc.io
You should have no problems with this approach, but it also shouldn't be necessary. gRPC already uses HTTP2 pings to keep channels alive while they're otherwise idle. If you're still seeing disconnects, there are a couple of channel options you can set (they go into an object in the third argument to the Client constructor). The list of them is defined here: https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h#L148. Specifically, modifying "grpc.http2.min_time_between_pings_ms" and "grpc.http2.max_pings_without_data".

In addition, gRPC automatically reconnects dropped TCP connections, so if your channel is idle with no ongoing calls, you shouldn't even notice connection drops.

--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/a5f6c9ea-48e4-4d68-a5ab-322c55c15f21%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Francesco Lazzarino

unread,
Feb 17, 2017, 2:08:41 PM2/17/17
to grpc.io, flazz...@gmail.com
Michael,

Thanks for the reply, I tried passing those options thusly:

var sClient = new services.SClient('localhost:50051', grpc.credentials.createInsecure(), {
  "grpc.http2.min_time_between_pings_ms": 100,
  "grpc.http2.max_pings_without_data": 0
});

I connected to the server for 10 seconds, I observed (via wireshark http2) DATA frames from an rpc call, but no PING frames. I had expected to see about 200 PING frames: 10 seconds * 10 per second * 2 (req and rep)

To verify the ability to observe PING frames I ran the following go code against the same server (nodejs + gRPC)

package main

import (
"context"
"fmt"
"log"
"net"

)

func main() {
fmt.Println("PING")
tp := http2.Transport{}

log.Println("dialing server")
conn0, err := net.Dial("tcp", "localhost:50051")
if err != nil {
log.Fatal(err)
}

conn, err := tp.NewClientConn(conn0)
if err != nil {
log.Fatal(err)
}

for i := 0; i < 2; i++ {
log.Println("PING", i)
if err := conn.Ping(context.TODO()); err != nil {
log.Fatal(err)
}
}
}

I observed 4 PING frames as expected.

Am I doing something wrong?

Michael Lumish

unread,
Feb 17, 2017, 2:19:12 PM2/17/17
to Francesco Lazzarino, grpc.io
I believe that we use PING frames to keep connections alive while they are otherwise idle. I wouldn't expect to need any PINGs when other data is moving across the connection. Have you tried observing an otherwise idle channel for PING frames?

Francesco Lazzarino

unread,
Feb 17, 2017, 2:26:07 PM2/17/17
to grpc.io, flazz...@gmail.com


On Friday, February 17, 2017 at 1:19:12 PM UTC-6, Michael Lumish wrote:
I believe that we use PING frames to keep connections alive while they are otherwise idle. I wouldn't expect to need any PINGs when other data is moving across the connection. Have you tried observing an otherwise idle channel for PING frames?


That makes sense. In my experiment I send one unary-unary call to initialize the connection, then waited 10 seconds with no activity before closing. I was hoping to see PINGS in that duration between the call and the close.

Under what conditions can a connection become idle? Can it be forced?


Reply all
Reply to author
Forward
0 new messages