From c3e9fbe538c8dc1eb3d287bc2ff6b046e52dcb2d Mon Sep 17 00:00:00 2001 From: Michael Lumish Date: Wed, 13 Mar 2024 14:53:43 -0700 Subject: [PATCH] Add keepalive example --- examples/keepalive/README.md | 16 ++++++++ examples/keepalive/client.js | 61 +++++++++++++++++++++++++++++++ examples/keepalive/server.js | 71 ++++++++++++++++++++++++++++++++++++ examples/package.json | 4 +- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 examples/keepalive/README.md create mode 100644 examples/keepalive/client.js create mode 100644 examples/keepalive/server.js diff --git a/examples/keepalive/README.md b/examples/keepalive/README.md new file mode 100644 index 000000000..8b859b1f0 --- /dev/null +++ b/examples/keepalive/README.md @@ -0,0 +1,16 @@ +# Keepalive + +This example illustrates how to set up client-side keepalive pings and +server-side keepalive pings and connection age and idleness settings. + +## Start the server + +``` +node server.js +``` + +## Start the client + +``` +GRPC_TRACE=transport,keepalive GRPC_VERBOSITY=DEBUG client.js +``` diff --git a/examples/keepalive/client.js b/examples/keepalive/client.js new file mode 100644 index 000000000..4da7756a8 --- /dev/null +++ b/examples/keepalive/client.js @@ -0,0 +1,61 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +const grpc = require('@grpc/grpc-js'); +const protoLoader = require('@grpc/proto-loader'); +const parseArgs = require('minimist'); + +const PROTO_PATH = __dirname + '/../protos/echo.proto'; + +const packageDefinition = protoLoader.loadSync( + PROTO_PATH, + {keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true + }); +const echoProto = grpc.loadPackageDefinition(packageDefinition).grpc.examples.echo; + +const keepaliveOptions = { + // Ping the client every 10 seconds to ensure the connection is still active + 'grpc.keepalive_time_ms': 10_000, + // Wait 1 second for the ping ack before assuming the connection is dead + 'grpc.keepalive_timeout_ms': 1_000, + // send pings even without active streams + 'grpc.keepalive_permit_without_calls': 1 +} + +function main() { + let argv = parseArgs(process.argv.slice(2), { + string: 'target', + default: {target: 'localhost:50052'} + }); + const client = new echoProto.Echo(argv.target, grpc.credentials.createInsecure(), keepaliveOptions); + client.unaryEcho({message: 'keepalive demo'}, (error, value) => { + if (error) { + console.log(`Unexpected error from UnaryEcho: ${error}`); + return; + } + console.log(`RPC response: ${JSON.stringify(value)}`); + }); + // Keep process alive forever; run with GRPC_TRACE=transport,keepalive GRPC_VERBOSITY=DEBUG to observe ping frames and GOAWAYs due to idleness. + setInterval(() => {}, 1000); +} + +main(); diff --git a/examples/keepalive/server.js b/examples/keepalive/server.js new file mode 100644 index 000000000..24a7ee778 --- /dev/null +++ b/examples/keepalive/server.js @@ -0,0 +1,71 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +const grpc = require('@grpc/grpc-js'); +const protoLoader = require('@grpc/proto-loader'); +const parseArgs = require('minimist'); + +const PROTO_PATH = __dirname + '/../protos/echo.proto'; + +const packageDefinition = protoLoader.loadSync( + PROTO_PATH, + {keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true + }); +const echoProto = grpc.loadPackageDefinition(packageDefinition).grpc.examples.echo; + +const keepaliveOptions = { + // If a client is idle for 15 seconds, send a GOAWAY + 'grpc.max_connection_idle_ms': 15_000, + // If any connection is alive for more than 30 seconds, send a GOAWAY + 'grpc.max_connection_age_ms': 30_000, + // Allow 5 seconds for pending RPCs to complete before forcibly closing connections + 'grpc.max_connection_age_grace_ms': 5_000, + // Ping the client every 5 seconds to ensure the connection is still active + 'grpc.keepalive_time_ms': 5_000, + // Wait 1 second for the ping ack before assuming the connection is dead + 'grpc.keepalive_timeout_ms': 1_000 +} + +function unaryEcho(call, callback) { + callback(null, call.request); +} + +const serviceImplementation = { + unaryEcho +}; + +function main() { + const argv = parseArgs(process.argv.slice(2), { + string: 'port', + default: {port: '50052'} + }); + const server = new grpc.Server(keepaliveOptions); + server.addService(echoProto.Echo.service, serviceImplementation); + server.bindAsync(`0.0.0.0:${argv.port}`, grpc.ServerCredentials.createInsecure(), (err, port) => { + if (err != null) { + return console.error(err); + } + console.log(`gRPC listening on ${port}`) + }); +} + +main(); diff --git a/examples/package.json b/examples/package.json index 6857aa5d9..6e717dadb 100644 --- a/examples/package.json +++ b/examples/package.json @@ -5,8 +5,8 @@ "@grpc/proto-loader": "^0.6.0", "async": "^1.5.2", "google-protobuf": "^3.0.0", - "@grpc/grpc-js": "^1.8.0", - "@grpc/grpc-js-xds": "^1.8.0", + "@grpc/grpc-js": "^1.10.2", + "@grpc/grpc-js-xds": "^1.10.0", "@grpc/reflection": "^1.0.0", "lodash": "^4.6.1", "minimist": "^1.2.0"