mirror of https://github.com/grpc/grpc.io.git
641 lines
43 KiB
HTML
641 lines
43 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 - Dart – 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/" class="active">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/" >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 - Dart</h3>
|
|
|
|
|
|
|
|
<p>This tutorial provides a basic Dart 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>Generate server and client code using the protocol buffer compiler.</li>
|
|
<li>Use the Dart 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 proto3 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-dart/tree/master/example/route_guide">grpc/grpc-dart/example/route_guide</a>.
|
|
To download the example, clone the <code>grpc-dart</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 https://github.com/grpc/grpc-dart.git</code></pre></div>
|
|
<p>Then change your current directory to <code>grpc-dart/example/route_guide</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 grpc-dart/example/route_guide</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/dart/">the Dart 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-dart/blob/master/example/route_guide/protos/route_guide.proto"><code>example/route_guide/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-proto" data-lang="proto"><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-proto" data-lang="proto"><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-proto" data-lang="proto"><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-proto" data-lang="proto"><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-proto" data-lang="proto"><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-proto" data-lang="proto"><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="generating-client-and-server-code">Generating client and server code</h3>
|
|
|
|
<p>Next we need to generate the gRPC client and server interfaces from our .proto
|
|
service definition. We do this using the protocol buffer compiler <code>protoc</code> with
|
|
a special Dart plugin.
|
|
This is similar to what we did in the <a href="/docs/quickstart/">quickstart guide</a></p>
|
|
|
|
<p>From the <code>route_guide</code> example directory run :</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"> protoc -I protos/ protos/route_guide.proto --dart_out<span style="color:#f92672">=</span>grpc:lib/src/generated</code></pre></div>
|
|
<p>Running this command generates the following files in the <code>lib/src/generated</code>
|
|
directory under the <code>route_guide</code> example directory:</p>
|
|
|
|
<ul>
|
|
<li><code>route_guide.pb.dart</code></li>
|
|
<li><code>route_guide.pbenum.dart</code></li>
|
|
<li><code>route_guide.pbgrpc.dart</code></li>
|
|
<li><code>route_guide.pbjson.dart</code></li>
|
|
</ul>
|
|
|
|
<p>This contains:</p>
|
|
|
|
<ul>
|
|
<li>All the protocol buffer code to populate, serialize, and retrieve our request
|
|
and response message types</li>
|
|
<li>An interface type (or <em>stub</em>) for clients to call with the methods defined in
|
|
the <code>RouteGuide</code> service.</li>
|
|
<li>An interface type for servers to implement, also with the methods defined in
|
|
the <code>RouteGuide</code> service.</li>
|
|
</ul>
|
|
|
|
<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:</p>
|
|
|
|
<ul>
|
|
<li>Implementing the service interface generated from our service definition:
|
|
doing the actual “work” of our service.</li>
|
|
<li>Running a gRPC server to listen for requests from clients and dispatch them to
|
|
the right service implementation.</li>
|
|
</ul>
|
|
|
|
<p>You can find our example <code>RouteGuide</code> server in
|
|
<a href="https://github.com/grpc/grpc-dart/tree/master/example/route_guide/lib/src/server.dart">grpc-dart/example/route_guide/lib/src/server.dart</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>RouteGuideService</code> class that extends the
|
|
generated abstract <code>RouteGuideServiceBase</code> class:</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-dart" data-lang="dart"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">RouteGuideService</span> <span style="color:#66d9ef">extends</span> RouteGuideServiceBase {
|
|
Future<span style="color:#f92672"><</span>Feature<span style="color:#f92672">></span> getFeature(grpc.ServiceCall call, Point request) <span style="color:#66d9ef">async</span> {
|
|
...
|
|
}
|
|
|
|
Stream<span style="color:#f92672"><</span>Feature<span style="color:#f92672">></span> listFeatures(
|
|
grpc.ServiceCall call, Rectangle request) <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
|
|
...
|
|
}
|
|
|
|
Future<span style="color:#f92672"><</span>RouteSummary<span style="color:#f92672">></span> recordRoute(
|
|
grpc.ServiceCall call, Stream<span style="color:#f92672"><</span>Point<span style="color:#f92672">></span> request) <span style="color:#66d9ef">async</span> {
|
|
...
|
|
}
|
|
|
|
Stream<span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span> routeChat(
|
|
grpc.ServiceCall call, Stream<span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span> request) <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
|
|
...
|
|
}
|
|
|
|
...
|
|
}</code></pre></div>
|
|
<h5 id="simple-rpc">Simple RPC</h5>
|
|
|
|
<p><code>RouteGuideService</code> implements 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-dart" data-lang="dart"><span style="color:#75715e">/// GetFeature handler. Returns a feature for the given location.
|
|
</span><span style="color:#75715e">/// The [context] object provides access to client metadata, cancellation, etc.
|
|
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">@</span>override
|
|
Future<span style="color:#f92672"><</span>Feature<span style="color:#f92672">></span> getFeature(grpc.ServiceCall call, Point request) <span style="color:#66d9ef">async</span> {
|
|
<span style="color:#66d9ef">return</span> featuresDb.firstWhere((f) <span style="color:#f92672">=></span> f.location <span style="color:#f92672">==</span> request,
|
|
orElse: () <span style="color:#f92672">=></span> <span style="color:#66d9ef">new</span> Feature()..location <span style="color:#f92672">=</span> request);
|
|
}</code></pre></div>
|
|
<p>The method is passed a context object for the RPC and the client’s <code>Point</code>
|
|
protocol buffer request. It returns a <code>Feature</code> protocol buffer object with the
|
|
response information. In the method we populate the <code>Feature</code> with the appropriate
|
|
information, and then <code>return</code> it to the gRPC framework, which sends it back to
|
|
the client.</p>
|
|
|
|
<h5 id="server-side-streaming-rpc">Server-side streaming RPC</h5>
|
|
|
|
<p>Now let’s look at one of our streaming RPCs. <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-dart" data-lang="dart"><span style="color:#75715e">/// ListFeatures handler. Returns a stream of features within the given
|
|
</span><span style="color:#75715e">/// rectangle.
|
|
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">@</span>override
|
|
Stream<span style="color:#f92672"><</span>Feature<span style="color:#f92672">></span> listFeatures(
|
|
grpc.ServiceCall call, Rectangle request) <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
|
|
<span style="color:#66d9ef">final</span> normalizedRectangle <span style="color:#f92672">=</span> _normalize(request);
|
|
<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:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> feature <span style="color:#66d9ef">in</span> featuresDb) {
|
|
<span style="color:#66d9ef">if</span> (feature.name.isEmpty) <span style="color:#66d9ef">continue</span>;
|
|
<span style="color:#66d9ef">final</span> location <span style="color:#f92672">=</span> feature.location;
|
|
<span style="color:#66d9ef">if</span> (_contains(normalizedRectangle, location)) {
|
|
<span style="color:#66d9ef">yield</span> feature;
|
|
}
|
|
}
|
|
}</code></pre></div>
|
|
<p>As you can see, instead of getting and returning simple request and response
|
|
objects in our method, this time we get a request object (the <code>Rectangle</code> in
|
|
which our client wants to find <code>Feature</code>s) and return a <code>Stream</code> of <code>Feature</code>
|
|
objects.</p>
|
|
|
|
<p>In the method, we populate as many <code>Feature</code> objects as we need to return,
|
|
adding them to the returned stream using <code>yield</code>. The stream is automatically
|
|
closed when the method returns, telling gRPC that we have finished writing
|
|
responses.</p>
|
|
|
|
<p>Should any error happen in this call, the error will be added as an exception
|
|
to the stream, and the gRPC layer will translate it into an appropriate RPC
|
|
status to be sent on the wire.</p>
|
|
|
|
<h5 id="client-side-streaming-rpc">Client-side streaming RPC</h5>
|
|
|
|
<p>Now let’s look at something a little more complicated: the client-side
|
|
streaming method <code>RecordRoute</code>, where we get a stream of <code>Point</code>s from the
|
|
client and return a single <code>RouteSummary</code> with information about their trip. As
|
|
you can see, this time the request parameter is a stream, which the server can
|
|
use to both read request messages from the client. The server returns its single
|
|
response just like in the simple RPC case.</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-dart" data-lang="dart"><span style="color:#75715e">/// RecordRoute handler. Gets a stream of points, and responds with statistics
|
|
</span><span style="color:#75715e">/// about the "trip": number of points, number of known features visited,
|
|
</span><span style="color:#75715e">/// total distance traveled, and total time spent.
|
|
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">@</span>override
|
|
Future<span style="color:#f92672"><</span>RouteSummary<span style="color:#f92672">></span> recordRoute(
|
|
grpc.ServiceCall call, Stream<span style="color:#f92672"><</span>Point<span style="color:#f92672">></span> request) <span style="color:#66d9ef">async</span> {
|
|
<span style="color:#66d9ef">int</span> pointCount <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
|
|
<span style="color:#66d9ef">int</span> featureCount <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
|
|
<span style="color:#66d9ef">double</span> distance <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>;
|
|
Point previous;
|
|
<span style="color:#66d9ef">final</span> timer <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Stopwatch();
|
|
|
|
<span style="color:#66d9ef">await</span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> location <span style="color:#66d9ef">in</span> request) {
|
|
<span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>timer.isRunning) timer.start();
|
|
pointCount<span style="color:#f92672">++</span>;
|
|
<span style="color:#66d9ef">final</span> feature <span style="color:#f92672">=</span> featuresDb.firstWhere((f) <span style="color:#f92672">=></span> f.location <span style="color:#f92672">==</span> location,
|
|
orElse: () <span style="color:#f92672">=></span> <span style="color:#66d9ef">null</span>);
|
|
<span style="color:#66d9ef">if</span> (feature <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span>) {
|
|
featureCount<span style="color:#f92672">++</span>;
|
|
}
|
|
<span style="color:#75715e">// For each point after the first, add the incremental distance from the
|
|
</span><span style="color:#75715e"></span> <span style="color:#75715e">// previous point to the total distance value.
|
|
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (previous <span style="color:#f92672">!=</span> <span style="color:#66d9ef">null</span>) distance <span style="color:#f92672">+=</span> _distance(previous, location);
|
|
previous <span style="color:#f92672">=</span> location;
|
|
}
|
|
timer.stop();
|
|
<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">new</span> RouteSummary()
|
|
..pointCount <span style="color:#f92672">=</span> pointCount
|
|
..featureCount <span style="color:#f92672">=</span> featureCount
|
|
..distance <span style="color:#f92672">=</span> distance.round()
|
|
..elapsedTime <span style="color:#f92672">=</span> timer.elapsed.inSeconds;
|
|
}</code></pre></div>
|
|
<p>In the method body we use <code>await for</code> in the request stream to repeatedly read
|
|
in our client’s requests (in this case <code>Point</code> objects) until there are no more
|
|
messages. Once the request stream is done, the server can return its
|
|
<code>RouteSummary</code>.</p>
|
|
|
|
<h5 id="bidirectional-streaming-rpc">Bidirectional streaming RPC</h5>
|
|
|
|
<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-dart" data-lang="dart"><span style="color:#75715e">/// RouteChat handler. Receives a stream of message/location pairs, and
|
|
</span><span style="color:#75715e">/// responds with a stream of all previous messages at each of those
|
|
</span><span style="color:#75715e">/// locations.
|
|
</span><span style="color:#75715e"></span><span style="color:#960050;background-color:#1e0010">@</span>override
|
|
Stream<span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span> routeChat(
|
|
grpc.ServiceCall call, Stream<span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span> request) <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
|
|
<span style="color:#66d9ef">await</span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> note <span style="color:#66d9ef">in</span> request) {
|
|
<span style="color:#66d9ef">final</span> notes <span style="color:#f92672">=</span> routeNotes.putIfAbsent(note.location, () <span style="color:#f92672">=></span> <span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span>[]);
|
|
<span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> note <span style="color:#66d9ef">in</span> notes) <span style="color:#66d9ef">yield</span> note;
|
|
notes.add(note);
|
|
}
|
|
}</code></pre></div>
|
|
<p>This time we get a stream of <code>RouteNote</code> that, as in our client-side streaming
|
|
example, can be used to read messages. However, this time we return values via
|
|
our method’s returned stream while the client is still writing messages to
|
|
<em>their</em> message stream.</p>
|
|
|
|
<p>The syntax for reading and writing here is the same as 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-dart" data-lang="dart">Future<span style="color:#f92672"><</span>Null<span style="color:#f92672">></span> main(List<span style="color:#f92672"><</span><span style="color:#66d9ef">String</span><span style="color:#f92672">></span> args) <span style="color:#66d9ef">async</span> {
|
|
<span style="color:#66d9ef">final</span> server <span style="color:#f92672">=</span>
|
|
<span style="color:#66d9ef">new</span> grpc.Server([<span style="color:#66d9ef">new</span> RouteGuideService()]);
|
|
<span style="color:#66d9ef">await</span> server.serve(port: <span style="color:#ae81ff">8080</span>);
|
|
print(<span style="color:#e6db74">'Server listening...'</span>);
|
|
}</code></pre></div>
|
|
<p>To build and start a server, we:</p>
|
|
|
|
<ol>
|
|
<li>Create an instance of the gRPC server using <code>new grpc.Server()</code>,
|
|
giving a list of service implementations.</li>
|
|
<li>Call <code>serve()</code> on the server to start listening for requests, optionally passing
|
|
in the address and port to listen on. The server will continue to serve requests
|
|
asynchronously until <code>shutdown()</code> is called on it.</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 Dart client for our <code>RouteGuide</code>
|
|
service. You can see our complete example client code in
|
|
<a href="https://github.com/grpc/grpc-dart/tree/master/example/route_guide/lib/src/client.dart">grpc-dart/example/route_guide/lib/src/client.dart</a>.</p>
|
|
|
|
<h4 id="creating-a-stub">Creating a stub</h4>
|
|
|
|
<p>To call service methods, we first need to create a gRPC <em>channel</em> to communicate
|
|
with the server. We create this by passing the server address and port number to
|
|
<code>new ClientChannel()</code> as follows:</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-dart" data-lang="dart"><span style="color:#66d9ef">final</span> channel <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> ClientChannel(<span style="color:#e6db74">'127.0.0.1'</span>,
|
|
port: <span style="color:#ae81ff">8080</span>,
|
|
options: <span style="color:#66d9ef">const</span> ChannelOptions(
|
|
credentials: <span style="color:#66d9ef">const</span> ChannelCredentials.insecure()));</code></pre></div>
|
|
<p>You can use <code>ChannelOptions</code> to set TLS options (e.g., trusted certificates) for
|
|
the channel, if necessary.</p>
|
|
|
|
<p>Once the gRPC <em>channel</em> is setup, we need a client <em>stub</em> to perform RPCs. We
|
|
get by creating a new instance of the <code>RouteGuideClient</code> object provided in the
|
|
package we generated from our .proto.</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-dart" data-lang="dart"><span style="color:#66d9ef">final</span> client <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> RouteGuideClient(channel,
|
|
options: <span style="color:#66d9ef">new</span> CallOptions(timeout: <span style="color:#66d9ef">new</span> Duration(seconds: <span style="color:#ae81ff">30</span>)));</code></pre></div>
|
|
<p>You can use <code>CallOptions</code> to set the auth credentials (e.g., GCE credentials,
|
|
JWT credentials) if the service you request requires that - however, we don’t
|
|
need to do this for our <code>RouteGuide</code> service.</p>
|
|
|
|
<h4 id="calling-service-methods">Calling service methods</h4>
|
|
|
|
<p>Now let’s look at how we call our service methods. Note that in gRPC-Dart, RPCs
|
|
are always asynchronous, which means that the RPC returns a <code>Future</code> or <code>Stream</code>
|
|
that must be listened to, to get the response from the server or an error.</p>
|
|
|
|
<h5 id="simple-rpc-1">Simple RPC</h5>
|
|
|
|
<p>Calling the simple RPC <code>GetFeature</code> is nearly as straightforward as calling a
|
|
local 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-dart" data-lang="dart"><span style="color:#66d9ef">final</span> point <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Point()
|
|
..latitude <span style="color:#f92672">=</span> <span style="color:#ae81ff">409146138</span>
|
|
..longitude <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">746188906</span>;
|
|
<span style="color:#66d9ef">final</span> feature <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> stub.getFeature(point));</code></pre></div>
|
|
<p>As you can see, we call the method on the stub we got earlier. In our method
|
|
parameters we pass a request protocol buffer object (in our case <code>Point</code>).
|
|
We can also pass an optional <code>CallOptions</code> object which lets us change our RPC’s
|
|
behaviour if necessary, such as time-out. If the call doesn’t return an error,
|
|
the returned <code>Future</code> completes with the response information from the server.
|
|
If there is an error, the <code>Future</code> will complete with the error.</p>
|
|
|
|
<h5 id="server-side-streaming-rpc-1">Server-side streaming RPC</h5>
|
|
|
|
<p>Here’s where we call the server-side streaming method <code>ListFeatures</code>, which
|
|
returns a stream of geographical <code>Feature</code>s. 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.</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-dart" data-lang="dart"><span style="color:#66d9ef">final</span> rect <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Rectangle()...; <span style="color:#75715e">// initialize a Rectangle
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#66d9ef">try</span> {
|
|
<span style="color:#66d9ef">await</span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> feature <span style="color:#66d9ef">in</span> stub.listFeatures(rect)) {
|
|
print(feature);
|
|
}
|
|
<span style="color:#66d9ef">catch</span> (e) {
|
|
print(<span style="color:#e6db74">'ERROR: </span><span style="color:#e6db74">$</span>e<span style="color:#e6db74">'</span>);
|
|
}</code></pre></div>
|
|
<p>As in the simple RPC, we pass the method a request. However, instead of getting
|
|
a <code>Future</code> back, we get a <code>Stream</code>. The client can use the stream to read the
|
|
server’s responses.</p>
|
|
|
|
<p>We use <code>await for</code> on the returned stream to repeatedly read in the server’s
|
|
responses to a response protocol buffer object (in this case a <code>Feature</code>) until
|
|
there are no more messages.</p>
|
|
|
|
<h5 id="client-side-streaming-rpc-1">Client-side streaming RPC</h5>
|
|
|
|
<p>The client-side streaming method <code>RecordRoute</code> is similar to the server-side
|
|
method, except that we pass the method a <code>Stream</code> and get a <code>Future</code> back.</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-dart" data-lang="dart"><span style="color:#66d9ef">final</span> random <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> Random();
|
|
|
|
<span style="color:#75715e">// Generate a number of random points
|
|
</span><span style="color:#75715e"></span>Stream<span style="color:#f92672"><</span>Point<span style="color:#f92672">></span> generateRoute(<span style="color:#66d9ef">int</span> count) <span style="color:#66d9ef">async</span><span style="color:#f92672">*</span> {
|
|
<span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; i <span style="color:#f92672"><</span> count; i<span style="color:#f92672">++</span>) {
|
|
<span style="color:#66d9ef">final</span> point <span style="color:#f92672">=</span> featuresDb[random.nextInt(featuresDb.length)].location;
|
|
<span style="color:#66d9ef">yield</span> point;
|
|
}
|
|
}
|
|
|
|
<span style="color:#66d9ef">final</span> pointCount <span style="color:#f92672">=</span> random.nextInt(<span style="color:#ae81ff">100</span>) <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span>; <span style="color:#75715e">// Traverse at least two points
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#66d9ef">final</span> summary <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> stub.recordRoute(generateRoute(pointCount));
|
|
print(<span style="color:#e6db74">'Route summary: </span><span style="color:#e6db74">$</span>summary<span style="color:#e6db74">'</span>);</code></pre></div>
|
|
<p>Since the <code>generateRoute()</code> method is <code>async*</code>, the points will be generated when
|
|
gRPC listens to the request stream and sends the point messages to the server. Once
|
|
the stream is done (when <code>generateRoute()</code> returns), gRPC knows that we’ve finished
|
|
writing and are expecting to receive a response. The returned <code>Future</code> will either
|
|
complete with the <code>RouteSummary</code> message received from the server, or an error.</p>
|
|
|
|
<h5 id="bidirectional-streaming-rpc-1">Bidirectional streaming RPC</h5>
|
|
|
|
<p>Finally, let’s look at our bidirectional streaming RPC <code>RouteChat()</code>. As in the
|
|
case of <code>RecordRoute</code>, we pass the method a stream where we will write the request
|
|
messages, and like in <code>ListFeatures</code>, we get back a stream that we can use to read
|
|
the response messages. However, this time we will send values via our method’s stream
|
|
while the server is also writing messages to <em>their</em> message stream.</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-dart" data-lang="dart">Stream<span style="color:#f92672"><</span>RouteNote<span style="color:#f92672">></span> outgoingNotes <span style="color:#f92672">=</span> ...;
|
|
|
|
<span style="color:#66d9ef">final</span> responses <span style="color:#f92672">=</span> stub.routeChat(outgoingNotes);
|
|
<span style="color:#66d9ef">await</span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">var</span> note <span style="color:#66d9ef">in</span> responses) {
|
|
print(<span style="color:#e6db74">'Got message </span><span style="color:#e6db74">${</span>note.message<span style="color:#e6db74">}</span><span style="color:#e6db74"> at </span><span style="color:#e6db74">${</span>note.location.latitude<span style="color:#e6db74">}</span><span style="color:#e6db74">, </span><span style="color:#e6db74">${</span>note
|
|
.location.longitude<span style="color:#e6db74">}</span><span style="color:#e6db74">'</span>);
|
|
}</code></pre></div>
|
|
<p>The syntax for reading and writing here is very similar to our client-side and
|
|
server-side 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>Go to the <code>examples/route_guide</code> folder.</p>
|
|
|
|
<p>First, make sure dependencies are downloaded:</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">$ pub get</code></pre></div>
|
|
<p>To run the server, simply:</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">$ dart bin/server.dart</code></pre></div>
|
|
<p>Likewise, to run the 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-sh" data-lang="sh">$ dart bin/client.dart</code></pre></div>
|
|
<h3 id="reporting-issues">Reporting issues</h3>
|
|
|
|
<p>Should you encounter an issue, please help us out by
|
|
<a href="https://github.com/grpc/grpc-dart/issues/new">filing issues</a>
|
|
in our issue tracker.</p></p>
|
|
|
|
</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>
|