gRPC Connectivity Semantics and API

本文档描述gRPC channels 连接语义和对 RPCs 的相应影响。然后我们讨论API。

States of Connectivity

gRPC Channels 提供一种抽象,在其之上客户端可以与服务器通信。客户端方面的 channel 对象仅使用一个DNS名就能够被构造。Channels 封装了一系列功能,包括名称解析,建立tco连接(包含重连和backoff) 和 TLS 握手。Channels也能在已建立的连接上处理错误或者重连,或者在HTTP/2 GO_AWAY的情况中,重新解析名称或重连。

为了向 gRPC API 的用户隐藏所有这些活动的细节(i.e.,应用代码),且暴露关于channel状态这些有意义的信息,我们使用具有5种状态的状态机,定义如下:

CONNECTING:channel 正在尝试建立连接并期望在以下步骤中取得进展 name resolution,TCP connection establishment 或 TLS handshake。这可能被用作通道创建的初始化状态。

READY:channel 已经通过以下方法成功建立连接,TLS handshake(或其他等价方式)和协议层级(HTTP/2,etc)的握手,且随后所有的通讯尝试都已经成功(或者在没有任何已经失败的情况下挂起).

TRANSIENT_FAILURE:已经有一些短暂的失败(例如TCP3路握手超时或者一个socket错误)。在这一状态的Channels将最终转换为CONNETCTING状态并尝试再次建立连接。因为重试使用指数回退,连接失败的channel开始时将花费很少的时间挂起,但是随之不断重复的尝试,时间会逐渐增加。对于很多的non-fatal失败(e.g.,TCP连接尝试超时因为服务器不可用),channel可能会花费大量的时间在这个状态上。

IDLE:在这一状态,channel甚至不会尝试创建连接,因为缺少新的或挂起的RPCs。新的 RPCs 可能在这一状态被创建。任何在本channel上开始rpc的尝试都会使channel离开本状态,进入CONNECTING 状态。当在指定的IDLE_TIMEOUT内,channel上没有RPC活动,i.e,,在这期间没有新的或者挂起的(激活的)RPCs,channel将从READY或CONNECTING状态IDEL。另外,接收到GOAWAY的channel且没有激活或挂起的RPCs,也应当转到IDLE来避免试图断开连接的服务器连接过载。我们将使用默认的IDEL_TIMEOUT(300s, 5min)。

SHUTDOWN:本channel已经开始关闭了。任何新的RPCs将会立刻失败。挂起的RPCs可能会继续运行直到应用取消它们。Channels可能进入这个状态,因为应用显式的请求关闭,或者在尝试连接通讯时一个不可修复的错误发生了。进入这种状态的Channel永远不会离开这种状态。

下述表格列出从一种状态到另一种状态的合法转换和相应原因。空单元格表示不允许转换。

From/ToCONNECTINGREADYTRANSIENT_FAILUREIDLESHUTDOWN
CONNECTING连接建立的增量过程所有建立连接需要的步骤都成功了建立连接需要的任何步骤中发生任意错误在IDLE_TIMEOUT时间内,没有新的RPC活动应用触发关闭
READY连接建立的增量过程在已建立连接的通道上期望成功的通讯中发生任何错误在IDLE_TIMEOUT时间内,没有新的RPC活动应用触发关闭
TRANSIENT_FAILUREbackoff实现所需要的等待时间过去应用触发关闭
IDLE本channel上有任何新的RPC活动应用触发关闭
SHUTDOWN

Channel State API

所有的 gRPC 库将暴露一个 channel层级的 API 方法来 poll 出当前 channel 的状态。在C++中,该方法为 GetState 并返回一个枚举变量表示5种 channel 合法状态之一。它也接受一个布尔值 try_to_connect 来将 channel 的 IDEL 状态转换为 CONNECTING。布尔值应当表现的与一个RPC发生时一样,所以它会重置IDLE_TIMEOUT。

grpc_connectivity_state GetState(bool try_to_connect);

