简单的分析下dart实现grpc客户端的流程,以helloworld为例

2023-09-18 20:56:51

第三步:实现实现gRPC方法

HelloWorldClient类中,为每个定义在.proto文件中的rpc方法实现对应的Dart方法。

简单的分析下dart实现grpc客户端的流程,以helloworld为例

这里给出helloworld的proto文件,grpc协议下客户端和服务端都只需要关注相同的proto文件并以自己的代码实现,客户端和服务端彼此都不关心对方的实现形式

项目目录

C:.
│  .gitignore
│  analysis_options.yaml
│  CHANGELOG.md
│  pubspec.lock
│  pubspec.yaml // 依赖管理,记得加入grpc和protobuf依赖项
│  README.md
│  
├─.dart_tool
│      package_config.json
│      
├─example
│      g_client_example.dart
│      
├─lib  // 这个目录是公共的,实现package最主要就是实现这里
│  │  g_client.dart
│  │  
│  ├─bin
│  └─src
│      │  g_client_base.dart
│      │  helloworld_client.dart
│      │  user_client.dart
│      │  
│      └─generated
│          ├─helloworld   // 由proto工具生成的代码集合
│          │      helloworld.pb.dart
│          │      helloworld.pbenum.dart
│          │      helloworld.pbgrpc.dart
│          │      helloworld.pbjson.dart
│          │
│          └─user
│                  user.pb.dart
│                  user.pbenum.dart
│                  user.pbgrpc.dart
│                  user.pbjson.dart
│
├─protos
│  ├─helloworld
│  │      helloworld.proto // 这是关键的proto文件
│  │
│  └─user
│          user.proto
│
└─test

helloworld.proto

syntax = "proto3";
//指定了 Go 语言代码生成后应该放置在名为 "github.com/rn-consider/grpcservice/helloworld" 的包中,
//会影响生成的 .pb.go 文件的 package 声明
option go_package = "github.com/rn-consider/grpcservice/helloworld";
option java_multiple_files = true;
option java_package = "io.helloworld_server.examples.helloworld_server";
option java_outer_classname = "HelloWorldProto";

package helloworld;

// 此处定义服务,为协议缓冲区中的服务定义
service Greeter {
  /*
  提供SayHello函数,接受HelloRequest类型的消息,
  返回HelloReply类型的消息在grpc中,函数必须始终具有输入消息并返回输出消息
  */
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}

  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}

}

// 要求传入参数必须要name
message HelloRequest {
  /*
  字段的设计十分重要,应谨慎分配字段编号,切勿更改,且在设计时考虑未来的修订
  消息中的字段定义必须指定三件事:类型,名称,编号
  字段的类型可以是当前支持的整数类型(int32,int64等),float,double,bool,字符串,字节(用于任何数据)
  要注意的是字段名称必须全部小写,并使用_分隔多个单词.
  如first_name,字段编号表示字段在消息中的位置,如name = 1表示name在返回信息中占第一位
  字段编号可以从1到2^29
  推荐在字段编号内留下间距,例如将第一个字段编号为1,然后将10用于下一个字段
  这意味着可在稍后添加任何其他字段而不需要对字段进行编号
  */
  string name = 1;
}

// 要求返回参数必须要是message
message HelloReply {
  string message = 1;
}

第一步:导入生成的pb文件

import 'package:grpc/grpc.dart'; // 导入dart grpc包
import 'generated/helloworld/helloworld.pb.dart'; //导入生成的pb文件
import 'generated/helloworld/helloworld.pbgrpc.dart';

第二步:创建grpc客户端类

