mirror of https://github.com/grpc/grpc.io.git
632 lines
52 KiB
HTML
632 lines
52 KiB
HTML
|
|
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400" rel="stylesheet">
|
|
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
|
<title>
|
|
gRPC Basics - Node.js – gRPC
|
|
</title>
|
|
|
|
|
|
<link rel="apple-touch-icon" href="/favicons/apple-touch-icon.png" sizes="180x180">
|
|
<link rel="icon" type="image/png" href="/favicons/android-chrome-192x192.png" sizes="192x192" >
|
|
<link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
|
|
<link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
|
|
<link rel="manifest" href="/favicons/manifest.json">
|
|
<link rel="mask-icon" href="/favicons/safari-pinned-tab.svg" color="#2DA6B0">
|
|
<meta name="msapplication-TileColor" content="#ffffff">
|
|
<meta name="msapplication-TileImage" content="/favicons/mstile-150x150.png">
|
|
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-60127042-1"></script>
|
|
<script>
|
|
window.dataLayer = window.dataLayer || [];
|
|
function gtag(){dataLayer.push(arguments);}
|
|
gtag('js', new Date());
|
|
|
|
gtag('config', 'UA-60127042-1');
|
|
</script>
|
|
|
|
</head>
|
|
<body>
|
|
<div id="landing-content">
|
|
<div class="row">
|
|
<div class="topbannersub">
|
|
<nav class="navbar navbar-expand-md navbar-dark topnav">
|
|
<a class="navbar-brand" href="https://cjyabraham.github.io/">
|
|
<img src="https://cjyabraham.github.io/img/grpc-logo.png" width="114" height="50">
|
|
</a>
|
|
|
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
|
|
<div class="topnav, collapse navbar-collapse" id="navbarSupportedContent" style="float:right !important">
|
|
<ul class="navbar-nav ml-auto">
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="https://cjyabraham.github.io/about/">About</a>
|
|
</li>
|
|
<li class="nav-item dropdown active">
|
|
<a class="nav-link dropdown-toggle" href="https://cjyabraham.github.io/docs/" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
Docs
|
|
</a>
|
|
|
|
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
|
|
|
|
|
<a class="dropdown-item" href="/docs">
|
|
Overview
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/quickstart/">
|
|
Quick Start
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/guides/">
|
|
Guides
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/tutorials/">
|
|
Tutorials
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/reference/">
|
|
Reference
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/samples/">
|
|
Samples
|
|
</a>
|
|
|
|
|
|
<a class="dropdown-item" href="/docs/talks">
|
|
Presentations
|
|
</a>
|
|
|
|
</div>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="/blog">
|
|
Blog
|
|
</a>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="/community">Community</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="https://packages.grpc.io/">
|
|
Packages
|
|
</a>
|
|
</li>
|
|
<li class="nav-item ">
|
|
<a class="nav-link" href="https://cjyabraham.github.io/faq/">FAQ</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
|
|
<div class="headertext">Documentation</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="subnav d-none d-md-block">
|
|
<a href="https://cjyabraham.github.io/docs/" >Overview</a>
|
|
| <a href="https://cjyabraham.github.io/docs/quickstart/" >Quick Start</a>
|
|
| <a href="https://cjyabraham.github.io/docs/guides/" >Guides</a>
|
|
| <a href="https://cjyabraham.github.io/docs/tutorials/" class="active">Tutorials</a>
|
|
| <a href="https://cjyabraham.github.io/docs/reference/" >Reference</a>
|
|
| <a href="https://cjyabraham.github.io/docs/samples/" >Samples</a>
|
|
| <a href="https://cjyabraham.github.io/docs/talks/" >Presentations</a>
|
|
</div>
|
|
|
|
|
|
<div class="quickstartcols">
|
|
|
|
<div class="quickstartcol1">
|
|
|
|
<h8>Tutorials</h8>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/async/helloasync-cpp/" >Async - C++</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/auth/oauth2-objective-c/" >Auth - Objective C</a>
|
|
<h8>Basic</h8>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/c/" >C++</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/csharp/" >C#</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/dart/" >Dart</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/go/" >Go</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/java/" >Java</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/android/" >Android Java</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/node/" class="active">Node</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/objective-c/" >Objective-C</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/php/" >PHP</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/python/" >Python</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/ruby/" >Ruby</a>
|
|
<a href="https://cjyabraham.github.io/docs/tutorials/basic/web/" >Web</a>
|
|
|
|
</div>
|
|
|
|
<div class="quickstartcol2" style="margin-top:4%">
|
|
<h3 style="margin-top:0px;">gRPC Basics - Node.js</h3>
|
|
|
|
|
|
|
|
<p>This tutorial provides a basic Node.js programmer’s introduction
|
|
to working with gRPC.</p>
|
|
|
|
<p>By walking through this example you’ll learn how to:</p>
|
|
|
|
<ul>
|
|
<li>Define a service in a .proto file.</li>
|
|
<li>Use the Node.js gRPC API to write a simple client and server for your service.</li>
|
|
</ul>
|
|
|
|
<p>It assumes that you have read the <a href="/docs/">Overview</a> and are familiar
|
|
with <a href="https://developers.google.com/protocol-buffers/docs/overview">protocol
|
|
buffers</a>. Note
|
|
that the example in this tutorial uses the
|
|
<a href="https://github.com/google/protobuf/releases">proto3</a> version of the protocol
|
|
buffers language. You can find out more in the
|
|
<a href="https://developers.google.com/protocol-buffers/docs/proto3">proto3 language guide</a>.</p>
|
|
|
|
<div id="toc"></div>
|
|
|
|
<h3 id="why-use-grpc">Why use gRPC?</h3>
|
|
|
|
<p>Our example is a simple route mapping application that lets clients get
|
|
information about features on their route, create a summary of their route, and
|
|
exchange route information such as traffic updates with the server and other
|
|
clients.</p>
|
|
|
|
<p>With gRPC we can define our service once in a .proto file and implement clients
|
|
and servers in any of gRPC’s supported languages, which in turn can be run in
|
|
environments ranging from servers inside Google to your own tablet - all the
|
|
complexity of communication between different languages and environments is
|
|
handled for you by gRPC. We also get all the advantages of working with protocol
|
|
buffers, including efficient serialization, a simple IDL, and easy interface
|
|
updating.</p>
|
|
|
|
<h3 id="example-code-and-setup">Example code and setup</h3>
|
|
|
|
<p>The example code for our tutorial is in
|
|
<a href="https://github.com/grpc/grpc/tree/
|
|
v1.20.0/examples/node/dynamic_codegen/route_guide">grpc/grpc/examples/node/dynamic_codegen/route_guide</a>.
|
|
As you’ll see if you look at the repository, there’s also a very similar-looking
|
|
example in
|
|
<a href="https://github.com/grpc/grpc/tree/
|
|
v1.20.0/examples/node/static_codegen/route_guide">grpc/grpc/examples/node/static_codegen/route_guide</a>.
|
|
We have two versions of our route guide example because there are two ways to
|
|
generate the code needed to work with protocol buffers in Node.js - one approach
|
|
uses <code>Protobuf.js</code> to dynamically generate the code at runtime, the other uses
|
|
code statically generated using the protocol buffer compiler <code>protoc</code>. The
|
|
examples behave identically, and either server can be used with either client.
|
|
As suggested by the directory name, we’ll be using the version with dynamically
|
|
generated code in this document, but feel free to look at the static code
|
|
example too.</p>
|
|
|
|
<p>To download the example, clone the <code>grpc</code> repository by running the following
|
|
command:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git clone -b v1.20.0 https://github.com/grpc/grpc
|
|
$ cd grpc</code></pre></div>
|
|
<p>Then change your current directory to <code>examples/node</code>:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ cd examples/node</code></pre></div>
|
|
<p>You also should have the relevant tools installed to generate the server and
|
|
client interface code - if you don’t already, follow the setup instructions in
|
|
<a href="/docs/quickstart/node/">the Node.js quick start guide</a>.</p>
|
|
|
|
<h3 id="defining-the-service">Defining the service</h3>
|
|
|
|
<p>Our first step (as you’ll know from the <a href="/docs/">Overview</a>) is to
|
|
define the gRPC <em>service</em> and the method <em>request</em> and <em>response</em> types using
|
|
<a href="https://developers.google.com/protocol-buffers/docs/overview">protocol
|
|
buffers</a>. You can
|
|
see the complete .proto file in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/protos/route_guide.proto"><code>examples/protos/route_guide.proto</code></a>.</p>
|
|
|
|
<p>To define a service, you specify a named <code>service</code> in your .proto file:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#66d9ef">service</span> RouteGuide {<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#f92672">...</span><span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span>}</code></pre></div>
|
|
<p>Then you define <code>rpc</code> methods inside your service definition, specifying their
|
|
request and response types. gRPC lets you define four kinds of service method,
|
|
all of which are used in the <code>RouteGuide</code> service:</p>
|
|
|
|
<ul>
|
|
<li>A <em>simple RPC</em> where the client sends a request to the server using the stub
|
|
and waits for a response to come back, just like a normal function call.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Obtains the feature at a given position.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> GetFeature(Point) <span style="color:#66d9ef">returns</span> (Feature) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>server-side streaming RPC</em> where the client sends a request to the server
|
|
and gets a stream to read a sequence of messages back. The client reads from
|
|
the returned stream until there are no more messages. As you can see in our
|
|
example, you specify a server-side streaming method by placing the <code>stream</code>
|
|
keyword before the <em>response</em> type.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Obtains the Features available within the given Rectangle. Results are
|
|
</span><span style="color:#75715e">// streamed rather than returned at once (e.g. in a response message with a
|
|
</span><span style="color:#75715e">// repeated field), as the rectangle may cover a large area and contain a
|
|
</span><span style="color:#75715e">// huge number of features.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> ListFeatures(Rectangle) <span style="color:#66d9ef">returns</span> (stream Feature) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>client-side streaming RPC</em> where the client writes a sequence of messages
|
|
and sends them to the server, again using a provided stream. Once the client
|
|
has finished writing the messages, it waits for the server to read them all
|
|
and return its response. You specify a client-side streaming method by placing
|
|
the <code>stream</code> keyword before the <em>request</em> type.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Accepts a stream of Points on a route being traversed, returning a
|
|
</span><span style="color:#75715e">// RouteSummary when traversal is completed.
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> RecordRoute(stream Point) <span style="color:#66d9ef">returns</span> (RouteSummary) {}</code></pre></div>
|
|
<ul>
|
|
<li>A <em>bidirectional streaming RPC</em> where both sides send a sequence of messages
|
|
using a read-write stream. The two streams operate independently, so clients
|
|
and servers can read and write in whatever order they like: for example, the
|
|
server could wait to receive all the client messages before writing its
|
|
responses, or it could alternately read a message then write a message, or
|
|
some other combination of reads and writes. The order of messages in each
|
|
stream is preserved. You specify this type of method by placing the <code>stream</code>
|
|
keyword before both the request and the response.</li>
|
|
</ul>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Accepts a stream of RouteNotes sent while a route is being traversed,
|
|
</span><span style="color:#75715e">// while receiving other RouteNotes (e.g. from other users).
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">rpc</span> RouteChat(stream RouteNote) <span style="color:#66d9ef">returns</span> (stream RouteNote) {}</code></pre></div>
|
|
<p>Our .proto file also contains protocol buffer message type definitions for all
|
|
the request and response types used in our service methods - for example, here’s
|
|
the <code>Point</code> message type:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-protobuf" data-lang="protobuf"><span style="color:#75715e">// Points are represented as latitude-longitude pairs in the E7 representation
|
|
</span><span style="color:#75715e">// (degrees multiplied by 10**7 and rounded to the nearest integer).
|
|
</span><span style="color:#75715e">// Latitudes should be in the range +/- 90 degrees and longitude should be in
|
|
</span><span style="color:#75715e">// the range +/- 180 degrees (inclusive).
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">message</span> <span style="color:#a6e22e">Point</span> {<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#66d9ef">int32</span> latitude <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>;<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span> <span style="color:#66d9ef">int32</span> longitude <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>;<span style="color:#960050;background-color:#1e0010">
|
|
</span><span style="color:#960050;background-color:#1e0010"></span>}</code></pre></div>
|
|
<h3 id="loading-service-descriptors-from-proto-files">Loading service descriptors from proto files</h3>
|
|
|
|
<p>The Node.js library dynamically generates service descriptors and client stub
|
|
definitions from <code>.proto</code> files loaded at runtime.</p>
|
|
|
|
<p>To load a <code>.proto</code> file, simply <code>require</code> the gRPC proto loader library and use its
|
|
<code>loadSync()</code> method, then pass the output to the gRPC library’s <code>loadPackageDefinition</code> method:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">PROTO_PATH</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">__dirname</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">'/../../../protos/route_guide.proto'</span>;
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">grpc</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">'grpc'</span>);
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">protoLoader</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">'@grpc/proto-loader'</span>);
|
|
<span style="color:#75715e">// Suggested options for similarity to existing grpc.load behavior
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">packageDefinition</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">protoLoader</span>.<span style="color:#a6e22e">loadSync</span>(
|
|
<span style="color:#a6e22e">PROTO_PATH</span>,
|
|
{<span style="color:#a6e22e">keepCase</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
|
|
<span style="color:#a6e22e">longs</span><span style="color:#f92672">:</span> String,
|
|
<span style="color:#a6e22e">enums</span><span style="color:#f92672">:</span> String,
|
|
<span style="color:#a6e22e">defaults</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
|
|
<span style="color:#a6e22e">oneofs</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>
|
|
});
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">protoDescriptor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">grpc</span>.<span style="color:#a6e22e">loadPackageDefinition</span>(<span style="color:#a6e22e">packageDefinition</span>);
|
|
<span style="color:#75715e">// The protoDescriptor object has the full package hierarchy
|
|
</span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">routeguide</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">protoDescriptor</span>.<span style="color:#a6e22e">routeguide</span>;
|
|
</code></pre></div>
|
|
<p>Once you’ve done this, the stub constructor is in the <code>routeguide</code> namespace
|
|
(<code>protoDescriptor.routeguide.RouteGuide</code>) and the service descriptor (which is
|
|
used to create a server) is a property of the stub
|
|
(<code>protoDescriptor.routeguide.RouteGuide.service</code>);</p>
|
|
|
|
<p><a name="server"></a></p>
|
|
|
|
<h3 id="creating-the-server">Creating the server</h3>
|
|
|
|
<p>First let’s look at how we create a <code>RouteGuide</code> server. If you’re only
|
|
interested in creating gRPC clients, you can skip this section and go straight
|
|
to <a href="#client">Creating the client</a> (though you might find it interesting
|
|
anyway!).</p>
|
|
|
|
<p>There are two parts to making our <code>RouteGuide</code> service do its job:
|
|
- Implementing the service interface generated from our service definition:
|
|
doing the actual “work” of our service.
|
|
- Running a gRPC server to listen for requests from clients and return the
|
|
service responses.</p>
|
|
|
|
<p>You can find our example <code>RouteGuide</code> server in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/node/dynamic_codegen/route_guide/route_guide_server.js">examples/node/dynamic_codegen/route_guide/route_guide_server.js</a>.
|
|
Let’s take a closer look at how it works.</p>
|
|
|
|
<h4 id="implementing-routeguide">Implementing RouteGuide</h4>
|
|
|
|
<p>As you can see, our server has a <code>Server</code> constructor generated from the
|
|
<code>RouteGuide.service</code> descriptor object</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">Server</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">grpc</span>.<span style="color:#a6e22e">Server</span>();
|
|
</code></pre></div>
|
|
<p>In this case we’re implementing the <em>asynchronous</em> version of <code>RouteGuide</code>,
|
|
which provides our default gRPC server behaviour.</p>
|
|
|
|
<p>The functions in <code>route_guide_server.js</code> implement all our service methods.
|
|
Let’s look at the simplest type first, <code>getFeature</code>, which just gets a <code>Point</code>
|
|
from the client and returns the corresponding feature information from its
|
|
database in a <code>Feature</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">checkFeature</span>(<span style="color:#a6e22e">point</span>) {
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">feature</span>;
|
|
<span style="color:#75715e">// Check if there is already a feature object for the given point
|
|
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672"><</span> <span style="color:#a6e22e">feature_list</span>.<span style="color:#a6e22e">length</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
|
|
<span style="color:#a6e22e">feature</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">feature_list</span>[<span style="color:#a6e22e">i</span>];
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span> <span style="color:#f92672">===</span> <span style="color:#a6e22e">point</span>.<span style="color:#a6e22e">latitude</span> <span style="color:#f92672">&&</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span> <span style="color:#f92672">===</span> <span style="color:#a6e22e">point</span>.<span style="color:#a6e22e">longitude</span>) {
|
|
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">feature</span>;
|
|
}
|
|
}
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">''</span>;
|
|
<span style="color:#a6e22e">feature</span> <span style="color:#f92672">=</span> {
|
|
<span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">name</span>,
|
|
<span style="color:#a6e22e">location</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">point</span>
|
|
};
|
|
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">feature</span>;
|
|
}
|
|
<span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getFeature</span>(<span style="color:#a6e22e">call</span>, <span style="color:#a6e22e">callback</span>) {
|
|
<span style="color:#a6e22e">callback</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">checkFeature</span>(<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">request</span>));
|
|
}
|
|
</code></pre></div>
|
|
<p>The method is passed a call object for the RPC, which has the <code>Point</code> parameter
|
|
as a property, and a callback to which we can pass our returned <code>Feature</code>. In
|
|
the method body we populate a <code>Feature</code> corresponding to the given point and
|
|
pass it to the callback, with a null first parameter to indicate that there is
|
|
no error.</p>
|
|
|
|
<p>Now let’s look at something a bit more complicated - a streaming RPC.
|
|
<code>listFeatures</code> is a server-side streaming RPC, so we need to send back multiple
|
|
<code>Feature</code>s to our client.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">listFeatures</span>(<span style="color:#a6e22e">call</span>) {
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">lo</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">lo</span>;
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">hi</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">hi</span>;
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">left</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">min</span>([<span style="color:#a6e22e">lo</span>.<span style="color:#a6e22e">longitude</span>, <span style="color:#a6e22e">hi</span>.<span style="color:#a6e22e">longitude</span>]);
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">right</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">max</span>([<span style="color:#a6e22e">lo</span>.<span style="color:#a6e22e">longitude</span>, <span style="color:#a6e22e">hi</span>.<span style="color:#a6e22e">longitude</span>]);
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">top</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">max</span>([<span style="color:#a6e22e">lo</span>.<span style="color:#a6e22e">latitude</span>, <span style="color:#a6e22e">hi</span>.<span style="color:#a6e22e">latitude</span>]);
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">bottom</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">min</span>([<span style="color:#a6e22e">lo</span>.<span style="color:#a6e22e">latitude</span>, <span style="color:#a6e22e">hi</span>.<span style="color:#a6e22e">latitude</span>]);
|
|
<span style="color:#75715e">// For each feature, check if it is in the given bounding box
|
|
</span><span style="color:#75715e"></span> <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">each</span>(<span style="color:#a6e22e">feature_list</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">feature</span>) {
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">===</span> <span style="color:#e6db74">''</span>) {
|
|
<span style="color:#66d9ef">return</span>;
|
|
}
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span> <span style="color:#f92672">>=</span> <span style="color:#a6e22e">left</span> <span style="color:#f92672">&&</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span> <span style="color:#f92672"><=</span> <span style="color:#a6e22e">right</span> <span style="color:#f92672">&&</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span> <span style="color:#f92672">>=</span> <span style="color:#a6e22e">bottom</span> <span style="color:#f92672">&&</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span> <span style="color:#f92672"><=</span> <span style="color:#a6e22e">top</span>) {
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">write</span>(<span style="color:#a6e22e">feature</span>);
|
|
}
|
|
});
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">end</span>();
|
|
}
|
|
</code></pre></div>
|
|
<p>As you can see, instead of getting the call object and callback in our method
|
|
parameters, this time we get a <code>call</code> object that implements the <code>Writable</code>
|
|
interface. In the method, we create as many <code>Feature</code> objects as we need to
|
|
return, writing them to the <code>call</code> using its <code>write()</code> method. Finally, we call
|
|
<code>call.end()</code> to indicate that we have sent all messages.</p>
|
|
|
|
<p>If you look at the client-side streaming method <code>RecordRoute</code> you’ll see it’s
|
|
quite similar to the unary call, except this time the <code>call</code> parameter
|
|
implements the <code>Reader</code> interface. The <code>call</code>’s <code>'data'</code> event fires every time
|
|
there is new data, and the <code>'end'</code> event fires when all data has been read. Like
|
|
the unary case, we respond by calling the callback</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'data'</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">point</span>) {
|
|
<span style="color:#75715e">// Process user data
|
|
</span><span style="color:#75715e"></span>});
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'end'</span>, <span style="color:#66d9ef">function</span>() {
|
|
<span style="color:#a6e22e">callback</span>(<span style="color:#66d9ef">null</span>, <span style="color:#a6e22e">result</span>);
|
|
});
|
|
</code></pre></div>
|
|
<p>Finally, let’s look at our bidirectional streaming RPC <code>RouteChat()</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">routeChat</span>(<span style="color:#a6e22e">call</span>) {
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'data'</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">note</span>) {
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">key</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">pointKey</span>(<span style="color:#a6e22e">note</span>.<span style="color:#a6e22e">location</span>);
|
|
<span style="color:#75715e">/* For each note sent, respond with all previous notes that correspond to
|
|
</span><span style="color:#75715e"> * the same point */</span>
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">route_notes</span>.<span style="color:#a6e22e">hasOwnProperty</span>(<span style="color:#a6e22e">key</span>)) {
|
|
<span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">each</span>(<span style="color:#a6e22e">route_notes</span>[<span style="color:#a6e22e">key</span>], <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">note</span>) {
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">write</span>(<span style="color:#a6e22e">note</span>);
|
|
});
|
|
} <span style="color:#66d9ef">else</span> {
|
|
<span style="color:#a6e22e">route_notes</span>[<span style="color:#a6e22e">key</span>] <span style="color:#f92672">=</span> [];
|
|
}
|
|
<span style="color:#75715e">// Then add the new note to the list
|
|
</span><span style="color:#75715e"></span> <span style="color:#a6e22e">route_notes</span>[<span style="color:#a6e22e">key</span>].<span style="color:#a6e22e">push</span>(<span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">parse</span>(<span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">stringify</span>(<span style="color:#a6e22e">note</span>)));
|
|
});
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'end'</span>, <span style="color:#66d9ef">function</span>() {
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">end</span>();
|
|
});
|
|
}
|
|
</code></pre></div>
|
|
<p>This time we get a <code>call</code> implementing <code>Duplex</code> that can be used to read <em>and</em>
|
|
write messages. The syntax for reading and writing here is exactly the same as
|
|
for our client-streaming and server-streaming methods. Although each side will
|
|
always get the other’s messages in the order they were written, both the client
|
|
and server can read and write in any order — the streams operate completely
|
|
independently.</p>
|
|
|
|
<h4 id="starting-the-server">Starting the server</h4>
|
|
|
|
<p>Once we’ve implemented all our methods, we also need to start up a gRPC server
|
|
so that clients can actually use our service. The following snippet shows how we
|
|
do this for our <code>RouteGuide</code> service:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getServer</span>() {
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">server</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">grpc</span>.<span style="color:#a6e22e">Server</span>();
|
|
<span style="color:#a6e22e">server</span>.<span style="color:#a6e22e">addProtoService</span>(<span style="color:#a6e22e">routeguide</span>.<span style="color:#a6e22e">RouteGuide</span>.<span style="color:#a6e22e">service</span>, {
|
|
<span style="color:#a6e22e">getFeature</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">getFeature</span>,
|
|
<span style="color:#a6e22e">listFeatures</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">listFeatures</span>,
|
|
<span style="color:#a6e22e">recordRoute</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">recordRoute</span>,
|
|
<span style="color:#a6e22e">routeChat</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">routeChat</span>
|
|
});
|
|
<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">server</span>;
|
|
}
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">routeServer</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">getServer</span>();
|
|
<span style="color:#a6e22e">routeServer</span>.<span style="color:#a6e22e">bind</span>(<span style="color:#e6db74">'0.0.0.0:50051'</span>, <span style="color:#a6e22e">grpc</span>.<span style="color:#a6e22e">ServerCredentials</span>.<span style="color:#a6e22e">createInsecure</span>());
|
|
<span style="color:#a6e22e">routeServer</span>.<span style="color:#a6e22e">start</span>();
|
|
</code></pre></div>
|
|
<p>As you can see, we build and start our server with the following steps:</p>
|
|
|
|
<ol>
|
|
<li>Create a <code>Server</code> constructor from the <code>RouteGuide</code> service descriptor.</li>
|
|
<li>Implement the service methods.</li>
|
|
<li>Create an instance of the server by calling the <code>Server</code> constructor with
|
|
the method implementations.</li>
|
|
<li>Specify the address and port we want to use to listen for client requests
|
|
using the instance’s <code>bind()</code> method.</li>
|
|
<li>Call <code>start()</code> on the instance to start the RPC server.</li>
|
|
</ol>
|
|
|
|
<p><a name="client"></a></p>
|
|
|
|
<h3 id="creating-the-client">Creating the client</h3>
|
|
|
|
<p>In this section, we’ll look at creating a Node.js client for our <code>RouteGuide</code>
|
|
service. You can see our complete example client code in
|
|
<a href="https://github.com/grpc/grpc/blob/
|
|
v1.20.0/examples/node/dynamic_codegen/route_guide/route_guide_client.js">examples/node/dynamic_codegen/route_guide/route_guide_client.js</a>.</p>
|
|
|
|
<h4 id="creating-a-stub">Creating a stub</h4>
|
|
|
|
<p>To call service methods, we first need to create a <em>stub</em>. To do this, we just
|
|
need to call the RouteGuide stub constructor, specifying the server address and
|
|
port.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">new</span> <span style="color:#a6e22e">routeguide</span>.<span style="color:#a6e22e">RouteGuide</span>(<span style="color:#e6db74">'localhost:50051'</span>, <span style="color:#a6e22e">grpc</span>.<span style="color:#a6e22e">credentials</span>.<span style="color:#a6e22e">createInsecure</span>());
|
|
</code></pre></div>
|
|
<h4 id="calling-service-methods">Calling service methods</h4>
|
|
|
|
<p>Now let’s look at how we call our service methods. Note that all of these
|
|
methods are asynchronous: they use either events or callbacks to retrieve
|
|
results.</p>
|
|
|
|
<h5 id="simple-rpc">Simple RPC</h5>
|
|
|
|
<p>Calling the simple RPC <code>GetFeature</code> is nearly as straightforward as calling a
|
|
local asynchronous method.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">point</span> <span style="color:#f92672">=</span> {<span style="color:#a6e22e">latitude</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">409146138</span>, <span style="color:#a6e22e">longitude</span><span style="color:#f92672">:</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">746188906</span>};
|
|
<span style="color:#a6e22e">stub</span>.<span style="color:#a6e22e">getFeature</span>(<span style="color:#a6e22e">point</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">feature</span>) {
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">err</span>) {
|
|
<span style="color:#75715e">// process error
|
|
</span><span style="color:#75715e"></span> } <span style="color:#66d9ef">else</span> {
|
|
<span style="color:#75715e">// process feature
|
|
</span><span style="color:#75715e"></span> }
|
|
});
|
|
</code></pre></div>
|
|
<p>As you can see, we create and populate a request object. Finally, we call the
|
|
method on the stub, passing it the request and callback. If there is no error,
|
|
then we can read the response information from the server from our response
|
|
object.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Found feature called "'</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">'" at '</span> <span style="color:#f92672">+</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">', '</span> <span style="color:#f92672">+</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span>);
|
|
</code></pre></div>
|
|
<h5 id="streaming-rpcs">Streaming RPCs</h5>
|
|
|
|
<p>Now let’s look at our streaming methods. If you’ve already read <a href="#server">Creating the
|
|
server</a> some of this may look very familiar - streaming RPCs are
|
|
implemented in a similar way on both sides. Here’s where we call the server-side
|
|
streaming method <code>ListFeatures</code>, which returns a stream of geographical
|
|
<code>Feature</code>s:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">call</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">listFeatures</span>(<span style="color:#a6e22e">rectangle</span>);
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'data'</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">feature</span>) {
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Found feature called "'</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">name</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">'" at '</span> <span style="color:#f92672">+</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">', '</span> <span style="color:#f92672">+</span>
|
|
<span style="color:#a6e22e">feature</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span>);
|
|
});
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'end'</span>, <span style="color:#66d9ef">function</span>() {
|
|
<span style="color:#75715e">// The server has finished sending
|
|
</span><span style="color:#75715e"></span> });
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'error'</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">e</span>) {
|
|
<span style="color:#75715e">// An error has occurred and the stream has been closed.
|
|
</span><span style="color:#75715e"></span> });
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">on</span>(<span style="color:#e6db74">'status'</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">status</span>) {
|
|
<span style="color:#75715e">// process status
|
|
</span><span style="color:#75715e"></span> });
|
|
</code></pre></div>
|
|
<p>Instead of passing the method a request and callback, we pass it a request and
|
|
get a <code>Readable</code> stream object back. The client can use the <code>Readable</code>’s
|
|
<code>'data'</code> event to read the server’s responses. This event fires with each
|
|
<code>Feature</code> message object until there are no more messages. Errors in the <code>'data'</code>
|
|
callback will not cause the stream to be closed. The <code>'error'</code> event
|
|
indicates that an error has occurred and the stream has been closed. The
|
|
<code>'end'</code> event indicates that the server has finished sending and no errors
|
|
occured. Only one of <code>'error'</code> or <code>'end'</code> will be emitted. Finally, the
|
|
<code>'status'</code> event fires when the server sends the status.</p>
|
|
|
|
<p>The client-side streaming method <code>RecordRoute</code> is similar, except there we pass
|
|
the method a callback and get back a <code>Writable</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">call</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">recordRoute</span>(<span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">error</span>, <span style="color:#a6e22e">stats</span>) {
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">error</span>) {
|
|
<span style="color:#a6e22e">callback</span>(<span style="color:#a6e22e">error</span>);
|
|
}
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Finished trip with'</span>, <span style="color:#a6e22e">stats</span>.<span style="color:#a6e22e">point_count</span>, <span style="color:#e6db74">'points'</span>);
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Passed'</span>, <span style="color:#a6e22e">stats</span>.<span style="color:#a6e22e">feature_count</span>, <span style="color:#e6db74">'features'</span>);
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Travelled'</span>, <span style="color:#a6e22e">stats</span>.<span style="color:#a6e22e">distance</span>, <span style="color:#e6db74">'meters'</span>);
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'It took'</span>, <span style="color:#a6e22e">stats</span>.<span style="color:#a6e22e">elapsed_time</span>, <span style="color:#e6db74">'seconds'</span>);
|
|
});
|
|
<span style="color:#66d9ef">function</span> <span style="color:#a6e22e">pointSender</span>(<span style="color:#a6e22e">lat</span>, <span style="color:#a6e22e">lng</span>) {
|
|
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">callback</span>) {
|
|
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'Visiting point '</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">lat</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">', '</span> <span style="color:#f92672">+</span>
|
|
<span style="color:#a6e22e">lng</span><span style="color:#f92672">/</span><span style="color:#a6e22e">COORD_FACTOR</span>);
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">write</span>({
|
|
<span style="color:#a6e22e">latitude</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">lat</span>,
|
|
<span style="color:#a6e22e">longitude</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">lng</span>
|
|
});
|
|
<span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">delay</span>(<span style="color:#a6e22e">callback</span>, <span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">random</span>(<span style="color:#ae81ff">500</span>, <span style="color:#ae81ff">1500</span>));
|
|
};
|
|
}
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">point_senders</span> <span style="color:#f92672">=</span> [];
|
|
<span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672"><</span> <span style="color:#a6e22e">num_points</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>) {
|
|
<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">rand_point</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">feature_list</span>[<span style="color:#a6e22e">_</span>.<span style="color:#a6e22e">random</span>(<span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">feature_list</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)];
|
|
<span style="color:#a6e22e">point_senders</span>[<span style="color:#a6e22e">i</span>] <span style="color:#f92672">=</span> <span style="color:#a6e22e">pointSender</span>(<span style="color:#a6e22e">rand_point</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">latitude</span>,
|
|
<span style="color:#a6e22e">rand_point</span>.<span style="color:#a6e22e">location</span>.<span style="color:#a6e22e">longitude</span>);
|
|
}
|
|
<span style="color:#66d9ef">async</span>.<span style="color:#a6e22e">series</span>(<span style="color:#a6e22e">point_senders</span>, <span style="color:#66d9ef">function</span>() {
|
|
<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">end</span>();
|
|
});
|
|
</code></pre></div>
|
|
<p>Once we’ve finished writing our client’s requests to the stream using <code>write()</code>,
|
|
we need to call <code>end()</code> on the stream to let gRPC know that we’ve finished
|
|
writing. If the status is <code>OK</code>, the <code>stats</code> object will be populated with the
|
|
server’s response.</p>
|
|
|
|
<p>Finally, let’s look at our bidirectional streaming RPC <code>routeChat()</code>. In this
|
|
case, we just pass a context to the method and get back a <code>Duplex</code> stream
|
|
object, which we can use to both write and read messages.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-js" data-lang="js"><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">call</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">routeChat</span>();
|
|
</code></pre></div>
|
|
<p>The syntax for reading and writing here is exactly the same as for our
|
|
client-streaming and server-streaming methods. Although each side will always
|
|
get the other’s messages in the order they were written, both the client and
|
|
server can read and write in any order — the streams operate completely
|
|
independently.</p>
|
|
|
|
<h3 id="try-it-out">Try it out!</h3>
|
|
|
|
<p>Build client and server:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ npm install</code></pre></div>
|
|
<p>Run the server, which will listen on port 50051:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ node ./dynamic_codegen/route_guide/route_guide_server.js --db_path<span style="color:#f92672">=</span>./dynamic_codegen/route_guide/route_guide_db.json</code></pre></div>
|
|
<p>Run the client (in a different terminal):</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ node ./dynamic_codegen/route_guide/route_guide_client.js --db_path<span style="color:#f92672">=</span>./dynamic_codegen/route_guide/route_guide_db.json</code></pre></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
|
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
|
|
|
|
</body>
|
|
</html>
|