百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

gRPC 学习笔记(grpc实战)

ccwgpt 2024-10-13 01:31 40 浏览 0 评论

1. 问题描述

我们认为是从 DevOps 盛行之后,运维也开始写代码(应该说比以前写的更多),诞生运维开发岗位(应该说越来越多)。比如说,开发 Prometheus Exporter 成为运维的工作(其实以前都是现有的,工作都是别人做好的,基本不用开发,或者简单配置即可实现)。

现在,我们需要为我们自己的应用开发 Prometheus Exporter,但是应用提供 gRPC 接口,所以运维就要看学习如何使用 gRPC 框架。

该笔记将记录:gRPC 的快速入门方法、学习笔记,以及相关问题处理(该笔记是我们对 gRPC 的学习笔记,因此仅记录基础入门相关的内容)。

2. 解决方案

我们通过学习 gRPC 的使用流程,来理解它是如何工作的:
1)编写 .proto 文件:该文件包含 RPC 方法、数据结构等等信息
2)生成桩代码:使用命令 protoc 生成桩代码(能够生成多种语言,比如 Go C++ Java Python C# 等等)。“桩代码”类似于“类库”。
3)编写服务端:在程序中,使用这些类库,编写服务端,实现 RPC 方法。然后,运行该服务端,监听 TCP 端口,等待请求;
4)编写客户端:在程序中,使用这些类库,编写客户端,该客户端连接服务端,并调用这些 RPC 方法,接收服务端返回结果;

在调用时,各个部分之间的关系,可以使用下图解释:

我们使用 Python 语言,因此重点使用 Python 示例。但是,使用其他语言也是可以的,并且只要使用 gRPC 框架,鉴于 gRPC 也是网络通信,所以多个语言之间是互通的。

2.1. 学习路线图

官方文档已经提供详细的示例,是最好的教程。跟着官方文档,一边学习,一边练习,已经足够。

对于 Python 使用 gRPC 框架,如果需要快速入门,建议按照以下顺序阅读文档:
1)针对 gRPC 介绍:Introduction to gRPC | gRPC(https://grpc.io/docs/what-is-grpc/introduction/ )
2)使用 gRPC 框架的入门示例(Python):Quick start | Python | gRPC(https://grpc.io/docs/languages/python/quickstart/ )
3)使用 gRPC 框架的完整示例(Python):Basics tutorial | Python | gRPC(https://grpc.io/docs/languages/python/basics/ )

常用文档(Python):
1)Welcome to gRPC Python’s documentation! — gRPC Python 1.36.1 documentation(https://grpc.github.io/grpc/python/ )
2)Language Guide | Protocol Buffers | Google Developers(https://developers.google.com/protocol-buffers/docs/overview )
3)关于 gRPC 概念及术语:Core concepts, architecture and lifecycle | gRPC(https://grpc.io/docs/what-is-grpc/core-concepts/ )

3. 入门案例

因为我们使用 Python 语言,所以这里的示例使用 Python 环境。虽然与其他语言不同,但是流程是类似的。

3.1. 准备工作(安装环境)

# 为了不影响主机环境,我们选择在 Virtual Environment 中操作
# 既然大家已经开始学习 gRPC 框架,我们相信虚拟环境等工具应该信手拈来,所以该笔记未进一步介绍虚拟环境
mkvirtualenv "grpc"
workon "grpc"
# 安装 gRPC 及工具
pip install grpcio grpcio-tools

3.2. 第一阶段、定义服务

定义 helloworld.proto 文件:

syntax = "proto3";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}

该文件定义“客户端与服务端的通信协议”:
1)站在服务端的角度:届时服务端将实现 Greeter 服务,该服务提供 SayHello 方法,参数为 HelloRequest 结构,返回 HelloReply 结构;
2)站在客户端的角度:届时客户端将调用 Greeter 服务的 SayHello 方法,传入 HelloRequest 结构,获得 HelloReply 结构的响应;

3.3. 第二阶段、创建桩代码

helloworld.proto 文件定义协议,但是无法直接使用,需要生成桩代码(类库)。

使用已有的 Protocal Buffers 定义来创建桩代码:

# 创建桩代码
python -m grpc_tools.protoc \
--proto_path=./ \
--python_out=. --grpc_python_out=. \
./helloworld.proto