helloworld_client.dart文件中,创建一个gRPC客户端类,你可以命名为HelloWorldClient。这个类应该包括以下属性:

  • ClientChannel对象用于与gRPC服务器建立连接。

  • GreeterClient对象,它是gRPC服务的客户端实例。

  • // 构造HelloWorldClient用于grpc,类名应该大驼峰命名
    class HelloWorldClient {
      // 定义 gRPC 客户端所需的属性
      // 客户端通道,用于与 gRPC 服务器建立连接
      // late用于延时变量的初始化,非空变量使用 late 关键字声明的变量会被视为非空变量,
      // 这意味着它们在被访问之前必须被初始化,否则会引发运行时异常。
      late ClientChannel _channel;
      // Greeter 客户端,它是 gRPC 服务的客户端实例,
      late GreeterClient _stub;
    
      // 构造函数(必须与类名完全一致),用于初始化客户端
      HelloWorldClient(String host, int port) {
         // 创建客户端通道,指定主机和端口,使用不安全的凭据(仅测试时使用)
        // 下划线表示变量私有
        _channel = ClientChannel(
          host,
          port: port,
          options: const ChannelOptions(
            credentials: ChannelCredentials.insecure(),
          ),
        );
        _stub = GreeterClient(_channel);
      }
    
      // ...
    } 
    

第三步:实现实现gRPC方法

HelloWorldClient类中,为每个定义在.proto文件中的rpc方法实现对应的Dart方法。

在我们的proto文件中定义了SayHelloSayHelloAgain两个rpc方法所以我们应当实现它们

/* 
    gRPC方法:向服务器发送问候消息,并返回服务器的响应
    Future 表示一个异步操作的结果。异步操作是指不会立即返回结果,
    而是在后续某个时刻返回结果的操作。Future 类型表示一个值,
    该值可能在未来某个时间点可用。在上面的代码中,Future<HelloReply> 
    表示这个方法会返回一个 HelloReply 类型的对象,但是这个对象不会立即返回,
    而是在将来的某个时间点返回。
*/
Future<HelloReply> sayHello(String name) async {
  final request = HelloRequest()
  /*
      '..'
      它是Dart中的一种级联操作(cascade)。
      级联操作允许您在同一个对象上执行一系列操作,而不必重复引用对象。
      在这里,..name = name 表示在 HelloRequest 对象上设置 name 字段的值
      为传入的 name 参数。这样做的好处是可以连续地对同一个对象进行多次操作,
      而不必重复引用对象。
  */ 
    ..name = name;
  return _stub.sayHello(request);
}

Future<HelloReply> sayHelloAgain(String name) async {
  final request = HelloRequest()
    ..name = name;
  return _stub.sayHelloAgain(request);
}

4.关闭客户端通道

最后,在客户端类中添加一个方法来关闭客户端通道,以释放资源。

void close() { _channel.shutdown(); }

完整代码

import 'package:grpc/grpc.dart';
import 'generated/helloworld/helloworld.pbgrpc.dart';

class HelloWorldClient {
  late ClientChannel _channel;
  late GreeterClient _stub;

  HelloWorldClient(String host, int port) {
    _channel = ClientChannel(
      host,
      port: port,
      options: const ChannelOptions(
        credentials: ChannelCredentials.insecure(), // 这里使用不安全的连接,仅供测试
      ),
    );
    _stub = GreeterClient(_channel);
  }

  Future<HelloReply> sayHello(String name) async {
    final request = HelloRequest()..name = name;
    return _stub.sayHello(request);
  }

  Future<HelloReply> sayHelloAgain(String name) async {
    final request = HelloRequest()..name = name;
    return _stub.sayHelloAgain(request);
  }

  void close() {
    _channel.shutdown();
  }
}
更多推荐

短视频源码php

对于php短视频源码而言,视频质量与用户使用体验息息相关,高质量的视频观感更有利于留下用户。但实际上视频质量很容易受到各种因素的影响,接下来我们分析一下php短视频源码中导致视频出现异常的各种原因吧。所谓短视频源码的原生开发,是指在Android、IOS等移动平台上利用官方提供的开发语言、开发类库、开发工具进行App开

java spring cloud 企业工程管理系统源码+二次开发+定制化服务

