GRPC状态码

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GRPC状态码相关的知识,希望对你有一定的参考价值。

参考技术A 使用grpc的时候,线上php客户端调用go服务端,出现2/5/14等状态码,没有做日志输出,导致问题查了很长时间,最终问题是因为连接没有close掉,php连接数不够了。
grpc的状态码在google.golang.org/grpc/codes:code码跟http的状态码是一个道理,整理下状态码的含义:
0:Ok:请求成功
1:Canceled:操作已取消
2:Unknown:未知错误。如果从另一个地址空间接收到的状态值属 于在该地址空间中未知的错误空间,则可以返回此错误的示例。 没有返回足够的错误信息的API引发的错误也可能会转换为此错误
3:InvalidArgument:表示客户端指定了无效的参数。 请注意,这与FailedPrecondition不同。 它表示无论系统状态如何(例如格式错误的文件名)都有问题的参数
4:DeadlineExceeded:意味着操作在完成之前过期。 对于更改系统状态的操作,即使操作成功完成,也可能会返回此错误。 例如,服务器的成功响应可能会延迟足够的时间以使截止日期到期
5:NotFound:表示找不到某个请求的实体(例如文件或目录)
6:AlreadyExists:表示尝试创建实体失败,因为已经存在
7:PermissionDenied:表示调用者没有执行指定操作的权限。它不能用于因耗尽某些资源而引起的拒绝(使用ResourceExhausted代替这些错误)。如果调用者无法识别,则不能使用它(使用Unauthenticated代替这些错误)
8:ResourceExhausted:表示某些资源已耗尽,可能是每个用户的配额,或者整个文件系统空间不足
9:FailedPrecondition:表示操作被拒绝,因为系统不处于操作执行所需的状态。例如,要删除的目录可能不是空的,rmdir操作应用于非目录等。可能帮助服务实现者判断FailedPrecondition,Aborted和Unavailable之间的试金石测试:使用不可用如果客户端只能重试失败的呼叫。如果客户端应该在更高级别重试(例如,重新启动读取 - 修改 - 写入序列),则使用中止。如果客户端不应该重试直到系统状态被明确修复,则使用FailedPrecondition。例如,如果“rmdir”由于目录非空而失败,应该返回FailedPrecondition,因为客户端不应该重试,除非他们首先通过从目录中删除文件来修复该目录。如果客户端在资源上执行条件REST获取/更新/删除并且服务器上的资源与条件不匹配,则使用FailedPrecondition。例如,在相同的资源上发生冲突的读取 - 修改 - 写入
10:Aborted:表示操作被中止,通常是由于并发问题(如序列器检查失败,事务异常终止等)造成的。请参阅上面的试金石测试以确定FailedPrecondition,Aborted和Unavailable之间的差异
11:OutOfRange:表示操作尝试超过有效范围。 例如,寻找或阅读文件末尾。 与InvalidArgument不同,此错误表示如果系统状态更改可能会解决的问题。 例如,如果要求读取的偏移量不在[0,2 ^ 32-1]范围内,则32位文件系统将生成InvalidArgument,但如果要求从偏移量读取当前值,则它将生成OutOfRange 文件大小。 FailedPrecondition和OutOfRange之间有相当多的重叠。 我们建议在应用时使用OutOfRange(更具体的错误),以便遍历空间的调用者可以轻松查找OutOfRange错误以检测何时完成
12:Unimplemented:表示此服务中未执行或未支持/启用操作
13:Internal: 意味着底层系统预期的一些不变量已被打破。 如果你看到其中的一个错误,那么事情就会非常糟糕
14:Unavailable:表示服务当前不可用。这很可能是一种暂时性情况,可能会通过退避重试来纠正。请参阅上面的试金石测试以确定FailedPrecondition,Aborted和Unavailable之间的差异
15:DataLoss:指示不可恢复的数据丢失或损坏
16:Unauthenticated:表示请求没有有效的操作认证凭证
17:_maxCode:这个是最大的状态码

如果 grpc-python 服务器端有类实例,如何让每个客户端获取它们的状态?

【中文标题】如果 grpc-python 服务器端有类实例,如何让每个客户端获取它们的状态?【英文标题】:How to make each client get their state if there is class instance in grpc-python server side? 【发布时间】:2021-12-30 19:40:04 【问题描述】:

