为什么这个 TensorFlow Serving gRPC 调用会挂起?

Why would this TensorFlow Serving gRPC call hang?

我们有一个相当复杂的系统,可以将不同的数据源拼接在一起,为我们的用户提供产品推荐。在这些组件中,通常会调用一个或多个我们拥有 运行ning 的 TensorFlow Serving 模型。这一直很好,即使在负载下,直到最近我们的一些最终 REST APIs(使用 Sanic 框架)现在有时需要超过 10 秒才能 return.

使用cProfile,问题似乎是gRPC 通道挂起。但它似乎与我们最终的网络服务层中的某些东西隔离开来。当我 运行 下面的代码分别用于 TensorFlow Serving 组件时,它轻松地通过了一系列随机输入,没有任何问题。

这是我们运行宁的代码,删除了一些特定的细节:

def get_tf_serving(model_uri, model_name, input_name, output_name, X):
    channel = grpc.insecure_channel(model_uri, options=MESSAGE_OPTIONS)
    stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
    request = predict_pb2.PredictRequest()
    request.model_spec.name = model_name
    request.model_spec.signature_name = 'serving_default'
    request.inputs[input_name].CopyFrom(util.make_tensor_proto(X.astype(np.float32), shape=X.shape))
    result = stub.Predict(request, 4.0)
    channel.close()

    # imagine more stuff here doing something with the returned data
    data = result.outputs[output_name].float_val    

    return data

这是由另一个函数调用的,该函数最终由如下所示的路由调用:

@doc.include(True)
async def get_suggestions(request):
    user_id = request.args.get('user_id', 'null')
    count = int(request.args.get('count', 10))

    data = # something that itself calls `get_tf_serving`

    return data

我在这里缺少一些基本的东西吗?当 TensorFlow Serving 服务没有明显的负载问题时,为什么这些请求会突然花费这么长时间并挂起?

为了仔细检查,我们实际上在 FastAPI 中快速重新实现了这些路由之一,虽然它可能好一点,但超时仍然不断发生。

更新:作为另一项测试,我们使用 TensorFlow Serving REST HTTP API 重新实现了所有内容。瞧,问题完全消失了。不过,我觉得 gRPC 应该更好。仍然无法弄清楚为什么挂起。

这里的问题不是 TensorFlow Serving 设置或 Python 代码,而是两个部分之间的网络配置方式。 TensorFlow Serving 实例由 Kubernetes 编排,然后使用 Kubernetes 服务拼接在一起。 Python 代码调用的正是该服务,以及导致超时的不良配置。

This post on the Kubernetes blog 详细说明。简而言之,由于 gRPC 依赖于 HTTP/2,由于多路复用,它在标准 Kubernetes 服务中遇到了一些问题,而多路复用是 gRPC 的优势之一。

同样在同一博客 post 中的解决方案是设置更复杂的网络对象来调节与 TensorFlow Serving 实例的连接。