mirror of https://github.com/grpc/grpc-web.git
Add Node gRPC Server to Example
This commit is contained in:
parent
20930ce66f
commit
3f4cc9e1dc
22
README.md
22
README.md
|
@ -23,8 +23,8 @@ Try gRPC-Web and run a quick Echo example from the browser!
|
|||
From the repo root directory:
|
||||
|
||||
```sh
|
||||
$ docker-compose pull prereqs common echo-server envoy commonjs-client
|
||||
$ docker-compose up -d echo-server envoy commonjs-client
|
||||
$ docker-compose pull prereqs common node-server envoy commonjs-client
|
||||
$ docker-compose up -d node-server envoy commonjs-client
|
||||
```
|
||||
|
||||
Open a browser tab, and go to:
|
||||
|
@ -52,6 +52,14 @@ You can compile the `protoc-gen-grpc-web` protoc plugin from this repo:
|
|||
$ sudo make install-plugin
|
||||
```
|
||||
|
||||
If you don't already have `protoc` installed, you may have to do this first:
|
||||
|
||||
```sh
|
||||
$ ./scripts/init_submodules.sh
|
||||
$ cd third_party/grpc/third_party/protobuf
|
||||
$ ./autogen.sh && ./configure && make -j8 && sudo make install
|
||||
```
|
||||
|
||||
|
||||
## Client Configuration Options
|
||||
|
||||
|
@ -138,10 +146,10 @@ service EchoService {
|
|||
|
||||
Next you need to have a gRPC server that implements the service interface and a
|
||||
gateway proxy that allows the client to connect to the server. Our example
|
||||
builds a simple C++ gRPC backend server and the Envoy proxy.
|
||||
builds a simple Node gRPC backend server and the Envoy proxy.
|
||||
|
||||
For the Echo service: see the
|
||||
[service implementations](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/echo_service_impl.cc).
|
||||
[service implementations](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/node-server/server.js).
|
||||
|
||||
For the Envoy proxy: see the
|
||||
[config yaml file](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/envoy.yaml).
|
||||
|
@ -239,20 +247,20 @@ Multiple proxies supports the gRPC-Web protocol. Currently, the default proxy
|
|||
is [Envoy](https://www.envoyproxy.io), which supports gRPC-Web out of the box.
|
||||
|
||||
```sh
|
||||
$ docker-compose up -d echo-server envoy commonjs-client
|
||||
$ docker-compose up -d node-server envoy commonjs-client
|
||||
```
|
||||
|
||||
An alternative is to build Nginx that comes with this repository.
|
||||
|
||||
```sh
|
||||
$ docker-compose up -d echo-server nginx commonjs-client
|
||||
$ docker-compose up -d node-server nginx commonjs-client
|
||||
```
|
||||
|
||||
You can also try this
|
||||
[gRPC-Web Go Proxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy).
|
||||
|
||||
```sh
|
||||
$ docker-compose up -d echo-server grpcwebproxy binary-client
|
||||
$ docker-compose up -d node-server grpcwebproxy binary-client
|
||||
```
|
||||
|
||||
## Acknowledgement
|
||||
|
|
|
@ -21,6 +21,15 @@ services:
|
|||
image: grpcweb/echo-server
|
||||
ports:
|
||||
- "9090:9090"
|
||||
node-server:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: ./net/grpc/gateway/docker/node_server/Dockerfile
|
||||
depends_on:
|
||||
- common
|
||||
image: grpcweb/node-server
|
||||
ports:
|
||||
- "9090:9090"
|
||||
envoy:
|
||||
build:
|
||||
context: ./
|
||||
|
@ -29,7 +38,7 @@ services:
|
|||
ports:
|
||||
- "8080:8080"
|
||||
links:
|
||||
- echo-server
|
||||
- node-server
|
||||
nginx:
|
||||
build:
|
||||
context: ./
|
||||
|
@ -40,7 +49,7 @@ services:
|
|||
ports:
|
||||
- "8080:8080"
|
||||
links:
|
||||
- echo-server
|
||||
- node-server
|
||||
grpcwebproxy:
|
||||
build:
|
||||
context: ./
|
||||
|
@ -49,7 +58,7 @@ services:
|
|||
ports:
|
||||
- "8080:8080"
|
||||
links:
|
||||
- echo-server
|
||||
- node-server
|
||||
commonjs-client:
|
||||
build:
|
||||
context: ./
|
||||
|
|
|
@ -32,7 +32,7 @@ ADD ./etc/localhost.crt /etc
|
|||
ADD ./etc/localhost.key /etc
|
||||
|
||||
ENTRYPOINT [ "/bin/sh", "-c", "exec /go/bin/grpcwebproxy \
|
||||
--backend_addr=echo-server:9090 \
|
||||
--backend_addr=node-server:9090 \
|
||||
--server_bind_address=0.0.0.0 \
|
||||
--server_http_debug_port=8080 \
|
||||
--run_http_server=true \
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
FROM grpcweb/common
|
||||
|
||||
RUN cd /github/grpc-web-base/net/grpc/gateway/examples/echo && \
|
||||
sed -i 's/localhost:9090/echo-server:9090/g' nginx.conf
|
||||
sed -i 's/localhost:9090/node-server:9090/g' nginx.conf
|
||||
|
||||
RUN cd /github/grpc-web-base && \
|
||||
make standalone-proxy
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2018 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
FROM grpcweb/common
|
||||
|
||||
RUN cd /github/grpc-web/net/grpc/gateway/examples/echo/node-server && \
|
||||
npm install
|
||||
|
||||
EXPOSE 9090
|
||||
CMD ["node", "/github/grpc-web/net/grpc/gateway/examples/echo/node-server/server.js"]
|
|
@ -5,7 +5,7 @@ example. The example has 3 key components:
|
|||
|
||||
- Front-end JS client
|
||||
- Envoy proxy
|
||||
- gRPC backend server (written in C++)
|
||||
- gRPC backend server (written in Node)
|
||||
|
||||
|
||||
## Before you start
|
||||
|
@ -32,13 +32,13 @@ $ docker build -t grpcweb/prereqs \
|
|||
|
||||
## Run the gRPC Backend server
|
||||
|
||||
This compiles the gRPC backend server, written in C++, and listens on port
|
||||
This compiles the gRPC backend server, written in Node, and listens on port
|
||||
9090.
|
||||
|
||||
```sh
|
||||
$ docker build -t grpcweb/echo-server \
|
||||
-f net/grpc/gateway/docker/echo_server/Dockerfile .
|
||||
$ docker run -d -p 9090:9090 --name echo-server grpcweb/echo-server
|
||||
$ docker build -t grpcweb/node-server \
|
||||
-f net/grpc/gateway/docker/node_server/Dockerfile .
|
||||
$ docker run -d -p 9090:9090 --name node-server grpcweb/node-server
|
||||
```
|
||||
|
||||
## Run the Envoy proxy
|
||||
|
@ -49,7 +49,7 @@ requests will be forwarded to port 9090.
|
|||
```sh
|
||||
$ docker build -t grpcweb/envoy \
|
||||
-f net/grpc/gateway/docker/envoy/Dockerfile .
|
||||
$ docker run -d -p 8080:8080 --link echo-server:echo-server grpcweb/envoy
|
||||
$ docker run -d -p 8080:8080 --link node-server:node-server grpcweb/envoy
|
||||
```
|
||||
|
||||
## Serve static JS/HTML contents
|
||||
|
|
|
@ -23,7 +23,7 @@ const {EchoApp} = require('../echoapp.js');
|
|||
const grpc = {};
|
||||
grpc.web = require('grpc-web');
|
||||
|
||||
var echoService = new EchoServiceClient('http://localhost:8080', null, null);
|
||||
var echoService = new EchoServiceClient('http://'+window.location.hostname+':8080', null, null);
|
||||
|
||||
var echoApp = new EchoApp(
|
||||
echoService,
|
||||
|
|
|
@ -16,16 +16,12 @@ syntax = "proto3";
|
|||
|
||||
package grpc.gateway.testing;
|
||||
|
||||
message Empty {
|
||||
}
|
||||
|
||||
message EchoRequest {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message EchoResponse {
|
||||
string message = 1;
|
||||
int32 message_count = 2;
|
||||
}
|
||||
|
||||
// Request type for server side streaming echo.
|
||||
|
@ -48,18 +44,6 @@ message ServerStreamingEchoResponse {
|
|||
string message = 1;
|
||||
}
|
||||
|
||||
// Request type for client side streaming echo.
|
||||
message ClientStreamingEchoRequest {
|
||||
// A special value "" indicates that there's no further messages.
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
// Response type for client side streaming echo.
|
||||
message ClientStreamingEchoResponse {
|
||||
// Total number of client messages that have been received.
|
||||
int32 message_count = 1;
|
||||
}
|
||||
|
||||
// A simple echo service.
|
||||
service EchoService {
|
||||
// One request followed by one response
|
||||
|
@ -70,34 +54,8 @@ service EchoService {
|
|||
rpc EchoAbort(EchoRequest) returns (EchoResponse) {
|
||||
};
|
||||
|
||||
// One empty request, ZERO processing, followed by one empty response
|
||||
// (minimum effort to do message serialization).
|
||||
rpc NoOp(Empty) returns (Empty);
|
||||
|
||||
// One request followed by a sequence of responses (streamed download).
|
||||
// The server will return the same client message repeatedly.
|
||||
rpc ServerStreamingEcho(ServerStreamingEchoRequest)
|
||||
returns (stream ServerStreamingEchoResponse);
|
||||
|
||||
// One request followed by a sequence of responses (streamed download).
|
||||
// The server abort directly.
|
||||
rpc ServerStreamingEchoAbort(ServerStreamingEchoRequest)
|
||||
returns (stream ServerStreamingEchoResponse) {
|
||||
}
|
||||
|
||||
// A sequence of requests followed by one response (streamed upload).
|
||||
// The server returns the total number of messages as the result.
|
||||
rpc ClientStreamingEcho(stream ClientStreamingEchoRequest)
|
||||
returns (ClientStreamingEchoResponse);
|
||||
|
||||
// A sequence of requests with each message echoed by the server immediately.
|
||||
// The server returns the same client messages in order.
|
||||
// E.g. this is how the speech API works.
|
||||
rpc FullDuplexEcho(stream EchoRequest) returns (stream EchoResponse);
|
||||
|
||||
// A sequence of requests followed by a sequence of responses.
|
||||
// The server buffers all the client messages and then returns the same
|
||||
// client messages one by one after the client half-closes the stream.
|
||||
// This is how an image recognition API may work.
|
||||
rpc HalfDuplexEcho(stream EchoRequest) returns (stream EchoResponse);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ using grpc::Status;
|
|||
using grpc::gateway::testing::EchoRequest;
|
||||
using grpc::gateway::testing::EchoResponse;
|
||||
using grpc::gateway::testing::EchoService;
|
||||
using grpc::gateway::testing::Empty;
|
||||
using grpc::gateway::testing::ServerStreamingEchoRequest;
|
||||
using grpc::gateway::testing::ServerStreamingEchoResponse;
|
||||
|
||||
|
@ -68,12 +67,6 @@ Status EchoServiceImpl::EchoAbort(ServerContext* context,
|
|||
"Aborted from server side.");
|
||||
}
|
||||
|
||||
Status EchoServiceImpl::NoOp(ServerContext* context, const Empty* request,
|
||||
Empty* response) {
|
||||
CopyClientMetadataToResponse(context);
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status EchoServiceImpl::ServerStreamingEcho(
|
||||
ServerContext* context, const ServerStreamingEchoRequest* request,
|
||||
ServerWriter<ServerStreamingEchoResponse>* writer) {
|
||||
|
@ -89,14 +82,3 @@ Status EchoServiceImpl::ServerStreamingEcho(
|
|||
}
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status EchoServiceImpl::ServerStreamingEchoAbort(
|
||||
ServerContext* context, const ServerStreamingEchoRequest* request,
|
||||
ServerWriter<ServerStreamingEchoResponse>* writer) {
|
||||
CopyClientMetadataToResponse(context);
|
||||
ServerStreamingEchoResponse response;
|
||||
response.set_message(request->message());
|
||||
writer->Write(response);
|
||||
return Status(grpc::StatusCode::ABORTED,
|
||||
"Aborted from server side.");
|
||||
}
|
||||
|
|
|
@ -40,20 +40,11 @@ class EchoServiceImpl final :
|
|||
grpc::ServerContext* context,
|
||||
const grpc::gateway::testing::EchoRequest* request,
|
||||
grpc::gateway::testing::EchoResponse* response) override;
|
||||
grpc::Status NoOp(
|
||||
grpc::ServerContext* context,
|
||||
const grpc::gateway::testing::Empty* request,
|
||||
grpc::gateway::testing::Empty* response) override;
|
||||
grpc::Status ServerStreamingEcho(
|
||||
grpc::ServerContext* context,
|
||||
const grpc::gateway::testing::ServerStreamingEchoRequest* request,
|
||||
grpc::ServerWriter<
|
||||
grpc::gateway::testing::ServerStreamingEchoResponse>* writer) override;
|
||||
grpc::Status ServerStreamingEchoAbort(
|
||||
grpc::ServerContext* context,
|
||||
const grpc::gateway::testing::ServerStreamingEchoRequest* request,
|
||||
grpc::ServerWriter<
|
||||
grpc::gateway::testing::ServerStreamingEchoResponse>* writer) override;
|
||||
};
|
||||
|
||||
#endif // NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_
|
||||
|
|
|
@ -40,4 +40,4 @@ static_resources:
|
|||
type: logical_dns
|
||||
http2_protocol_options: {}
|
||||
lb_policy: round_robin
|
||||
hosts: [{ socket_address: { address: echo-server, port_value: 9090 }}]
|
||||
hosts: [{ socket_address: { address: node-server, port_value: 9090 }}]
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
node_modules/
|
||||
package-lock.json
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "grpc-web-node-server-example",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.3.0",
|
||||
"async": "^1.5.2",
|
||||
"google-protobuf": "^3.6.0",
|
||||
"grpc": "^1.15.0",
|
||||
"lodash": "^4.6.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015 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.
|
||||
*
|
||||
*/
|
||||
|
||||
var PROTO_PATH = __dirname + '/../echo.proto';
|
||||
|
||||
var async = require('async');
|
||||
var _ = require('lodash');
|
||||
var grpc = require('grpc');
|
||||
var protoLoader = require('@grpc/proto-loader');
|
||||
var packageDefinition = protoLoader.loadSync(
|
||||
PROTO_PATH,
|
||||
{keepCase: true,
|
||||
longs: String,
|
||||
enums: String,
|
||||
defaults: true,
|
||||
oneofs: true
|
||||
});
|
||||
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
||||
var echo = protoDescriptor.grpc.gateway.testing;
|
||||
|
||||
|
||||
function copyMetadata(call) {
|
||||
var metadata = call.metadata.getMap();
|
||||
var response_metadata = new grpc.Metadata();
|
||||
for (var key in metadata) {
|
||||
response_metadata.set(key, metadata[key]);
|
||||
}
|
||||
return response_metadata;
|
||||
}
|
||||
|
||||
function doEcho(call, callback) {
|
||||
callback(null, {
|
||||
message: call.request.message
|
||||
}, copyMetadata(call));
|
||||
}
|
||||
|
||||
function doEchoAbort(call, callback) {
|
||||
callback({
|
||||
code: grpc.status.ABORTED,
|
||||
message: 'Aborted from server side.'
|
||||
});
|
||||
}
|
||||
|
||||
function doServerStreamingEcho(call) {
|
||||
var senders = [];
|
||||
function sender(message, interval) {
|
||||
return (callback) => {
|
||||
call.write({
|
||||
message: message
|
||||
});
|
||||
_.delay(callback, interval);
|
||||
};
|
||||
}
|
||||
for (var i = 0; i < call.request.message_count; i++) {
|
||||
senders[i] = sender(call.request.message, call.request.message_interval);
|
||||
}
|
||||
async.series(senders, () => {
|
||||
call.end(copyMetadata(call));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new server with the handler functions in this file bound to the methods
|
||||
* it serves.
|
||||
* @return {Server} The new server object
|
||||
*/
|
||||
function getServer() {
|
||||
var server = new grpc.Server();
|
||||
server.addProtoService(echo.EchoService.service, {
|
||||
echo: doEcho,
|
||||
echoAbort: doEchoAbort,
|
||||
serverStreamingEcho: doServerStreamingEcho,
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
// If this is run as a script, start a server on an unused port
|
||||
var echoServer = getServer();
|
||||
echoServer.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
|
||||
echoServer.start();
|
||||
}
|
||||
|
||||
exports.getServer = getServer;
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"noImplicitAny": true,
|
||||
"allowJs": true,
|
||||
|
|
|
@ -26,19 +26,16 @@ service EchoService {
|
|||
|
||||
## Implement gRPC Backend Server
|
||||
|
||||
Next, we implement our EchoService interface using C++ in the backend gRPC
|
||||
Next, we implement our EchoService interface using Node in the backend gRPC
|
||||
`EchoServer`. This will handle requests from clients. See the file
|
||||
[`echo_server.cc`](echo_server.cc) for details.
|
||||
[`node-server/server.js`](./node-server/server.js) for details.
|
||||
|
||||
You can implement the server in any language supported by gRPC. Please see
|
||||
the [gRPC website][] for more details.
|
||||
|
||||
```cpp
|
||||
Status EchoServiceImpl::Echo(ServerContext* context,
|
||||
const EchoRequest* request,
|
||||
EchoResponse* response) {
|
||||
response->set_message(request->message());
|
||||
return Status::OK;
|
||||
```js
|
||||
function doEcho(call, callback) {
|
||||
callback(null, {message: call.request.message});
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -80,7 +77,7 @@ this:
|
|||
type: logical_dns
|
||||
http2_protocol_options: {}
|
||||
lb_policy: round_robin
|
||||
hosts: [{ socket_address: { address: echo-server, port_value: 9090 }}]
|
||||
hosts: [{ socket_address: { address: node-server, port_value: 9090 }}]
|
||||
```
|
||||
|
||||
You may also need to add some CORS setup to make sure the browser can request
|
||||
|
|
|
@ -76,7 +76,7 @@ Here's a quick way to get started!
|
|||
```sh
|
||||
$ git clone https://github.com/grpc/grpc-web
|
||||
$ cd grpc-web
|
||||
$ docker-compose up echo-server envoy commonjs-client
|
||||
$ docker-compose up node-server envoy commonjs-client
|
||||
```
|
||||
|
||||
Open a browser tab, and go to:
|
||||
|
|
|
@ -44,7 +44,7 @@ cd packages/grpc-web && \
|
|||
# Bring up the Echo server and the Envoy proxy (in background).
|
||||
# The 'sleep' seems necessary for the docker containers to be fully up
|
||||
# and listening before we test the with curl requests
|
||||
docker-compose up -d echo-server envoy && sleep 5;
|
||||
docker-compose up -d node-server envoy && sleep 5;
|
||||
|
||||
# Run a curl request and verify the output
|
||||
source ./scripts/test-proxy.sh
|
||||
|
|
Loading…
Reference in New Issue