原文地址: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。
- 如果你还没有安装编译器,请下载软件包并按照 README 中的说明进行安装。
- 按照 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是你的朋友)。
转载自彗星代码