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



我们有一个相当复杂的系统,它将不同的数据源缝合在一起,为用户提供产品推荐。在这些组件中,通常会调用我们正在运行的一个或多个TensorFlow Serving模型。即使在负载下,这也很好,直到最近,我们的一些最终RESTAPI(使用Sanic框架(有时需要10秒以上才能返回。

使用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 RESTHTTP API重新实现了所有内容。瞧,这个问题完全消失了。不过,我觉得gRPC应该更好。仍然不明白为什么挂着。

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

Kubernetes博客上的这篇文章解释了细节。简而言之,由于gRPC依赖于HTTP/2,它在标准Kubernetes服务中遇到了一些问题,因为多路复用是gRPC的优势特性之一。

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

最新更新