├── README.md ├── golang ├── .gitignore ├── client_main.go ├── proto │ ├── hello.pb.go │ └── hello.proto └── server_main.go └── node ├── .autod.conf.js ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── README.zh-CN.md ├── app.js ├── app ├── controller │ └── home.js ├── proto │ └── hello.proto └── router.js ├── appveyor.yml ├── config ├── config.default.js └── plugin.js ├── package.json └── test └── app └── controller └── home.test.js /README.md: -------------------------------------------------------------------------------- 1 | # grpc_node_go_demo 2 | 3 | 这是一个使用grpc让Node.js与Go互相调用RPC服务的Demo。 4 | 5 | 下文是教程 6 | 7 | # 定义.proto 8 | 9 | 新建文件 **hello.proto** 定义一个 **HelloService** ,并且有一个 **SayHello** 方法。 10 | 11 | ```protobuf 12 | syntax = "proto3"; 13 | 14 | package demo; 15 | 16 | service HelloService { 17 | rpc SayHello (HelloRequest) returns (HelloResponse) {} 18 | } 19 | 20 | message HelloRequest { 21 | string code = 1; 22 | string message = 2; 23 | } 24 | 25 | message HelloResponse { 26 | string code = 1; 27 | string message = 2; 28 | } 29 | ``` 30 | 31 | # Node.js 32 | 33 | 官网教程:[Node Quick Start](https://grpc.io/docs/quickstart/node.html) 34 | 35 | 官方例子:[grpc/examples/node/](https://github.com/grpc/grpc/tree/master/examples/node) 36 | 37 | Node.js使用: 38 | * 开发框架:[egg.js](https://github.com/eggjs/egg) 39 | * grpc插件:[egg-grpc](https://github.com/eggjs/egg-grpc) 40 | 41 | 将 **hello.proto** 放入 **app/proto/** 下 42 | 43 | ## Node.js客户端 44 | 45 | 在 **config/plugin.js** 启动egg-grpc插件 46 | ```js 47 | // egg-grpc插件 48 | exports.grpc = { 49 | enable: true, 50 | package: 'egg-grpc', 51 | }; 52 | ``` 53 | 54 | 在 **config/config.default.js** 配置grpc 55 | ```js 56 | // egg-grpc配置 57 | config.grpc = { 58 | endpoint: 'localhost:50051', // 服务端地址 59 | }; 60 | ``` 61 | 62 | 在 **app/controller/home.js** 调用服务端 63 | ```js 64 | class HomeController extends Controller { 65 | async index() { 66 | const ctx = this.ctx; 67 | 68 | // 获得HelloService实例 69 | const helloService = ctx.grpc.demo.helloService; 70 | 71 | // 向服务端发送请求 72 | const result = await helloService.sayHello({ 73 | code: '0', 74 | message: '来自Node客户端的OK', 75 | }); 76 | 77 | // 打印服务端响应内容 78 | console.log(result); 79 | ctx.body = result; 80 | } 81 | } 82 | ``` 83 | 84 | ## Node.js服务端 85 | 86 | 一般服务端是在项目启动时进行加载,所以在 **app.js** 定义项目启动时执行的方法 87 | ```js 88 | const PROTO_FILE_PATH = __dirname + '/app/proto/hello.proto'; // proto文件位置 89 | const PORT = ':50051'; // RPC服务端端口 90 | 91 | const grpc = require('grpc'); 92 | const protoLoader = require('@grpc/proto-loader'); 93 | 94 | module.exports = app => { 95 | app.beforeStart(async () => { 96 | // 新建一个grpc服务器 97 | const server = new grpc.Server(); 98 | 99 | // 异步加载服务 100 | await protoLoader.load(PROTO_FILE_PATH).then(packageDefinition => { 101 | 102 | // 获取proto 103 | const helloProto = grpc.loadPackageDefinition(packageDefinition); 104 | 105 | // 获取package 106 | const grpc_demo = helloProto.demo; 107 | 108 | // 定义HelloService的SayHello实现 109 | const sayHello = (call, callback) => { 110 | // 打印客户端请求内容 111 | console.log(call.request); 112 | 113 | // 响应客户端 114 | callback(null, { 115 | code: '0', 116 | message: '来自Node服务端的OK', 117 | }); 118 | }; 119 | 120 | // 将sayHello方法作为HelloService的实现放入grpc服务器中 121 | server.addService(grpc_demo.HelloService.service, { sayHello }); 122 | }); 123 | 124 | // 启动服务监听 125 | server.bind(`0.0.0.0${PORT}`, grpc.ServerCredentials.createInsecure()); 126 | server.start(); 127 | }); 128 | }; 129 | ``` 130 | 131 | # Go 132 | 133 | 官网教程:[Go Quick Start](https://grpc.io/docs/quickstart/go.html) 134 | 135 | 官方例子:[grpc-go/examples/helloworld/](https://github.com/grpc/grpc-go/tree/master/examples/helloworld) 136 | 137 | Go按照教程直接导入对应的包即可,然后 138 | 1. 将 **hello.proto** 放入 **proto/** 下 139 | 2. 然后使用 `protoc -I proto/ proto/hello.proto --go_out=plugins=grpc:proto` 生成 **hello.pb.go** 140 | 141 | ## Go客户端 142 | ```go 143 | const ( 144 | address = "localhost:50051" // 服务端地址 145 | ) 146 | 147 | func main() { 148 | // 启动grpc客户端,连接grpc服务端 149 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 150 | if err != nil { 151 | log.Fatalf("did not connect: %v", err) 152 | } 153 | defer conn.Close() 154 | 155 | // 使用连接,创建HelloService实例 156 | helloService := pb.NewHelloServiceClient(conn) 157 | 158 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 159 | defer cancel() 160 | 161 | // 调用SayHello,向服务端发送消息 162 | response, err := helloService.SayHello(ctx, &pb.HelloRequest{ 163 | Code: "0", 164 | Message: "来自GO客户端的OK", 165 | }) 166 | if err != nil { 167 | log.Fatalf("could not sayHello: %v", err) 168 | } 169 | 170 | // 打印服务端回应内容 171 | log.Printf("SayHello: Code: %s,Message: %s", response.Code, response.Message) 172 | } 173 | ``` 174 | 175 | ## Go服务端 176 | 177 | ```go 178 | const ( 179 | port = ":50051" // RPC服务端端口 180 | ) 181 | 182 | type HelloService struct{} 183 | 184 | // 实现hello的HelloServiceServer中的SayHello方法 185 | // 表示实现了HelloServiceServer接口 186 | func (s *HelloService) SayHello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloResponse, error) { 187 | 188 | // 打印客户端请求内容 189 | log.Printf("SayHello: Code: %s,Message: %s", request.Code, request.Message) 190 | 191 | // 响应客户端 192 | return &pb.HelloResponse{ 193 | Code: "0", 194 | Message: "来自GO服务端的OK", 195 | }, nil 196 | } 197 | 198 | func main() { 199 | // 启动服务监听 200 | lis, err := net.Listen("tcp", port) 201 | if err != nil { 202 | log.Fatalf("failed to listen: %v", err) 203 | } 204 | 205 | // 新建一个grpc服务器 206 | s := grpc.NewServer() 207 | 208 | // 将实现HelloService注册到grpc服务器中 209 | pb.RegisterHelloServiceServer(s, &HelloService{}) 210 | reflection.Register(s) 211 | 212 | if err := s.Serve(lis); err != nil { 213 | log.Fatalf("failed to serve: %v", err) 214 | } 215 | } 216 | ``` 217 | -------------------------------------------------------------------------------- /golang/.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /golang/client_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | pb "./proto" 10 | ) 11 | 12 | const ( 13 | address = "localhost:50051" // 服务端地址 14 | ) 15 | 16 | func main() { 17 | // 启动grpc客户端,连接grpc服务端 18 | conn, err := grpc.Dial(address, grpc.WithInsecure()) 19 | if err != nil { 20 | log.Fatalf("did not connect: %v", err) 21 | } 22 | defer conn.Close() 23 | 24 | // 使用连接,创建HelloService实例 25 | helloService := pb.NewHelloServiceClient(conn) 26 | 27 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 28 | defer cancel() 29 | 30 | // 调用SayHello,向服务端发送消息 31 | response, err := helloService.SayHello(ctx, &pb.HelloRequest{ 32 | Code: "0", 33 | Message: "来自GO客户端的OK", 34 | }) 35 | if err != nil { 36 | log.Fatalf("could not sayHello: %v", err) 37 | } 38 | 39 | // 打印服务端回应内容 40 | log.Printf("SayHello: Code: %s,Message: %s", response.Code, response.Message) 41 | } -------------------------------------------------------------------------------- /golang/proto/hello.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: hello.proto 3 | 4 | package demo 5 | 6 | import proto "github.com/golang/protobuf/proto" 7 | import fmt "fmt" 8 | import math "math" 9 | 10 | import ( 11 | context "golang.org/x/net/context" 12 | grpc "google.golang.org/grpc" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | // This is a compile-time assertion to ensure that this generated file 21 | // is compatible with the proto package it is being compiled against. 22 | // A compilation error at this line likely means your copy of the 23 | // proto package needs to be updated. 24 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 25 | 26 | type HelloRequest struct { 27 | Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` 28 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 35 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 36 | func (*HelloRequest) ProtoMessage() {} 37 | func (*HelloRequest) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_hello_8e675a015969d7bb, []int{0} 39 | } 40 | func (m *HelloRequest) XXX_Unmarshal(b []byte) error { 41 | return xxx_messageInfo_HelloRequest.Unmarshal(m, b) 42 | } 43 | func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 | return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) 45 | } 46 | func (dst *HelloRequest) XXX_Merge(src proto.Message) { 47 | xxx_messageInfo_HelloRequest.Merge(dst, src) 48 | } 49 | func (m *HelloRequest) XXX_Size() int { 50 | return xxx_messageInfo_HelloRequest.Size(m) 51 | } 52 | func (m *HelloRequest) XXX_DiscardUnknown() { 53 | xxx_messageInfo_HelloRequest.DiscardUnknown(m) 54 | } 55 | 56 | var xxx_messageInfo_HelloRequest proto.InternalMessageInfo 57 | 58 | func (m *HelloRequest) GetCode() string { 59 | if m != nil { 60 | return m.Code 61 | } 62 | return "" 63 | } 64 | 65 | func (m *HelloRequest) GetMessage() string { 66 | if m != nil { 67 | return m.Message 68 | } 69 | return "" 70 | } 71 | 72 | type HelloResponse struct { 73 | Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` 74 | Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` 75 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 76 | XXX_unrecognized []byte `json:"-"` 77 | XXX_sizecache int32 `json:"-"` 78 | } 79 | 80 | func (m *HelloResponse) Reset() { *m = HelloResponse{} } 81 | func (m *HelloResponse) String() string { return proto.CompactTextString(m) } 82 | func (*HelloResponse) ProtoMessage() {} 83 | func (*HelloResponse) Descriptor() ([]byte, []int) { 84 | return fileDescriptor_hello_8e675a015969d7bb, []int{1} 85 | } 86 | func (m *HelloResponse) XXX_Unmarshal(b []byte) error { 87 | return xxx_messageInfo_HelloResponse.Unmarshal(m, b) 88 | } 89 | func (m *HelloResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 90 | return xxx_messageInfo_HelloResponse.Marshal(b, m, deterministic) 91 | } 92 | func (dst *HelloResponse) XXX_Merge(src proto.Message) { 93 | xxx_messageInfo_HelloResponse.Merge(dst, src) 94 | } 95 | func (m *HelloResponse) XXX_Size() int { 96 | return xxx_messageInfo_HelloResponse.Size(m) 97 | } 98 | func (m *HelloResponse) XXX_DiscardUnknown() { 99 | xxx_messageInfo_HelloResponse.DiscardUnknown(m) 100 | } 101 | 102 | var xxx_messageInfo_HelloResponse proto.InternalMessageInfo 103 | 104 | func (m *HelloResponse) GetCode() string { 105 | if m != nil { 106 | return m.Code 107 | } 108 | return "" 109 | } 110 | 111 | func (m *HelloResponse) GetMessage() string { 112 | if m != nil { 113 | return m.Message 114 | } 115 | return "" 116 | } 117 | 118 | func init() { 119 | proto.RegisterType((*HelloRequest)(nil), "demo.HelloRequest") 120 | proto.RegisterType((*HelloResponse)(nil), "demo.HelloResponse") 121 | } 122 | 123 | // Reference imports to suppress errors if they are not otherwise used. 124 | var _ context.Context 125 | var _ grpc.ClientConn 126 | 127 | // This is a compile-time assertion to ensure that this generated file 128 | // is compatible with the grpc package it is being compiled against. 129 | const _ = grpc.SupportPackageIsVersion4 130 | 131 | // HelloServiceClient is the client API for HelloService service. 132 | // 133 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 134 | type HelloServiceClient interface { 135 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) 136 | } 137 | 138 | type helloServiceClient struct { 139 | cc *grpc.ClientConn 140 | } 141 | 142 | func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient { 143 | return &helloServiceClient{cc} 144 | } 145 | 146 | func (c *helloServiceClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { 147 | out := new(HelloResponse) 148 | err := c.cc.Invoke(ctx, "/demo.HelloService/SayHello", in, out, opts...) 149 | if err != nil { 150 | return nil, err 151 | } 152 | return out, nil 153 | } 154 | 155 | // HelloServiceServer is the server API for HelloService service. 156 | type HelloServiceServer interface { 157 | SayHello(context.Context, *HelloRequest) (*HelloResponse, error) 158 | } 159 | 160 | func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) { 161 | s.RegisterService(&_HelloService_serviceDesc, srv) 162 | } 163 | 164 | func _HelloService_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 165 | in := new(HelloRequest) 166 | if err := dec(in); err != nil { 167 | return nil, err 168 | } 169 | if interceptor == nil { 170 | return srv.(HelloServiceServer).SayHello(ctx, in) 171 | } 172 | info := &grpc.UnaryServerInfo{ 173 | Server: srv, 174 | FullMethod: "/demo.HelloService/SayHello", 175 | } 176 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 177 | return srv.(HelloServiceServer).SayHello(ctx, req.(*HelloRequest)) 178 | } 179 | return interceptor(ctx, in, info, handler) 180 | } 181 | 182 | var _HelloService_serviceDesc = grpc.ServiceDesc{ 183 | ServiceName: "demo.HelloService", 184 | HandlerType: (*HelloServiceServer)(nil), 185 | Methods: []grpc.MethodDesc{ 186 | { 187 | MethodName: "SayHello", 188 | Handler: _HelloService_SayHello_Handler, 189 | }, 190 | }, 191 | Streams: []grpc.StreamDesc{}, 192 | Metadata: "hello.proto", 193 | } 194 | 195 | func init() { proto.RegisterFile("hello.proto", fileDescriptor_hello_8e675a015969d7bb) } 196 | 197 | var fileDescriptor_hello_8e675a015969d7bb = []byte{ 198 | // 147 bytes of a gzipped FileDescriptorProto 199 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9, 200 | 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x49, 0x49, 0xcd, 0xcd, 0x57, 0xb2, 0xe1, 201 | 0xe2, 0xf1, 0x00, 0x09, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0x24, 202 | 0xe7, 0xa7, 0xa4, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x12, 0x5c, 0xec, 203 | 0xb9, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0xa9, 0x12, 0x4c, 0x60, 0x61, 0x18, 0x57, 0xc9, 0x96, 0x8b, 204 | 0x17, 0xaa, 0xbb, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x95, 0x34, 0xed, 0x46, 0xae, 0x50, 0xcb, 0x83, 205 | 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x85, 0x4c, 0xb9, 0x38, 0x82, 0x13, 0x2b, 0xc1, 0x42, 0x42, 206 | 0x42, 0x7a, 0x20, 0xf7, 0xe9, 0x21, 0x3b, 0x4e, 0x4a, 0x18, 0x45, 0x0c, 0x62, 0xa5, 0x12, 0x43, 207 | 0x12, 0x1b, 0xd8, 0x43, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0xba, 0xda, 0x5e, 0xdf, 208 | 0x00, 0x00, 0x00, 209 | } 210 | -------------------------------------------------------------------------------- /golang/proto/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package demo; 4 | 5 | service HelloService { 6 | rpc SayHello (HelloRequest) returns (HelloResponse) {} 7 | } 8 | 9 | message HelloRequest { 10 | string code = 1; 11 | string message = 2; 12 | } 13 | 14 | message HelloResponse { 15 | string code = 1; 16 | string message = 2; 17 | } -------------------------------------------------------------------------------- /golang/server_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | "golang.org/x/net/context" 8 | "google.golang.org/grpc" 9 | pb "./proto" 10 | "google.golang.org/grpc/reflection" 11 | ) 12 | 13 | const ( 14 | port = ":50051" // RPC服务端端口 15 | ) 16 | 17 | type HelloService struct{} 18 | 19 | // 实现hello的HelloServiceServer中的SayHello方法 20 | // 表示实现了HelloServiceServer接口 21 | func (s *HelloService) SayHello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloResponse, error) { 22 | 23 | // 打印客户端请求内容 24 | log.Printf("SayHello: Code: %s,Message: %s", request.Code, request.Message) 25 | 26 | // 响应客户端 27 | return &pb.HelloResponse{ 28 | Code: "0", 29 | Message: "来自GO服务端的OK", 30 | }, nil 31 | } 32 | 33 | func main() { 34 | // 启动服务监听 35 | lis, err := net.Listen("tcp", port) 36 | if err != nil { 37 | log.Fatalf("failed to listen: %v", err) 38 | } 39 | 40 | // 新建一个grpc服务器 41 | s := grpc.NewServer() 42 | 43 | // 将实现HelloService注册到grpc服务器中 44 | pb.RegisterHelloServiceServer(s, &HelloService{}) 45 | reflection.Register(s) 46 | 47 | if err := s.Serve(lis); err != nil { 48 | log.Fatalf("failed to serve: %v", err) 49 | } 50 | } -------------------------------------------------------------------------------- /node/.autod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | write: true, 5 | prefix: '^', 6 | plugin: 'autod-egg', 7 | test: [ 8 | 'test', 9 | 'benchmark', 10 | ], 11 | dep: [ 12 | 'egg', 13 | 'egg-scripts', 14 | ], 15 | devdep: [ 16 | 'egg-ci', 17 | 'egg-bin', 18 | 'egg-mock', 19 | 'autod', 20 | 'autod-egg', 21 | 'eslint', 22 | 'eslint-config-egg', 23 | 'webstorm-disable-index', 24 | ], 25 | exclude: [ 26 | './test/fixtures', 27 | './dist', 28 | ], 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /node/.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | -------------------------------------------------------------------------------- /node/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-egg" 3 | } 4 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | npm-debug.log 3 | yarn-error.log 4 | node_modules/ 5 | package-lock.json 6 | yarn.lock 7 | coverage/ 8 | .idea/ 9 | run/ 10 | .DS_Store 11 | *.sw* 12 | *.un~ 13 | -------------------------------------------------------------------------------- /node/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '8' 5 | install: 6 | - npm i npminstall && npminstall 7 | script: 8 | - npm run ci 9 | after_script: 10 | - npminstall codecov && codecov 11 | -------------------------------------------------------------------------------- /node/README.md: -------------------------------------------------------------------------------- 1 | # grpc_demo 2 | 3 | 4 | 5 | ## QuickStart 6 | 7 | 8 | 9 | see [egg docs][egg] for more detail. 10 | 11 | ### Development 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### Deploy 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### npm scripts 27 | 28 | - Use `npm run lint` to check code style. 29 | - Use `npm test` to run unit test. 30 | - Use `npm run autod` to auto detect dependencies upgrade, see [autod](https://www.npmjs.com/package/autod) for more detail. 31 | 32 | 33 | [egg]: https://eggjs.org -------------------------------------------------------------------------------- /node/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # grpc_demo 2 | 3 | 4 | 5 | ## 快速入门 6 | 7 | 8 | 9 | 如需进一步了解,参见 [egg 文档][egg]。 10 | 11 | ### 本地开发 12 | 13 | ```bash 14 | $ npm i 15 | $ npm run dev 16 | $ open http://localhost:7001/ 17 | ``` 18 | 19 | ### 部署 20 | 21 | ```bash 22 | $ npm start 23 | $ npm stop 24 | ``` 25 | 26 | ### 单元测试 27 | 28 | - [egg-bin] 内置了 [mocha], [thunk-mocha], [power-assert], [istanbul] 等框架,让你可以专注于写单元测试,无需理会配套工具。 29 | - 断言库非常推荐使用 [power-assert]。 30 | - 具体参见 [egg 文档 - 单元测试](https://eggjs.org/zh-cn/core/unittest)。 31 | 32 | ### 内置指令 33 | 34 | - 使用 `npm run lint` 来做代码风格检查。 35 | - 使用 `npm test` 来执行单元测试。 36 | - 使用 `npm run autod` 来自动检测依赖更新,详细参见 [autod](https://www.npmjs.com/package/autod) 。 37 | 38 | 39 | [egg]: https://eggjs.org 40 | -------------------------------------------------------------------------------- /node/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const PROTO_FILE_PATH = __dirname + '/app/proto/hello.proto'; // proto文件位置 4 | const PORT = ':50051'; // RPC服务端端口 5 | 6 | const grpc = require('grpc'); 7 | const protoLoader = require('@grpc/proto-loader'); 8 | 9 | module.exports = app => { 10 | app.beforeStart(async () => { 11 | // 新建一个grpc服务器 12 | const server = new grpc.Server(); 13 | 14 | // 异步加载服务 15 | await protoLoader.load(PROTO_FILE_PATH).then(packageDefinition => { 16 | 17 | // 获取proto 18 | const helloProto = grpc.loadPackageDefinition(packageDefinition); 19 | 20 | // 获取package 21 | const grpc_demo = helloProto.demo; 22 | 23 | // 定义HelloService的SayHello实现 24 | const sayHello = (call, callback) => { 25 | // 打印客户端请求内容 26 | console.log(call.request); 27 | 28 | // 响应客户端 29 | callback(null, { 30 | code: '0', 31 | message: '来自Node服务端的OK', 32 | }); 33 | }; 34 | 35 | // 将sayHello方法作为HelloService的实现放入grpc服务器中 36 | server.addService(grpc_demo.HelloService.service, { sayHello }); 37 | }); 38 | 39 | // 启动服务监听 40 | server.bind(`0.0.0.0${PORT}`, grpc.ServerCredentials.createInsecure()); 41 | server.start(); 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /node/app/controller/home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Controller = require('egg').Controller; 4 | 5 | class HomeController extends Controller { 6 | async index() { 7 | const ctx = this.ctx; 8 | 9 | // 获得HelloService实例 10 | const helloService = ctx.grpc.demo.helloService; 11 | 12 | // 向服务端发送请求 13 | const result = await helloService.sayHello({ 14 | code: '0', 15 | message: '来自Node客户端的OK', 16 | }); 17 | 18 | // 打印服务端响应内容 19 | console.log(result); 20 | ctx.body = result; 21 | } 22 | } 23 | 24 | module.exports = HomeController; 25 | -------------------------------------------------------------------------------- /node/app/proto/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package demo; 4 | 5 | service HelloService { 6 | rpc SayHello (HelloRequest) returns (HelloResponse) {} 7 | } 8 | 9 | message HelloRequest { 10 | string code = 1; 11 | string message = 2; 12 | } 13 | 14 | message HelloResponse { 15 | string code = 1; 16 | string message = 2; 17 | } 18 | -------------------------------------------------------------------------------- /node/app/router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @param {Egg.Application} app - egg application 5 | */ 6 | module.exports = app => { 7 | const { router, controller } = app; 8 | router.get('/', controller.home.index); 9 | }; 10 | -------------------------------------------------------------------------------- /node/appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | 5 | install: 6 | - ps: Install-Product node $env:nodejs_version 7 | - npm i npminstall && node_modules\.bin\npminstall 8 | 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm run test 13 | 14 | build: off 15 | -------------------------------------------------------------------------------- /node/config/config.default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = appInfo => { 4 | const config = exports = {}; 5 | 6 | // use for cookie sign key, should change to your own and keep security 7 | config.keys = appInfo.name + '_1531908274808_8695'; 8 | 9 | // egg-grpc配置 10 | config.grpc = { 11 | endpoint: 'localhost:50051', // 服务端地址 12 | }; 13 | 14 | return config; 15 | }; 16 | -------------------------------------------------------------------------------- /node/config/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // egg-grpc插件 4 | exports.grpc = { 5 | enable: true, 6 | package: 'egg-grpc', 7 | }; 8 | -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc_demo", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "dependencies": { 7 | "@grpc/proto-loader": "^0.3.0", 8 | "egg": "^2.2.1", 9 | "egg-grpc": "^1.0.3", 10 | "egg-scripts": "^2.5.0" 11 | }, 12 | "devDependencies": { 13 | "autod": "^3.0.1", 14 | "autod-egg": "^1.0.0", 15 | "egg-bin": "^4.3.5", 16 | "egg-ci": "^1.8.0", 17 | "egg-mock": "^3.14.0", 18 | "eslint": "^4.11.0", 19 | "eslint-config-egg": "^6.0.0", 20 | "webstorm-disable-index": "^1.2.0" 21 | }, 22 | "engines": { 23 | "node": ">=8.9.0" 24 | }, 25 | "scripts": { 26 | "start": "egg-scripts start --daemon --title=egg-server-grpc_demo", 27 | "stop": "egg-scripts stop --title=egg-server-grpc_demo", 28 | "dev": "egg-bin dev", 29 | "debug": "egg-bin debug", 30 | "test": "npm run lint -- --fix && npm run test-local", 31 | "test-local": "egg-bin test", 32 | "cov": "egg-bin cov", 33 | "lint": "eslint .", 34 | "ci": "npm run lint && npm run cov", 35 | "autod": "autod" 36 | }, 37 | "ci": { 38 | "version": "8" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "" 43 | }, 44 | "author": "", 45 | "license": "MIT" 46 | } 47 | -------------------------------------------------------------------------------- /node/test/app/controller/home.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app, assert } = require('egg-mock/bootstrap'); 4 | 5 | describe('test/app/controller/home.test.js', () => { 6 | 7 | it('should assert', function* () { 8 | const pkg = require('../../../package.json'); 9 | assert(app.config.keys.startsWith(pkg.name)); 10 | 11 | // const ctx = app.mockContext({}); 12 | // yield ctx.service.xx(); 13 | }); 14 | 15 | it('should GET /', () => { 16 | return app.httpRequest() 17 | .get('/') 18 | .expect('hi, egg') 19 | .expect(200); 20 | }); 21 | }); 22 | --------------------------------------------------------------------------------