我想在下面的场景中使用grpc-python,但我不知道如何实现。

场景是,在python服务器中,它使用类来计算和更新实例的状态,然后将这个状态发送给相应的客户端;在客户端,需要多个客户端与服务器通信才能得到一个结果,而不受其他客户端的干扰。

具体来说,假设有一个类的初始值self.i=0,那么客户端每次调用该类的更新函数时,都会执行self.i=self.i+1,并返回self.i。实际上有两个客户端同时调用这个更新函数,比如client1第三次调用update,client2第一次调用update。

我认为这可以通过为每个客户端创建线程以避免冲突来解决。如果新客户端调用,则会创建新的客户端;如果现有客户端调用,将使用现有线程。但我不知道如何实现?

希望你能帮助我。提前致谢。

【问题讨论】:

到目前为止您尝试过什么?你的代码在哪里? 添加线程不会简化事情,反而会使事情复杂化。你需要独立线程做什么?如果两个客户端同时拨打电话,只需一个接一个地处理即可。 抱歉,我认为你没有理解我的问题。 【参考方案1】:

我想我自己解决了这个问题。如果您有其他更好的解决方案,可以在这里发布。

我在 grpc-python 简介中编辑了 helloworld 示例来解释我的目标。

对于 helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter 
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) 
  rpc Unsubscribe (HelloRequest) returns (HelloReply) 


// The request message containing the user's name.
message HelloRequest 
  string name = 1;


// The response message containing the greetings
message HelloReply 
  string message = 1;

我添加了取消订阅功能以允许一个特定的客户端与服务器断开连接。

在 hello_server.py 中

import grpc
import helloworld_pb2
import helloworld_pb2_grpc
import threading
from threading import RLock
import time
from concurrent import futures
import logging


class Calcuate:
    def __init__(self):
        self.i = 0

    def add(self):
        self.i+=1
        return self.i


class PeerSet(object):
    def __init__(self):
        self._peers_lock = RLock()
        self._peers = 
        self.instances = 

    def connect(self, peer):
        with self._peers_lock:
            if peer not in self._peers:
                print("Peer  connecting".format(peer))
                self._peers[peer] = 1
                a = Calcuate()
                self.instances[peer] = a
                output = a.add()
                return output
            else:
                self._peers[peer] += 1
                a = self.instances[peer]
                output = a.add()
                return output


    def disconnect(self, peer):
        print("Peer  disconnecting".format(peer))
        with self._peers_lock:
            if peer not in self._peers:
                raise RuntimeError("Tried to disconnect peer '' but it was never connected.".format(peer))
            del self._peers[peer]
            del self.instances[peer]

    def peers(self):
        with self._peers_lock:
            return self._peers.keys()


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def __init__(self):
        self._peer_set = PeerSet()

    def _record_peer(self, context):
        return self._peer_set.connect(context.peer())

    def SayHello(self, request, context):
        output = self._record_peer(context)
        print("[thread ] Peers: , output: ".format(threading.currentThread().ident, self._peer_set.peers(), output))
        time.sleep(1)
        return helloworld_pb2.HelloReply(message='Hello, , !'.format(request.name, output))

    def Unsubscribe(self, request, context):
        self._peer_set.disconnect(context.peer())
        return helloworld_pb2.HelloReply(message=' disconnected!'.format(context.peer()))



def serve():
    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()


if __name__ == '__main__':
    logging.basicConfig()
    serve()

context.peer() 的使用改编自 Richard Belleville 的回答 in this post。您可以将 add() 函数更改为可用于更新实例状态的任何其他函数。

在 hello_client.py 中

from __future__ import print_function

import logging

import grpc
import helloworld_pb2
import helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    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)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='Tom'))
        print("Greeter client received: " + response.message)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='Jerry'))
        print("Greeter client received: " + response.message)
        stub.Unsubscribe(helloworld_pb2.HelloRequest(name="end"))


if __name__ == '__main__':
    logging.basicConfig()
    run()

如果我们同时运行多个 hello_client.py,服务器可以区分不同的客户端并发送正确的相应信息给它们。

【讨论】:

以上是关于GRPC状态码的主要内容,如果未能解决你的问题,请参考以下文章

grpc基于http2之上的协议

常用状态码

12.FastAPI响应状态码

Fabric1.4:链码管理与测试

HTTP协议状态码

http协议状态码