当如上命令执行后,将生成如下文件:
1)helloworld_pb2.py:该文件包含我们在“协议”中定义的数据结构、服务信息等等(多用于客户端)。
2)helloworld_pb2_grpc.py:该文件包含 gRPC 相关的方法(多用于服务端)

命令行参数的说明:
1)--proto_path:指定 .proto 查找路径(类似于 Java 的 CLASSPATH,Python 的 PYTHONPATH)。代码通常会写在不同文件中,然后相互引用(import、include),所以需要指定搜索路径,这就是该选项的作用。当然,我们的示例只使用 helloworld.proto,未使用其他文件,所以在这里是多余的。
2)--python_out:helloworld_pb2.py 的输出路径
3)--grpc_python_out:文件 helloworld_pb2_grpc.py 的输出路径
4)helloworld.proto:我们的协议文件

3.4. 第三阶段、编写服务端

定义 helloworld_server.py 文件:

from concurrent import futures
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
if __name__ == '__main__':
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()

class Greeter(helloworld_pb2_grpc.GreeterServicer):
1)服务端应该提供服务,所以它要实现 Greeter 服务,继承自 GreeterServicer 类(这基本是固定格式)。然后,实现 SayHello 方法。
2)SayHello 方法的 request 参数则为 HelloRequest 结构,通过 request.name 获取 name 属性;
3)正如协议的定义,该方法返回 HelloReply 结构,该结构包含 message 参数。从代码上看:HelloReply 以方法的形式出现;它的属性以方法参数形式出现;而不是直接传入 HelloReply 结构。

然后,main 方法启动 gRPC 服务,监听 50051 端口,等待客户端的访问。

最后,我们需要启动这个服务端,否则后面的客户端无法连接:python ./helloworld_server.py

3.5. 第四阶段、编写客户端

定义 helloworld_client.py 文件:

from __future__ import print_function
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
from google.protobuf.json_format import MessageToJson

if __name__ == '__main__':
with grpc.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)
print(MessageToJson(response, including_default_value_fields=True))

MessageToJson():将返回转为 Json 字符串类型
including_default_value_fields=True:如果字段为默认值,是不会在 Client 和 Server 之间传递的。需要该属性,才能显示默认字段。

该示例代码的流程(基本都是这样的流程):
1)连接 gRPC 服务;
2)设置桩代码使用该连接;
3)发送请求,调用那 SayHello 方法,并传入 HelloRequest 结构(当然,这里依旧是以方法的形式来体现数据结构);

运行该客户端,将获取到服务端的输出:

# python3 helloworld_client.py
Greeter client received: Hello, you!
{
"message": "Hello, you!"
}

至此,一个简单的 gRPC 示例就运行成功了。

4. 常见问题处理

4.1. ModuleNotFoundError: No module named 'common_pb2'

ImportError: No module named 'InternalMessage_pb2' · Issue #3430 · protocolbuffers/protobuf(https://github.com/protocolbuffers/protobuf/issues/3430 )

问题描述

Traceback (most recent call last):
File "agent.py", line 22, in <module>
import controller_pb2 as Controller
File "/srv/http/agent/controller_pb2.py", line 14, in <module>
import common_pb2 as common__pb2
ModuleNotFoundError: No module named 'common_pb2'

问题原因:所有的 .proto 都要参与桩代码的生成。

解决方案:注意在创建桩代码时,我们使用 *.proto 就是为了将所有 .proto 定义参与桩代码的生成。

5. 参考文献

