[Dart翻译]在Dart中构建gRPC服务器和客户端

1,366 阅读3分钟

原文地址:dev.to/caelinsutch…

原文作者:dev.to/caelinsutch…

发布时间:2020年5月21日 ・6分钟阅读

将Dart或Flutter与GRPC集成。

gRPC是一个现代的、开源的、高性能的RPC框架,它是语言不可知的,这意味着它可以与各种平台一起工作,以满足各种用例的需要。这意味着你可以在.proto文件中定义你的数据结构和数据访问器。

该项目

在本文的最后,你将会在Dart中使用gRPC构建一个基本的todos api。该API将有三个独立的服务调用,createTodos创建一个todo项目,readTodos获得所有的当前todos,和readTodosStream将返回一个流,认为像一个套接字,todos得到不断更新。

GRPC与Flutter

如果你正在为你的Flutter应用程序使用gRPC,你很幸运,因为gRPC有一个支持Dart的库!这意味着你可以使用gRPC协议制作客户端和服务器端代码,并在Dart语言中生成缓冲区。这意味着你可以使用gRPC协议制作客户端和服务器端代码,并在Dart语言中生成缓冲区。在这篇文章中,我们将在Dart中使用gRPC构建一个服务器和客户端,但将暂缓与Flutter的集成(虽然应该很清楚如何做)。

在Dart中构建一个gRPC服务器

首先你要做的是安装Dart SDK。这是独立于Flutter SDK的,可以按照本指南进行安装)。)

设置你的Dart工作空间

一旦你安装了Dart,你就需要创建一个基本的Dart工作区。

mkdir dart-grpc-tutorial
cd dart-grpc-tutorial && mkdir lib
touch pubspec.yaml
touch lib/server.dart

让我们用正确的信息更新 pubspec.yaml。

name: grpc_tutorial
version: 0.0.1
environment:
    sdk: ">=2.1.0 <3.0.0"
dependencies:
    grpc: 2.1.3
    protobuf: 1.0.1

然后运行pub get来安装这两个依赖项。

现在还记得我说的protobuff文件吗?我们要做一个自定义的protobuff文件。 并为我们自己的Dart编译它! 这个proto文件将保存我们的API的定义。

安装Proto编译器

你必须安装proto编译器和dart协议缓冲区插件来使用gRPC。

  1. 如果你还没有安装编译器,请下载软件包并按照 README 中的说明进行安装。
  2. 按照 README 中的说明安装 Dart 协议缓冲区插件。可执行文件bin/protoc-gen-dart必须在你的PATH中,以便协议缓冲区protoc找到它。

现在让我们创建一个文件夹来存放protobuff文件和生成的dart代码,并创建我们的todo.proto文件。

mkdir protos && touch protos/todo.proto
mkdir lib/src && mkdir lib/src/generated

制作您的Proto文件

打开todo.proto文件,让我们用我们的消息类型更新它。 syntax = "proto3"。

package todoPackage.proto文件,让我们更新我们的消息类型。

syntax = "proto3";

package todoPackage;

service Todo {
    rpc createTodo(TodoItem) returns (TodoItem);
    rpc readTodos(voidNoParam) returns (TodoItems);
    rpc readTodosStream(voidNoParam) returns (stream TodoItem);
}

message voidNoParam {}

message TodoItem {
    int32 id = 1;
    string text = 2;
}

message TodoItems {
    repeated TodoItem items = 1;
}

哇,代码真多。让我们一行一行地看,看看这个东西是干什么的。

首先我们要声明我们的proto语法,也就是proto3

syntax = "proto3";

接下来,我们声明我们的包

package todoPackage;

这是它变得有趣的地方。我们在这里声明我们实际的TodoService。你可以把这个服务想象成一个API规范,它列出了API的不同 "端点"。

service Todo {
    rpc createTodo(TodoItem) returns (TodoItem);                    // Add a todo
    rpc readTodos(voidNoParam) returns (TodoItems);                 // Get all todos
    rpc readTodosStream(voidNoParam) returns (stream TodoItem);     // Get all todos as a stream
}

你会注意到我们这里有一些类型,让我们把这些类型分解开来。

rpc createTodo(TodoItem) returns (TodoItem);

前面的RPC将其声明为一个rpc端点。声明了端点的名称createTodo,以及输入参数,也就是一个TodoItem。TodoItem在这段代码的底部被声明为消息类型,我们很快就会介绍。最后returns(TodoItem)告诉我们函数的返回类型,也是一个TodoItem。

rpc readTodos(voidNoParam) returns (TodoItems);

这个很有趣,因为你会看到我们传入了一个叫做voidNoParam的类型。在 gRPC 中,你不能有一个空的参数输入,所以我们传入这个类型,它只是一个空的对象,以防止错误。

rpc readTodosStream(voidNoParam) returns (stream TodoItem);