鸿鹄工程项目管理系统SpringCloud+SpringBoot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统1.项目背景一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管理的提升提出了更高的要求。二、企业通过

基于TCP的Qt网络通信

目录前言实现原理1.模块添加2.常用接口函数API3.QTcpServer3.1公共成员函数3.1.1构造函数3.1.2给监听的套接字设置监听3.1.3返回监听成功的套接字对象3.2信号4.QTcpSocket4.1公共成员函数4.1.1构造函数4.1.2连接服务器,需要指定服务器端绑定的IP和端口信息4.1.3接收数

CSP-J 2023 入门级 第一轮 阅读程序(1)

【题目】CSP-J2023入门级第一轮阅读程序(1)#include<iostream>#include<cmath>usingnamespacestd;doublef(doublea,doubleb,doublec){doubles=(a+b+c)/2;returnsqrt(s*(s-a)*(s-b)*(s-c));

七天学会C语言-第六天(指针)

1.指针变量与普通变量指针变量与普通变量是C语言中的两种不同类型的变量,它们有一些重要的区别和联系。普通变量是一种存储数据的容器,可以直接存储和访问数据的值。:intnum=10;//定义一个整数型普通变量num,赋值为10在例子中,变量num是一个普通整数变量,它直接存储了值10。指针变量是一种特殊类型的变量,它存储

面试经典刷题)挑战一周刷完150道-Python版本-第2天(22个题)

一、轮转数组给定一个整数数组nums,将数组中的元素向右轮转k个位置,其中k是非负数。这用于旋转一个整数列表nums中的元素。k%=len(nums):这一行的目的是确保k的值在合理的范围内,因为如果k大于列表的长度len(nums),旋转是循环的,所以我们取余数来确保k在合适的范围内,以避免不必要的重复旋转。比如,如

【Java 基础篇】Java网络编程实时数据流处理

在现代计算机应用程序中,处理实时数据流是一项关键任务。这种数据流可以是来自传感器、网络、文件或其他源头的数据,需要即时处理并做出相应的决策。Java提供了强大的网络编程工具和库,可以用于处理实时数据流。本文将详细介绍如何使用Java进行实时数据流处理。什么是实时数据流?实时数据流是一连串持续不断到达的数据,需要及时处理

【UE 粒子练习】08——LOD概述

目录概念应用举例一、检查当前粒子系统中是否设置了LOD二、添加LOD三、LOD设置(单个粒子发射器)四、LOD设置(多个粒子发射器)概念在UnrealEngine中,LOD(LevelofDetail,细节层次)是一种优化技术,用于在不同距离或屏幕空间尺寸下使用不同的模型或网格,以提高性能并减少资源消耗。LOD技术的主

低功耗蓝牙物联网:未来连接的无限可能

物联网是连接各种设备和传感器的网络,其目的是实现信息的交换和共享,提高效率并优化生活。在这个领域,低功耗蓝牙(BLE)正在发挥着越来越重要的作用。低功耗蓝牙是一种无线通信技术,它的主要特点是低功耗和长寿命。在保证纽扣电池长时间使用的同时,BLE的广播模式功耗可以做到1mA以下,进入低功耗模式后甚至可以达到200ua以下

第一个 Go 程序“hello,world“ 与 main 函数

第一个Go程序"hello,world"与main函数文章目录第一个Go程序"hello,world"与main函数一.创建“hello,world”示例程序二.“hello,world”程序结构拆解三、main函数四、Go语言中程序是怎么编译的?一.创建“hello,world”示例程序创建一个叫做main的go文件

Haproxy负载均衡集群 超详细 (附部署实例)

Haproxy一、Web集群调度器1.1常用的Web集群调度器1.2常用集群调度器的优缺点(LVS,Nginx,Haproxy)1.2.1Nginx1.2.2LVS1.2.3Haproxy1.3LVS、Nginx、Haproxy的区别二、Haproxy2.1简介2.2Haproxy的主要特性2.3Haproxy应用分析

热文推荐