Python GRPC - 无法选择子通道

Woo*_*193 5 python grpc envoyproxy

我正在尝试在 Python 中设置 GRPC 客户端来访问特定服务器。服务器设置为需要通过访问令牌进行身份验证。因此,我的实现如下所示:

def create_connection(target, access_token):
    credentials = composite_channel_credentials(
        ssl_channel_credentials(),
        access_token_call_credentials(access_token))

    target = target if target else DEFAULT_ENDPOINT
    return secure_channel(target = target, credentials = credentials)

conn = create_connection(svc = "myservice", session = Session(client_id = id, client_secret = secret)
stub = FakeStub(conn)
stub.CreateObject(CreateObjectRequest())
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是,当我尝试使用此连接时,出现以下错误:

File "<stdin>", line 1, in <module>
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 216, in __call__
    response, ignored_call = self._with_call(request,
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 257, in _with_call
    return call.result(), call
File "anaconda3\envs\test\lib\site-packages\grpc\_channel.py", line 343, in result
    raise self
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 241, in continuation
    response, call = self._thunk(new_method).with_call(
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 266, in with_call
    return self._with_call(request,
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 257, in _with_call
    return call.result(), call
File "\anaconda3\envs\test\lib\site-packages\grpc\_channel.py", line 343, in result
    raise self
File "\anaconda3\envs\test\lib\site-packages\grpc\_interceptor.py", line 241, in continuation
    response, call = self._thunk(new_method).with_call(
File "\anaconda3\envs\test\lib\site-packages\grpc\_channel.py", line 957, in with_call
    return _end_unary_response_blocking(state, call, True, None)
File "\anaconda3\envs\test\lib\site-packages\grpc\_channel.py", line 849, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
    status = StatusCode.UNAVAILABLE
    details = "failed to connect to all addresses"
    debug_error_string = "{
        "created":"@1633399048.828000000",
        "description":"Failed to pick subchannel",
        "file":"src/core/ext/filters/client_channel/client_channel.cc",
        "file_line":3159,
        "referenced_errors":[
            {
                "created":"@1633399048.828000000",
                "description":
                "failed to connect to all addresses",
                "file":"src/core/lib/transport/error_utils.cc",
                "file_line":147,
                "grpc_status":14
            }
        ]
    }"
Run Code Online (Sandbox Code Playgroud)

我查找了与此响应相关的状态代码,似乎服务器不可用。因此,我尝试等待连接准备就绪:

channel_ready_future(conn).result()
Run Code Online (Sandbox Code Playgroud)

但这挂了。我在这里做错了什么?

更新1

我将代码转换为使用异步连接而不是同步连接,但问题仍然存在。另外,我看到这个问题也已发布在 SO 上,但那里提供的解决方案都没有解决我遇到的问题。

更新2

我认为发生此问题是因为客户端找不到服务器颁发的 TLS 证书,因此我添加了以下代码:

def _get_cert(target: str) -> bytes:
    split_around_port = target.split(":")
    data = ssl.get_server_certificate((split_around_port[0], split_around_port[1]))
    return str.encode(data)
Run Code Online (Sandbox Code Playgroud)

然后更改ssl_channel_credentials()ssl_channel_credentials(_get_cert(target)). 然而,这也没有解决问题。

Woo*_*193 5

这里的问题其实相当深奥。首先,我打开跟踪并将 GRPC 日志级别设置为调试,然后找到这一行:

D1006 12:01:33.694000000 9032 src/core/lib/security/transport/security_handshaker.cc:182] 安全握手失败:{"created":"@1633489293.693000000","description":"无法检查对等点:缺少选定的 ALPN 属性.","文件":"src/core/lib/security/security_connector/ssl_utils.cc","file_line":160}

这让我发现了这个 GitHub 问题,该问题指出问题在于grpcio未将协议插入h2到请求中,这将导致启用 ALPN 的服务器返回该特定错误。进一步的挖掘让我发现了这个问题,并且由于我连接的服务器也使用 Envoy,所以只需修改 envoy 部署文件即可:

clusters:
  - name: my-server
    connect_timeout: 10s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: python-server
        port_value: 1337
    tls_context:
      common_tls_context:
        tls_certificates:
        alpn_protocols: ["h2"] <====== Add this.
Run Code Online (Sandbox Code Playgroud)