所有的库也应当暴露一个API用于确保应用(gRPC API 的用户)能够在 channel 状态发生改变时获得通知。因为状态改变可能十分迅速,与任何此类通知产生竞争,所以通知仅仅告知用户某些状态改变已经发生了,将channel当前状态的poll丢给用户去做。

同步版本的API如下:

bool WaitForStateChange(grpc_connectivity_state source_state, gpr_timespec deadline);

当状态与source_state不一致时,返回true;如果超时,返回false。Asynchronous-based 和 futures-based APIs也有对应的方法,用于channel状态改变时进行通知。

注意,每次有任何状态过渡时通知都会被递交。另一方面,对于可恢复的失败,合法状态转换的规则,需要从CONNECTING转换为TRANSIENT_FAILURE,然后返回CONNECTING,即使相应的指数backoff在重试前不需要等待。在共同作用下,应用收到的状态改变通知看上去像是虚假的,e.g.,一个应用等待一个channel上额状态改变,在收到一个状态改变通知后,发现状态仍然是CONNECTING,这是因为channel有极短持续时间的 TRANSIENT_FAILURE 状态。

gRPC channel

class ChannelInterface {
public:
	virtual ~ChannelInterface() {}
	// 获取当前 channel 状态。如果 channel 在 IDEL 状态且参数`try_to_connect`被设置为true,尝试连接。 
	virtual grpc_connectivity_state GetState(bool try_to_connect) = 0;
	// 当channel状态变化时或者截止时间过期,返回在参数cq[in]上的参数tag[out],需要调用GetState()来
	// 获取当期状态
	template<typename T>
	void NotifyOnStateChange(grpc_connectivity_state last_observed, 
							 T deadline, ::grpc::CompletionQueue* cq, void*tag) {
		TimePoint<T> deadline_tp(deadline);
		NotifyOnStateChangeImpl(last_observed, deadline_tp.raw_time(), cq, tag);
	}
	
