Skip to Content
🎉 gRPCity 3.0 is released. Read more →
文档开始使用

Get Started

本页通过四步走完一个完整的请求/响应闭环:项目初始化、loader、服务端、客户端。对 gRPCprotobuf 有基本了解会更顺手,但不是前置——下面的代码段都可以直接跑。

延伸阅读:gRPC proto3 

项目初始化

创建项目

新建一个 demo 目录并进入:

Terminal
mkdir demo && cd demo

初始化项目并安装 gRPCity(需要 Node.js >= 18):

Terminal
npm init -y npm i grpcity

package.json 里加上 "type": "module",下面的代码段以 ESM 形式运行。

项目目录

最终目录结构如下,其中高亮的几行是接下来要新建的文件:

Terminal
. ├── client.js ├── loader.js ├── package-lock.json ├── package.json ├── proto └── helloworld ├── model └── message.proto └── service.proto └── server.js

加载 proto

定义 proto

两个 service——GreeterHellor——共用 model 包里的 message。

service.proto

./proto/helloworld/service.proto
syntax = "proto3"; package helloworld; import "helloworld/model/message.proto"; service Greeter { rpc SayGreet(HelloRequest) returns (HelloReply) {} } service Hellor { rpc SayHello(HelloRequest) returns (HelloReply) {} }

model/message.proto

./proto/helloworld/model/message.proto
syntax = "proto3"; package helloworld.model; message HelloRequest { string name = 1; } message HelloReply { string message = 1; int32 count = 2; }

编写 loader

一份 loader 实例由 server 与 client 共用,proto 文件只解析一次。

./loader.js

./loader.js
import { ProtoLoader } from 'grpcity' import path from 'node:path' // __dirname for esm import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) export default new ProtoLoader({ location: path.join(__dirname, './proto'), files: [ 'helloworld/service.proto' ] })

location 是 loader 搜索的根目录;files 是该目录下的 proto 文件。

loader 部分到此为止。任何需要构造 client 或注册 service 的模块,引入 ./loader.js 即可。

实现服务端

下面的代码全部写在 ./server.js

导入 loader

./server.js
import loader from "./loader.js"

实现 Greeter

每个 service 就是一个普通类。方法收到 call(带 requestmetadata 和辅助方法),返回响应对象即可。

./server.js
class Greeter { constructor() { this.count = 0 } async sayGreet(call) { const { name } = call.request this.count++ return { message: `hello ${name || "world"} by Greeter`, count: this.count } } }

实现 Hellor

同样的写法,第二个 service:

./server.js
class Hellor { async sayHello(call) { const { name } = call.request return { message: `hello ${name || "world"} by Hellor` } } }

绑定和启动

./server.js
const start = async (addr) => { // loader 初始化 await loader.init() // server 初始化并获取实例 const server = await loader.initServer() // 类方法与 service 进行绑定 server.add('helloworld.Greeter', new Greeter()) server.add('helloworld.Hellor', new Hellor()) // 监听 await server.listen(addr) console.log('helloworld server is started: ', addr) } // 启动 start('127.0.0.1:9098')

实现客户端

导入 loader

./client.js
import loader from "./loader.js"

获取客户端

写一个 start 函数,初始化 loader 与 clients 工厂:

./client.js
const start = async (addr) => { await loader.init() const clients = await loader.initClients({ services: { 'helloworld.Greeter': addr, 'helloworld.Hellor': addr } }) }

发起调用

start 里发起两次 RPC 并打印各自的结果:

./client.js
const start = async (addr) => { // ... // loader init // clients init // .... // greeter client const greeterClient = clients.get('helloworld.Greeter') const greeterResult = await greeterClient.sayGreet({ name: 'grpcity' }) console.log('greeterClient.sayGreet', greeterResult.response) // hellor client const hellorClient = clients.get('helloworld.Hellor') const hellorResult = await hellorClient.sayHello({ name: 'grpcity' }) console.log('hellorClient.sayHello', hellorResult.response) } // 执行 start('127.0.0.1:9098')

每次客户端调用都会拿到 { status, peer, metadata, response } 四个字段。这里只打印 response,其余三个在做日志和追踪时同样有用。

运行起来

开两个终端窗口:一个跑服务端,一个跑客户端。

启动服务端

Terminal
node ./server.js helloworld server is started: 127.0.0.1:9098

服务端起来之后,客户端就可以拨这个地址了。

启动客户端

把客户端跑两次,观察服务端在内存里维持的计数累加:

第一次:

Terminal
node ./client.js greeterClient.sayGreet { message: 'hello grpcity by Greeter', count: 1 } hellorClient.sayHello { message: 'hello grpcity by Hellor' }

第二次:

Terminal
node ./client.js greeterClient.sayGreet { message: 'hello grpcity by Greeter', count: 2 } hellorClient.sayHello { message: 'hello grpcity by Hellor' }

count 字段在两次之间累加——状态保留在 Greeter 实例上,正是常驻服务端该有的样子。

整个闭环就到这里。继续阅读 使用指南 了解 streaming、middleware、TLS 等能力。

完整源码见 chakhsu/grpcity-basic-demo 

Last updated on