Introduction to gRPC | gRPC(https://grpc.io/docs/what-is-grpc/introduction/ )
Quick start | Python | gRPC(https://grpc.io/docs/languages/python/quickstart/ )
Protobuf to json in python - Stack Overflow(https://stackoverflow.com/questions/19734617/protobuf-to-json-in-python )
python - Protobuf doesn't serialize default values - Stack Overflow(https://stackoverflow.com/questions/31021797/protobuf-doesnt-serialize-default-values )
python - Getting all field names from a protocol buffer? - Stack Overflow(https://stackoverflow.com/questions/24639562/getting-all-field-names-from-a-protocol-buffer )

相关推荐

迈向群体智能 | 智源发布首个跨本体具身大小脑协作框架

允中发自凹非寺量子位|公众号QbitAI3月29日,智源研究院在2025中关村论坛“未来人工智能先锋论坛”上发布首个跨本体具身大小脑协作框架RoboOS与开源具身大脑RoboBrain,可实...

大模型对接微信个人号,极空间部署AstrBot机器人,万事不求百度

「亲爱的粉丝朋友们好啊!今天熊猫又来介绍好玩有趣的Docker项目了,喜欢的记得点个关注哦!」引言前两天熊猫发过一篇关于如何在极空间部署AstrBot并对接QQ消息平台的文章,不过其实QQ现在已经很少...

Seata,让分布式事务不再是难题!实战分享带你领略Seata的魅力!

终身学习、乐于分享、共同成长!前言Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的...

常见分布式事务解决方案(分布式事务解决的问题)

1.两阶段提交(2PC)原理:分为准备阶段(协调者询问参与者是否可提交)和提交阶段(协调者根据参与者反馈决定提交或回滚)。优点:强一致性,适用于数据库层(如XA协议)。缺点:同步阻塞:所有参与者阻塞...

分布式事务:从崩溃到高可用,程序员必须掌握的实战方案!

“支付成功,但订单状态未更新!”、“库存扣减后,交易却回滚了!”——如果你在分布式系统中踩过这些“天坑”,这篇文章就是你的救命稻草!本文将手把手拆解分布式事务的核心痛点和6大主流解决方案,用代码实战+...

谈谈对分布式事务的一点理解和解决方案

分布式事务首先,做系统拆分的时候几乎都会遇到分布式事务的问题,一个仿真的案例如下:项目初期,由于用户体量不大,订单模块和钱包模块共库共应用(大war包时代),模块调用可以简化为本地事务操作,这样做只要...

一篇教你通过Seata解决分布式事务问题

1 Seata介绍Seata是由阿里中间件团队发起的开源分布式事务框架项目,依赖支持本地ACID事务的关系型数据库,可以高效并且对业务0侵入的方式解决微服务场景下面临的分布式事务问题,目前提供AT...

Seata分布式事务详解(原理流程及4种模式)

Seata分布式事务是SpringCloudAlibaba的核心组件,也是构建分布式的基石,下面我就全面来详解Seata@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合...

分布式事务最终一致性解决方案有哪些?MQ、TCC、saga如何实现?

JTA方案适用于单体架构多数据源时实现分布式事务,但对于微服务间的分布式事务就无能为力了,我们需要使用其他的方案实现分布式事务。1、本地消息表本地消息表的核心思想是将分布式事务拆分成本地事务进行处理...

彻底掌握分布式事务2PC、3PC模型(分布式事务视频教程)

原文:https://mp.weixin.qq.com/s/_zhntxv07GEz9ktAKuj70Q作者:马龙台工作中使用最多的是本地事务,但是在对单一项目拆分为SOA、微服务之后,就会牵扯出分...

Seata分布式事务框架关于Annotation的SAGA模式分析

SAGAAnnotation是ApacheSeata版本2.3.0中引入的功能,它提供了一种使用Java注解而不是传统的JSON配置或编程API来实现SAGA事务模式的声明...

分布式事务,原理简单,写起来全是坑

今天我们就一起来看下另一种模式,XA模式!其实我觉得seata中的四种不同的分布式事务模式,学完AT、TCC以及XA就够了,Saga不好玩,而且长事务本身就有很多问题,也不推荐使用。S...

内存空间节约利器redis的bitmap(位图)应用场景有哪些你知道吗

在前面我们分享过一次Redis常用数据结构和使用场景,文章对Redis基本使用做了一个简单的API说明,但是对于其中String类型中的bitmap(位图)我们需要重点说明一下,因为他的作用真的不容忽...

分布式事务原理详解(图文全面总结)

分布式事务是非常核心的分布式系统,也是大厂经常考察对象,下面我就重点详解分布式事务及原理实现@mikechen本文作者:陈睿|mikechen文章来源:mikechen.cc分布式事务分布式事务指的是...

大家平时天天说的分布式系统到底是什么东西?

目录从单块系统说起团队越来越大,业务越来越复杂分布式出现:庞大系统分而治之分布式系统所带来的技术问题一句话总结:什么是分布式系统设计和开发经验补充说明:中间件系统及大数据系统前言现在有很多Java技术...

取消回复欢迎 发表评论: