{"id":28717248,"url":"https://github.com/edvardchen/grpclb","last_synced_at":"2025-06-15T03:13:58.250Z","repository":{"id":46926031,"uuid":"198962199","full_name":"edvardchen/grpclb","owner":"edvardchen","description":"grpc client-side load balancer for Node.js","archived":false,"fork":false,"pushed_at":"2023-01-04T05:51:29.000Z","size":1185,"stargazers_count":5,"open_issues_count":14,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-25T17:47:23.199Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edvardchen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-26T06:45:48.000Z","updated_at":"2023-05-25T05:29:13.000Z","dependencies_parsed_at":"2023-02-01T23:46:26.152Z","dependency_job_id":null,"html_url":"https://github.com/edvardchen/grpclb","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/edvardchen/grpclb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvardchen%2Fgrpclb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvardchen%2Fgrpclb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvardchen%2Fgrpclb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvardchen%2Fgrpclb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edvardchen","download_url":"https://codeload.github.com/edvardchen/grpclb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvardchen%2Fgrpclb/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259914929,"owners_count":22931334,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-15T03:13:52.379Z","updated_at":"2025-06-15T03:13:58.240Z","avatar_url":"https://github.com/edvardchen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# grpclb\n\ngrpc load balancer integrated with [etcd] for Node.js\n\n## Install\n\n```bash\nnpm i grpclb grpc\n```\n\n\u003e `grpclb` lists `grpc` as its **`peerDependency`** not `dependency` because [here](#Notes)\n\n## Server side\n\n```typescript\nimport { register } from 'grpclb';\n\nconst revoke = await register({\n  server, // your grpc server instance\n  etcdKV: { key, value }, // leave you to decide how to serialize service name, host, port into KV\n  ttl: 10, // default 10 in seconds\n  etcdHosts: hosts, // etcd hosts, or you can set as env var ETCD_HOSTS\n});\n\n// then you can revoke\n// by direct call the revoke handler\nrevoke();\n\n// or by shutting down the grpc server\nserver.tryShutdown(() =\u003e {});\n```\n\n## Client side\n\n- client-side load balancing with `round-robin strategy`\n\nWe can't register custom service resolver util [the `C` library exposes the api](https://github.com/grpc/grpc-node/issues/719).\n\nSo we can implement client-side load-balancing on the other way: [javascript Proxy](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Proxy)\n\n```typescript\nimport { createGrpcProxy } from 'grpclb';\nimport { loadSync } from '@grpc/proto-loader';\nimport { loadPackageDefinition } from 'grpc';\n\n// load .proto file\nconst packageDefinition = loadSync(PROTO_PATH);\n// initialize into javascript object\nconst pkgDef = loadPackageDefinition(packageDefinition);\n\nconst proxy = await createGrpcProxy({\n  etcdHosts: hosts, // etcd hosts, or you can set as env var ETCD_HOSTS\n  target: pkgDef.helloworld, // your gRPC object, MUST be the package definition object\n  parseKV, // how to extract service name, host, port from etcd key and value\n});\n\n// Every time you access the service object, you get the new servant address.\nconst servant = proxy.Greeter;\n\n// The service was already initialized and\n// you can just call the service method to send request\nservant.sayHello({ name }, (error, response) =\u003e {});\n```\n\n### For **static** generated grpc javascript codes\n\nThe `Proxy` way is not convenient. So `grpclb` also provides another api to do the load balancing:\n\n```typescript\nimport { createClientPool } from 'grpclb';\nimport { GreeterClient } from 'helloworld/static_codegen/helloworld_grpc_pb';\nimport { HelloRequest } from 'helloworld/static_codegen/helloworld_pb';\n\nconst pool = await createClientPool({\n  Client: GreeterClient, // your client service\n  parseKV, // how to extract service name, host, port from etcd key and value\n  etcdHosts: hosts, // etcd hosts, or you can set as env var ETCD_HOSTS\n});\n\n// Every time you access the service object, you get the new servant address.\nconst servant = pool.get();\n\n// The service was already initialized and\n// you can just call the service method to send request\nservant.sayHello(new HelloRequest(), (error, response) =\u003e {});\n```\n\n## Notes\n\n### `grpc` as peerDependency, not dependency\n\nImage you have two copies of `grpc`, it would look like:\n\n```bash\n├── node_modules\n│   ├── grpclb\n│   │   └── node_modules\n│   │       └── grpc\n│   └── grpc\n└── src\n    └── static_codegen\n        ├── helloworld_grpc_pb.js\n        ├── helloworld_pb.d.ts\n        └── helloworld_pb.js\n```\n\n- `require('grpc')` in src directory, no matter dynamic generated gRPC javascript code or static generated, would resolve to `node_modules/grpc`\n- `require('grpc')` in `grpclb` package would resolve to `node_modules/grpclb/node_modules/grpc`\n\nThen initialization would throw error\n`TypeError: Channel's second argument must be a ChannelCredentials`. See details for [this issue](https://github.com/grpc/grpc/issues/10786)\n\nList `grpc` as peerDependency can avoid this situation.\n\n[etcd]: https://github.com/etcd-io/etcd\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedvardchen%2Fgrpclb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedvardchen%2Fgrpclb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedvardchen%2Fgrpclb/lists"}