	// 阻塞等待channel状态改变或者截止时间过期。GetState 需要被调用来获得当前的状态
	template <typename T>
	bool WaitForStateChange(grpc_connectivity_state last_observed, T deadline){
		TimePoint<T> point_tp(deadline);
		return WaitForStateChangeImpl(last_observed, deadline_tp.raw_time());   
	}
	// 等待该 channel 被连接
	template <typename T>
	bool WaitForConnected(T deadline) {
		grpc_connectivity_state state;
		while((state = GetState(true)) != GRPC_CHANNEL_READY) {
			if(!WaitForStateChange(state, deadine)) return false;
		}
		return true;
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您好!以下是一个使用 FastAPIgRPC 的示例: ```python from fastapi import FastAPI from fastapi_grpc import GRPCApp from grpc import server as grpc_server from grpc_reflection.v1alpha import reflection from proto.helloworld_pb2_grpc import GreeterServicer, add_GreeterServicer_to_server app = FastAPI() grpc_app = GRPCApp(app) class HelloWorldService(GreeterServicer): def SayHello(self, request, context): return helloworld_pb2.HelloReply(message='Hello, {}!'.format(request.name)) grpc_app.add_servicer(HelloWorldService()) grpc_app.add_reflection_service(reflection.ServerReflectionServicer()) @app.get("/") def read_root(): return {"Hello": "World"} if __name__ == "__main__": grpc_server = grpc_server(futures.ThreadPoolExecutor(max_workers=10)) add_GreeterServicer_to_server(HelloWorldService(), grpc_server) grpc_server.add_insecure_port('[::]:50051') grpc_server.start() uvicorn.run(app, host="0.0.0.0", port=8000) ``` 在这个示例中,我们创建了一个 FastAPI 应用,并使用 `fastapi_grpc` 库来将 gRPC 服务添加到应用中。我们还创建了一个 `HelloWorldService` 类来实现我们的 gRPC 服务,并使用 `add_servicer` 方法将其添加到应用中。最后,我们使用 `uvicorn` 库来运行应用程序。 希望这对您有所帮助! ### 回答2: FastAPI是一个高性能的Web框架,可以方便地创建RESTful API。虽然FastAPI本身并不支持gRPC协议,但可以通过第三方库实现FastAPIgRPC的结合。 首先,我们需要安装与导入所需的库。可以使用Python的包管理工具pip来安装fastapigrpcio库。 ``` pip install fastapi grpcio ``` 接下来,我们需要创建一个gRPC服务和一个FastAPI应用。 ```python # 引入fastapigrpcio库 from fastapi import FastAPI import grpc # 创建FastAPI应用 app = FastAPI() # 创建gRPC服务 class Greeter(grpc.Base): def SayHello(self, request, context): return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) # 启动gRPC服务 server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) server.add_insecure_port('[::]:50051') server.start() print('gRPC server running...') # 定义FastAPI路由 @app.get('/hello/{name}') async def hello(name: str): channel = grpc.insecure_channel('localhost:50051') stub = helloworld_pb2_grpc.GreeterStub(channel) response = stub.SayHello(helloworld_pb2.HelloRequest(name=name)) return {'message': response.message} ``` 上述示例中,我们首先导入必要的库。然后创建一个gRPC服务,并在FastAPI应用中定义一个路由。在路由中,我们使用grpc库与gRPC服务进行通信,然后将返回的结果作为响应返回给客户端。 最后,我们需要运行FastAPI应用。 ```python if __name__ == '__main__': uvicorn.run(app, host='0.0.0.0', port=8000) ``` 通过运行上述代码,我们就可以在本地的8000端口上启动一个FastAPI应用,该应用与gRPC服务进行通信。 以上就是使用FastAPI实现gRPC示例的简要说明。通过结合FastAPIgRPC,我们可以创建高性能的、能够处理大量请求的Web应用程序。 ### 回答3: FastAPI 是一个现代、快速(高性能)的 Web 框架,可以与 gRPC 一起使用。下面是一个 FastAPI 使用 gRPC 的示例: 首先,我们需要安装所需的库。可以使用 pip 命令安装 fastapigrpcio: ``` pip install fastapi grpcio ``` 接下来,我们需要定义一个 gRPC 的服务和消息类型。在 proto 文件中定义服务和消息类型,并使用 protoc 进行编译。比如,我们在 example.proto 文件中定义一个简单的服务和消息类型: ```protobuf syntax = "proto3"; message HelloRequest { string name = 1; } message HelloResponse { string message = 1; } service HelloService { rpc SayHello(HelloRequest) returns (HelloResponse); } ``` 然后,我们可以使用 protoc 命令编译 proto 文件,生成 gRPC 相关代码: ``` python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. example.proto ``` 生成的代码将包括一个名为 example_pb2.py 的文件,其中定义了消息类型,并且一个名为 example_pb2_grpc.py 的文件,其中定义了服务。 接下来,我们可以在 FastAPI 应用程序中使用 gRPC 服务。创建一个 main.py 文件,在其中导入所需的库和生成的 gRPC 代码: ```python from fastapi import FastAPI from example_pb2_grpc import HelloServiceStub from example_pb2 import HelloRequest app = FastAPI() @app.get("/hello/{name}") async def hello(name: str): channel = grpc.insecure_channel('localhost:50051') stub = HelloServiceStub(channel) response = stub.SayHello(HelloRequest(name=name)) return response.message ``` 在上面的代码中,我们通过在 /hello/{name} 路径上定义一个 GET 方法,当请求到来时,将调用 gRPC 服务,并返回响应消息。 最后,我们可以使用 uvicorn 启动 FastAPI 应用程序: ``` uvicorn main:app --host 0.0.0.0 --port 8000 ``` 现在,我们的 FastAPI 应用程序已经使用 gRPC 成功启动了。可以通过访问 http://localhost:8000/hello/{name} 来测试。 注意:上面只是一个简单的示例,实际使用时,可能需要处理 gRPC 的异常以及更复杂的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值