最后,这个返回一个TodoItem的流。来自Dart背景的你应该已经知道流是如何表现的,作为一个不断更新的信息集。

然后在下面声明消息类型。请注意,每个消息类型的每个参数都用一个类型来声明。

message TodoItem {
    int32 id = 1;
    string text = 2;
}

后面的数字告诉gRPC期望这些参数的顺序。在TodoItems中看到的重复类型只是意味着一个数组(或列表)或TodoItems。

编译你的Proto文件

现在,我们将通过运行以下命令将原文件编译成可用的Dart代码。

protoc -I protos/ protos/todo.proto --dart_out=grpc:lib/src/generated

如果一切正常,你应该会看到你生成的文件夹中充满了一些代码文件。

编写一个简单的服务器

我已经注释了这段代码的每个部分的作用,但本质上我们是用proto文件生成的代码来创建TodoService。我们创建一个类TodoServer来处理使用TodoService创建和实现gRPC服务器。这就是拥有proto文件的优势之一,它们提供了易于扩展的抽象类来实现方法。

import 'package:grpc/grpc.dart';  // Import GRPC
import 'package:grpc/src/server/call.dart';   // Import GRPC Server
import 'package:grpc_tutorial/src/generated/todo.pbgrpc.dart';  // Import the protofbuff files

class TodoService extends TodoServiceBase { 
     TodoItems todos = new TodoItems(); // TodoItems is what's generated by Proto

     /**
     * Createa  new Todo
     * @param call - meta information on the request
     * @param request - information that's sent
     * @returns todoItem - The item that was added
     **/
     @override  
    Future<TodoItem> createTodo(ServiceCall call, TodoItem request) async {
        // Create a new TodoObject with the data  
        TodoItem todoItem = new TodoItem();  
        todoItem.text = request.text;  
        todoItem.id = todos.$_getList(1).length +1;
        // Add a new todo Item to our list  
        todos.items.add(todoItem);  
        // Return a todoItem in accordance with function return typ[e
        return todoItem;  
    }  
    /**
    * Read the todos as a Future
    **/
    @override  
    Future<TodoItems> readTodos(ServiceCall call, voidNoParam request) async {  
         return todos;  
    }  

     /**
     * Get all of the todos as a stream
     **/
    @override  
    Stream<TodoItem> readTodosStream(ServiceCall call, voidNoParam request) async* {  
        // Iterate through all of the todos and 'yield' each todo (returns it to the stream)
        for (var todo in todos.items) {  
            yield todo;  
        }
    }
}  

/**
* gRPC Server
**/
class TodoServer {  
    Future<void> main(List<String> args) async {  
        final server = Server([TodoService()]);  // Create a new server from the TodoService
        await server.serve(port: 9000); // Start the server on port 9000
        print('Server listening on port ${server.port}...');  
 }}  


main() {  
  TodoServer todoServer = new TodoServer();  
  todoServer.main([]);  
}

编写一个简单的客户端

现在你有了一个服务器,让我们写一个客户端。我将使用内联注释来解释这段代码在做什么。

import 'package:grpc/grpc.dart';  
import 'package:grpc_tutorial/src/generated/todo.pbgrpc.dart'; // Import GRPC  

// Helper class to handle client methods. In the real world, you'd probably have wrappers aroudn each gRPC method call
class Client { 
  ClientChannel channel;  
  TodoClient stub;  

  Future<void> main(List<String> args) async {
      // Create a new channel with localhost and the server port 
      channel = ClientChannel('localhost',  
          port: 8001,  
          options:  // No credentials in this example
              const ChannelOptions(credentials: ChannelCredentials.insecure()));
      // Create the stub, which is the client that you interact with to get gRPC methods  
      stub = TodoClient(channel,  
          options: CallOptions(timeout: Duration(seconds: 30)));  
      try {
          // Create a new todo    
          final TodoItem todoItem = new TodoItem();  
          todoItem.text = "Test 1"; 
          // Send a request to the server to make the new item 
          var res = await stub.createTodo(todoItem);  
          // Prints the recieved item
          print(res);  

          // Create the void message
          final v = new voidNoParam();
          // Get a list of todos as a future  
          var todos = await stub.readTodos(v);  
          print(todos.items);  

          // Get the todos as a stream, listen to it, and print the values. If you run another client and create new todos, you'll see this stream get updated
          stub.readTodosStream(v).listen((value) {print(value); });  
     } catch (e) {  
          print(e);  
     }  
     // Cleanup with shutdown
     await channel.shutdown();  
 }}  

main() {  
  var client = Client();  
  client.main([]);  
}

Bam! 就是这样,你刚刚在Dart中创建了你的第一个gRPC服务器和客户端。要在Flutter中集成这个,你只需要在Client类中写更多的方法,然后在widgets中使用它来添加和获取数据(StreamBuilder是你的朋友)。

转载自彗星代码


www.deepl.com 翻译