mirror of https://github.com/grpc/grpc.git
commit
6d6c5ca009
566 changed files with 30037 additions and 12458 deletions
@ -1,252 +0,0 @@ |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
package(default_visibility = ["//visibility:public"]) |
||||
|
||||
load("@grpc_python_dependencies//:requirements.bzl", "requirement") |
||||
load("@rules_proto//proto:defs.bzl", "proto_library") |
||||
load("//bazel:cc_grpc_library.bzl", "cc_grpc_library") |
||||
load("//bazel:grpc_build_system.bzl", "grpc_proto_library") |
||||
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library") |
||||
|
||||
grpc_proto_library( |
||||
name = "auth_sample", |
||||
srcs = ["protos/auth_sample.proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "hellostreamingworld", |
||||
srcs = ["protos/hellostreamingworld.proto"], |
||||
) |
||||
|
||||
# The following three rules demonstrate the usage of the cc_grpc_library rule in |
||||
# in a mode compatible with the native proto_library and cc_proto_library rules. |
||||
proto_library( |
||||
name = "helloworld_proto", |
||||
srcs = ["protos/helloworld.proto"], |
||||
) |
||||
|
||||
cc_proto_library( |
||||
name = "helloworld_cc_proto", |
||||
deps = [":helloworld_proto"], |
||||
) |
||||
|
||||
cc_grpc_library( |
||||
name = "helloworld_cc_grpc", |
||||
srcs = [":helloworld_proto"], |
||||
grpc_only = True, |
||||
deps = [":helloworld_cc_proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "route_guide", |
||||
srcs = ["protos/route_guide.proto"], |
||||
) |
||||
|
||||
proto_library( |
||||
name = "keyvaluestore_proto", |
||||
srcs = ["protos/keyvaluestore.proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "keyvaluestore", |
||||
srcs = ["protos/keyvaluestore.proto"], |
||||
) |
||||
|
||||
proto_library( |
||||
name = "protos/helloworld_proto", |
||||
srcs = ["protos/helloworld.proto"], |
||||
) |
||||
|
||||
py_proto_library( |
||||
name = "helloworld_py_pb2", |
||||
deps = [":protos/helloworld_proto"], |
||||
) |
||||
|
||||
py_grpc_library( |
||||
name = "helloworld_py_pb2_grpc", |
||||
srcs = [":protos/helloworld_proto"], |
||||
deps = [":helloworld_py_pb2"], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_client", |
||||
srcs = ["cpp/helloworld/greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_client", |
||||
srcs = ["cpp/helloworld/greeter_async_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_client2", |
||||
srcs = ["cpp/helloworld/greeter_async_client2.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_server", |
||||
srcs = ["cpp/helloworld/greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
"//:grpc++_reflection", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_server", |
||||
srcs = ["cpp/helloworld/greeter_async_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "metadata_client", |
||||
srcs = ["cpp/metadata/greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "metadata_server", |
||||
srcs = ["cpp/metadata/greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "lb_client", |
||||
srcs = ["cpp/load_balancing/greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "lb_server", |
||||
srcs = ["cpp/load_balancing/greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "compression_client", |
||||
srcs = ["cpp/compression/greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "compression_server", |
||||
srcs = ["cpp/compression/greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":helloworld_cc_grpc", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "keyvaluestore_client", |
||||
srcs = [ |
||||
"cpp/keyvaluestore/caching_interceptor.h", |
||||
"cpp/keyvaluestore/client.cc", |
||||
], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":keyvaluestore", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "keyvaluestore_server", |
||||
srcs = ["cpp/keyvaluestore/server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":keyvaluestore", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "route_guide_client", |
||||
srcs = [ |
||||
"cpp/route_guide/helper.cc", |
||||
"cpp/route_guide/helper.h", |
||||
"cpp/route_guide/route_guide_client.cc", |
||||
], |
||||
data = ["cpp/route_guide/route_guide_db.json"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":route_guide", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "route_guide_server", |
||||
srcs = [ |
||||
"cpp/route_guide/helper.cc", |
||||
"cpp/route_guide/helper.h", |
||||
"cpp/route_guide/route_guide_server.cc", |
||||
], |
||||
data = ["cpp/route_guide/route_guide_db.json"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":route_guide", |
||||
"//:grpc++", |
||||
], |
||||
) |
||||
|
||||
proto_library( |
||||
name = "route_guide_proto", |
||||
srcs = ["protos/route_guide.proto"], |
||||
) |
@ -0,0 +1,35 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_binary( |
||||
name = "compression_client", |
||||
srcs = ["greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "compression_server", |
||||
srcs = ["greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
@ -0,0 +1,66 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_binary( |
||||
name = "greeter_client", |
||||
srcs = ["greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_client", |
||||
srcs = ["greeter_async_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_client2", |
||||
srcs = ["greeter_async_client2.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_server", |
||||
srcs = ["greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//:grpc++_reflection", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "greeter_async_server", |
||||
srcs = ["greeter_async_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
@ -0,0 +1,38 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_binary( |
||||
name = "keyvaluestore_client", |
||||
srcs = [ |
||||
"caching_interceptor.h", |
||||
"client.cc", |
||||
], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:keyvaluestore", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "keyvaluestore_server", |
||||
srcs = ["server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:keyvaluestore", |
||||
], |
||||
) |
@ -0,0 +1,35 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_binary( |
||||
name = "lb_server", |
||||
srcs = ["greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "lb_client", |
||||
srcs = ["greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
@ -0,0 +1,35 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_binary( |
||||
name = "metadata_client", |
||||
srcs = ["greeter_client.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "metadata_server", |
||||
srcs = ["greeter_server.cc"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:helloworld_cc_grpc", |
||||
], |
||||
) |
@ -0,0 +1,56 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
cc_library( |
||||
name = "route_guide_helper", |
||||
srcs = [ |
||||
"helper.cc", |
||||
"helper.h", |
||||
], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
"//:grpc++", |
||||
"//examples/protos:route_guide", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "route_guide_client", |
||||
srcs = [ |
||||
"route_guide_client.cc", |
||||
], |
||||
data = ["route_guide_db.json"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":route_guide_helper", |
||||
"//:grpc++", |
||||
"//examples/protos:route_guide", |
||||
], |
||||
) |
||||
|
||||
cc_binary( |
||||
name = "route_guide_server", |
||||
srcs = [ |
||||
"route_guide_server.cc", |
||||
], |
||||
data = ["route_guide_db.json"], |
||||
defines = ["BAZEL_BUILD"], |
||||
deps = [ |
||||
":route_guide_helper", |
||||
"//:grpc++", |
||||
"//examples/protos:route_guide", |
||||
], |
||||
) |
@ -0,0 +1,62 @@ |
||||
/* |
||||
* |
||||
* Copyright 2015 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
var PROTO_PATH = __dirname + '/../../protos/helloworld.proto'; |
||||
|
||||
var parseArgs = require('minimist'); |
||||
var grpc = require('@grpc/grpc-js'); |
||||
var grpc_xds = require('@grpc/grpc-js-xds'); |
||||
grpc_xds.register(); |
||||
|
||||
var protoLoader = require('@grpc/proto-loader'); |
||||
var packageDefinition = protoLoader.loadSync( |
||||
PROTO_PATH, |
||||
{keepCase: true, |
||||
longs: String, |
||||
enums: String, |
||||
defaults: true, |
||||
oneofs: true |
||||
}); |
||||
var hello_proto = grpc.loadPackageDefinition(packageDefinition).helloworld; |
||||
|
||||
function main() { |
||||
var argv = parseArgs(process.argv.slice(2), { |
||||
string: 'target' |
||||
}); |
||||
var target; |
||||
if (argv.target) { |
||||
target = argv.target; |
||||
} else { |
||||
target = 'localhost:50051'; |
||||
} |
||||
var client = new hello_proto.Greeter(target, |
||||
grpc.credentials.createInsecure()); |
||||
var user; |
||||
if (argv._.length > 0) { |
||||
user = argv._[0];
|
||||
} else { |
||||
user = 'world'; |
||||
} |
||||
client.sayHello({name: user}, function(err, response) { |
||||
if (err) throw err; |
||||
console.log('Greeting:', response.message); |
||||
client.close(); |
||||
}); |
||||
} |
||||
|
||||
main(); |
@ -0,0 +1,10 @@ |
||||
{ |
||||
"name": "grpc-examples-xds", |
||||
"version": "0.1.0", |
||||
"dependencies": { |
||||
"@grpc/proto-loader": "^0.5.0", |
||||
"@grpc/grpc-js": "^1.2.0", |
||||
"@grpc/grpc-js-xds": "^1.2.0", |
||||
"minimist": "^1.2.0" |
||||
} |
||||
} |
@ -0,0 +1,83 @@ |
||||
# Copyright 2020 the gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
licenses(["notice"]) # 3-clause BSD |
||||
|
||||
package(default_visibility = ["//visibility:public"]) |
||||
|
||||
load("@grpc_python_dependencies//:requirements.bzl", "requirement") |
||||
load("@rules_proto//proto:defs.bzl", "proto_library") |
||||
load("//bazel:cc_grpc_library.bzl", "cc_grpc_library") |
||||
load("//bazel:grpc_build_system.bzl", "grpc_proto_library") |
||||
load("//bazel:python_rules.bzl", "py_grpc_library", "py_proto_library") |
||||
|
||||
grpc_proto_library( |
||||
name = "auth_sample", |
||||
srcs = ["auth_sample.proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "hellostreamingworld", |
||||
srcs = ["hellostreamingworld.proto"], |
||||
) |
||||
|
||||
# The following three rules demonstrate the usage of the cc_grpc_library rule in |
||||
# in a mode compatible with the native proto_library and cc_proto_library rules. |
||||
proto_library( |
||||
name = "helloworld_proto", |
||||
srcs = ["helloworld.proto"], |
||||
) |
||||
|
||||
cc_proto_library( |
||||
name = "helloworld_cc_proto", |
||||
deps = [":helloworld_proto"], |
||||
) |
||||
|
||||
cc_grpc_library( |
||||
name = "helloworld_cc_grpc", |
||||
srcs = [":helloworld_proto"], |
||||
grpc_only = True, |
||||
deps = [":helloworld_cc_proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "route_guide", |
||||
srcs = ["route_guide.proto"], |
||||
) |
||||
|
||||
proto_library( |
||||
name = "keyvaluestore_proto", |
||||
srcs = ["keyvaluestore.proto"], |
||||
) |
||||
|
||||
grpc_proto_library( |
||||
name = "keyvaluestore", |
||||
srcs = ["keyvaluestore.proto"], |
||||
) |
||||
|
||||
py_proto_library( |
||||
name = "helloworld_py_pb2", |
||||
deps = [":helloworld_proto"], |
||||
) |
||||
|
||||
py_grpc_library( |
||||
name = "helloworld_py_pb2_grpc", |
||||
srcs = [":helloworld_proto"], |
||||
deps = [":helloworld_py_pb2"], |
||||
) |
||||
|
||||
proto_library( |
||||
name = "route_guide_proto", |
||||
srcs = [":route_guide.proto"], |
||||
) |
@ -0,0 +1,50 @@ |
||||
# gRPC Python Non-Blocking Streaming RPC Client Example |
||||
|
||||
The goal of this example is to demonstrate how to handle streaming responses |
||||
without blocking the current thread. Effectively, this can be achieved by |
||||
converting the gRPC Python streaming API into callback-based. |
||||
|
||||
In this example, the RPC service `Phone` simulates the life cycle of virtual |
||||
phone calls. It requires one thread to handle the phone-call session state |
||||
changes, and another thread to process the audio stream. In this case, the |
||||
normal blocking style API could not fulfill the need easily. Hence, we should |
||||
asynchronously execute the streaming RPC. |
||||
|
||||
## Steps to run this example |
||||
|
||||
Start the server in one session |
||||
``` |
||||
python3 server.py |
||||
``` |
||||
|
||||
Start the client in another session |
||||
``` |
||||
python3 client.py |
||||
``` |
||||
|
||||
## Example Output |
||||
``` |
||||
$ python3 server.py |
||||
INFO:root:Server serving at [::]:50051 |
||||
INFO:root:Received a phone call request for number [1415926535] |
||||
INFO:root:Created a call session [{ |
||||
"sessionId": "0", |
||||
"media": "https://link.to.audio.resources" |
||||
}] |
||||
INFO:root:Call finished [1415926535] |
||||
INFO:root:Call session cleaned [{ |
||||
"sessionId": "0", |
||||
"media": "https://link.to.audio.resources" |
||||
}] |
||||
``` |
||||
|
||||
``` |
||||
$ python3 client.py |
||||
INFO:root:Waiting for peer to connect [1415926535]... |
||||
INFO:root:Call toward [1415926535] enters [NEW] state |
||||
INFO:root:Call toward [1415926535] enters [ACTIVE] state |
||||
INFO:root:Consuming audio resource [https://link.to.audio.resources] |
||||
INFO:root:Call toward [1415926535] enters [ENDED] state |
||||
INFO:root:Audio session finished [https://link.to.audio.resources] |
||||
INFO:root:Call finished! |
||||
``` |
@ -0,0 +1,119 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import logging |
||||
import threading |
||||
from typing import Iterator |
||||
from concurrent.futures import ThreadPoolExecutor |
||||
|
||||
import grpc |
||||
|
||||
import phone_pb2 |
||||
import phone_pb2_grpc |
||||
|
||||
|
||||
class CallMaker: |
||||
|
||||
def __init__(self, executor: ThreadPoolExecutor, channel: grpc.Channel, |
||||
phone_number: str) -> None: |
||||
self._executor = executor |
||||
self._channel = channel |
||||
self._stub = phone_pb2_grpc.PhoneStub(self._channel) |
||||
self._phone_number = phone_number |
||||
self._session_id = None |
||||
self._audio_session_link = None |
||||
self._call_state = None |
||||
self._peer_responded = threading.Event() |
||||
self._call_finished = threading.Event() |
||||
self._consumer_future = None |
||||
|
||||
def _response_watcher( |
||||
self, |
||||
response_iterator: Iterator[phone_pb2.StreamCallResponse]) -> None: |
||||
try: |
||||
for response in response_iterator: |
||||
# NOTE: All fields in Proto3 are optional. This is the recommended way |
||||
# to check if a field is present or not, or to exam which one-of field is |
||||
# fulfilled by this message. |
||||
if response.HasField("call_info"): |
||||
self._on_call_info(response.call_info) |
||||
elif response.HasField("call_state"): |
||||
self._on_call_state(response.call_state.state) |
||||
else: |
||||
raise RuntimeError( |
||||
"Received StreamCallResponse without call_info and call_state" |
||||
) |
||||
except Exception as e: |
||||
self._peer_responded.set() |
||||
raise |
||||
|
||||
def _on_call_info(self, call_info: phone_pb2.CallInfo) -> None: |
||||
self._session_id = call_info.session_id |
||||
self._audio_session_link = call_info.media |
||||
|
||||
def _on_call_state(self, call_state: phone_pb2.CallState.State) -> None: |
||||
logging.info("Call toward [%s] enters [%s] state", self._phone_number, |
||||
phone_pb2.CallState.State.Name(call_state)) |
||||
self._call_state = call_state |
||||
if call_state is phone_pb2.CallState.State.ACTIVE: |
||||
self._peer_responded.set() |
||||
if call_state is phone_pb2.CallState.State.ENDED: |
||||
self._peer_responded.set() |
||||
self._call_finished.set() |
||||
|
||||
def call(self) -> None: |
||||
request = phone_pb2.StreamCallRequest() |
||||
request.phone_number = self._phone_number |
||||
response_iterator = self._stub.StreamCall(iter((request,))) |
||||
# Instead of consuming the response on current thread, spawn a consumption thread. |
||||
self._consumer_future = self._executor.submit(self._response_watcher, |
||||
response_iterator) |
||||
|
||||
def wait_peer(self) -> None: |
||||
logging.info("Waiting for peer to connect [%s]...", self._phone_number) |
||||
self._peer_responded.wait(timeout=None) |
||||
if self._consumer_future.done(): |
||||
# If the future raises, forwards the exception here |
||||
self._consumer_future.result() |
||||
return self._call_state is phone_pb2.CallState.State.ACTIVE |
||||
|
||||
def audio_session(self) -> None: |
||||
assert self._audio_session_link is not None |
||||
logging.info("Consuming audio resource [%s]", self._audio_session_link) |
||||
self._call_finished.wait(timeout=None) |
||||
logging.info("Audio session finished [%s]", self._audio_session_link) |
||||
|
||||
|
||||
def process_call(executor: ThreadPoolExecutor, channel: grpc.Channel, |
||||
phone_number: str) -> None: |
||||
call_maker = CallMaker(executor, channel, phone_number) |
||||
call_maker.call() |
||||
if call_maker.wait_peer(): |
||||
call_maker.audio_session() |
||||
logging.info("Call finished!") |
||||
else: |
||||
logging.info("Call failed: peer didn't answer") |
||||
|
||||
|
||||
def run(): |
||||
executor = ThreadPoolExecutor() |
||||
with grpc.insecure_channel("localhost:50051") as channel: |
||||
future = executor.submit(process_call, executor, channel, |
||||
"555-0100-XXXX") |
||||
future.result() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
run() |
@ -0,0 +1,52 @@ |
||||
// Copyright 2020 The gRPC Authors |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
syntax = "proto3"; |
||||
|
||||
package grpc.testing; |
||||
|
||||
message CallInfo { |
||||
string session_id = 1; |
||||
string media = 2; |
||||
} |
||||
|
||||
message CallState { |
||||
enum State { |
||||
// The default state. |
||||
UNDEFINED = 0; |
||||
// The call is newly created. |
||||
NEW = 1; |
||||
// The call is connected. |
||||
ACTIVE = 6; |
||||
// The call is finished. |
||||
ENDED = 7; |
||||
} |
||||
State state = 2; |
||||
} |
||||
|
||||
message StreamCallRequest { |
||||
string phone_number = 1; |
||||
} |
||||
|
||||
message StreamCallResponse { |
||||
oneof stream_call_response { |
||||
CallInfo call_info = 1; |
||||
CallState call_state = 2; |
||||
} |
||||
} |
||||
|
||||
service Phone { |
||||
// Makes a phone call and communicate states via a stream. |
||||
rpc StreamCall(stream StreamCallRequest) returns (stream StreamCallResponse); |
||||
} |
@ -0,0 +1,267 @@ |
||||
# -*- coding: utf-8 -*- |
||||
# Generated by the protocol buffer compiler. DO NOT EDIT! |
||||
# source: phone.proto |
||||
|
||||
from google.protobuf import descriptor as _descriptor |
||||
from google.protobuf import message as _message |
||||
from google.protobuf import reflection as _reflection |
||||
from google.protobuf import symbol_database as _symbol_database |
||||
# @@protoc_insertion_point(imports) |
||||
|
||||
_sym_db = _symbol_database.Default() |
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor( |
||||
name='phone.proto', |
||||
package='grpc.testing', |
||||
syntax='proto3', |
||||
serialized_options=None, |
||||
serialized_pb=b'\n\x0bphone.proto\x12\x0cgrpc.testing\"-\n\x08\x43\x61llInfo\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\r\n\x05media\x18\x02 \x01(\t\"q\n\tCallState\x12,\n\x05state\x18\x02 \x01(\x0e\x32\x1d.grpc.testing.CallState.State\"6\n\x05State\x12\r\n\tUNDEFINED\x10\x00\x12\x07\n\x03NEW\x10\x01\x12\n\n\x06\x41\x43TIVE\x10\x06\x12\t\n\x05\x45NDED\x10\x07\")\n\x11StreamCallRequest\x12\x14\n\x0cphone_number\x18\x01 \x01(\t\"\x88\x01\n\x12StreamCallResponse\x12+\n\tcall_info\x18\x01 \x01(\x0b\x32\x16.grpc.testing.CallInfoH\x00\x12-\n\ncall_state\x18\x02 \x01(\x0b\x32\x17.grpc.testing.CallStateH\x00\x42\x16\n\x14stream_call_response2\\\n\x05Phone\x12S\n\nStreamCall\x12\x1f.grpc.testing.StreamCallRequest\x1a .grpc.testing.StreamCallResponse(\x01\x30\x01\x62\x06proto3' |
||||
) |
||||
|
||||
|
||||
|
||||
_CALLSTATE_STATE = _descriptor.EnumDescriptor( |
||||
name='State', |
||||
full_name='grpc.testing.CallState.State', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
values=[ |
||||
_descriptor.EnumValueDescriptor( |
||||
name='UNDEFINED', index=0, number=0, |
||||
serialized_options=None, |
||||
type=None), |
||||
_descriptor.EnumValueDescriptor( |
||||
name='NEW', index=1, number=1, |
||||
serialized_options=None, |
||||
type=None), |
||||
_descriptor.EnumValueDescriptor( |
||||
name='ACTIVE', index=2, number=6, |
||||
serialized_options=None, |
||||
type=None), |
||||
_descriptor.EnumValueDescriptor( |
||||
name='ENDED', index=3, number=7, |
||||
serialized_options=None, |
||||
type=None), |
||||
], |
||||
containing_type=None, |
||||
serialized_options=None, |
||||
serialized_start=135, |
||||
serialized_end=189, |
||||
) |
||||
_sym_db.RegisterEnumDescriptor(_CALLSTATE_STATE) |
||||
|
||||
|
||||
_CALLINFO = _descriptor.Descriptor( |
||||
name='CallInfo', |
||||
full_name='grpc.testing.CallInfo', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='session_id', full_name='grpc.testing.CallInfo.session_id', index=0, |
||||
number=1, type=9, cpp_type=9, label=1, |
||||
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
_descriptor.FieldDescriptor( |
||||
name='media', full_name='grpc.testing.CallInfo.media', index=1, |
||||
number=2, type=9, cpp_type=9, label=1, |
||||
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
], |
||||
serialized_options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
], |
||||
serialized_start=29, |
||||
serialized_end=74, |
||||
) |
||||
|
||||
|
||||
_CALLSTATE = _descriptor.Descriptor( |
||||
name='CallState', |
||||
full_name='grpc.testing.CallState', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='state', full_name='grpc.testing.CallState.state', index=0, |
||||
number=2, type=14, cpp_type=8, label=1, |
||||
has_default_value=False, default_value=0, |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
_CALLSTATE_STATE, |
||||
], |
||||
serialized_options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
], |
||||
serialized_start=76, |
||||
serialized_end=189, |
||||
) |
||||
|
||||
|
||||
_STREAMCALLREQUEST = _descriptor.Descriptor( |
||||
name='StreamCallRequest', |
||||
full_name='grpc.testing.StreamCallRequest', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='phone_number', full_name='grpc.testing.StreamCallRequest.phone_number', index=0, |
||||
number=1, type=9, cpp_type=9, label=1, |
||||
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
], |
||||
serialized_options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
], |
||||
serialized_start=191, |
||||
serialized_end=232, |
||||
) |
||||
|
||||
|
||||
_STREAMCALLRESPONSE = _descriptor.Descriptor( |
||||
name='StreamCallResponse', |
||||
full_name='grpc.testing.StreamCallResponse', |
||||
filename=None, |
||||
file=DESCRIPTOR, |
||||
containing_type=None, |
||||
fields=[ |
||||
_descriptor.FieldDescriptor( |
||||
name='call_info', full_name='grpc.testing.StreamCallResponse.call_info', index=0, |
||||
number=1, type=11, cpp_type=10, label=1, |
||||
has_default_value=False, default_value=None, |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
_descriptor.FieldDescriptor( |
||||
name='call_state', full_name='grpc.testing.StreamCallResponse.call_state', index=1, |
||||
number=2, type=11, cpp_type=10, label=1, |
||||
has_default_value=False, default_value=None, |
||||
message_type=None, enum_type=None, containing_type=None, |
||||
is_extension=False, extension_scope=None, |
||||
serialized_options=None, file=DESCRIPTOR), |
||||
], |
||||
extensions=[ |
||||
], |
||||
nested_types=[], |
||||
enum_types=[ |
||||
], |
||||
serialized_options=None, |
||||
is_extendable=False, |
||||
syntax='proto3', |
||||
extension_ranges=[], |
||||
oneofs=[ |
||||
_descriptor.OneofDescriptor( |
||||
name='stream_call_response', full_name='grpc.testing.StreamCallResponse.stream_call_response', |
||||
index=0, containing_type=None, fields=[]), |
||||
], |
||||
serialized_start=235, |
||||
serialized_end=371, |
||||
) |
||||
|
||||
_CALLSTATE.fields_by_name['state'].enum_type = _CALLSTATE_STATE |
||||
_CALLSTATE_STATE.containing_type = _CALLSTATE |
||||
_STREAMCALLRESPONSE.fields_by_name['call_info'].message_type = _CALLINFO |
||||
_STREAMCALLRESPONSE.fields_by_name['call_state'].message_type = _CALLSTATE |
||||
_STREAMCALLRESPONSE.oneofs_by_name['stream_call_response'].fields.append( |
||||
_STREAMCALLRESPONSE.fields_by_name['call_info']) |
||||
_STREAMCALLRESPONSE.fields_by_name['call_info'].containing_oneof = _STREAMCALLRESPONSE.oneofs_by_name['stream_call_response'] |
||||
_STREAMCALLRESPONSE.oneofs_by_name['stream_call_response'].fields.append( |
||||
_STREAMCALLRESPONSE.fields_by_name['call_state']) |
||||
_STREAMCALLRESPONSE.fields_by_name['call_state'].containing_oneof = _STREAMCALLRESPONSE.oneofs_by_name['stream_call_response'] |
||||
DESCRIPTOR.message_types_by_name['CallInfo'] = _CALLINFO |
||||
DESCRIPTOR.message_types_by_name['CallState'] = _CALLSTATE |
||||
DESCRIPTOR.message_types_by_name['StreamCallRequest'] = _STREAMCALLREQUEST |
||||
DESCRIPTOR.message_types_by_name['StreamCallResponse'] = _STREAMCALLRESPONSE |
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR) |
||||
|
||||
CallInfo = _reflection.GeneratedProtocolMessageType('CallInfo', (_message.Message,), { |
||||
'DESCRIPTOR' : _CALLINFO, |
||||
'__module__' : 'phone_pb2' |
||||
# @@protoc_insertion_point(class_scope:grpc.testing.CallInfo) |
||||
}) |
||||
_sym_db.RegisterMessage(CallInfo) |
||||
|
||||
CallState = _reflection.GeneratedProtocolMessageType('CallState', (_message.Message,), { |
||||
'DESCRIPTOR' : _CALLSTATE, |
||||
'__module__' : 'phone_pb2' |
||||
# @@protoc_insertion_point(class_scope:grpc.testing.CallState) |
||||
}) |
||||
_sym_db.RegisterMessage(CallState) |
||||
|
||||
StreamCallRequest = _reflection.GeneratedProtocolMessageType('StreamCallRequest', (_message.Message,), { |
||||
'DESCRIPTOR' : _STREAMCALLREQUEST, |
||||
'__module__' : 'phone_pb2' |
||||
# @@protoc_insertion_point(class_scope:grpc.testing.StreamCallRequest) |
||||
}) |
||||
_sym_db.RegisterMessage(StreamCallRequest) |
||||
|
||||
StreamCallResponse = _reflection.GeneratedProtocolMessageType('StreamCallResponse', (_message.Message,), { |
||||
'DESCRIPTOR' : _STREAMCALLRESPONSE, |
||||
'__module__' : 'phone_pb2' |
||||
# @@protoc_insertion_point(class_scope:grpc.testing.StreamCallResponse) |
||||
}) |
||||
_sym_db.RegisterMessage(StreamCallResponse) |
||||
|
||||
|
||||
|
||||
_PHONE = _descriptor.ServiceDescriptor( |
||||
name='Phone', |
||||
full_name='grpc.testing.Phone', |
||||
file=DESCRIPTOR, |
||||
index=0, |
||||
serialized_options=None, |
||||
serialized_start=373, |
||||
serialized_end=465, |
||||
methods=[ |
||||
_descriptor.MethodDescriptor( |
||||
name='StreamCall', |
||||
full_name='grpc.testing.Phone.StreamCall', |
||||
index=0, |
||||
containing_service=None, |
||||
input_type=_STREAMCALLREQUEST, |
||||
output_type=_STREAMCALLRESPONSE, |
||||
serialized_options=None, |
||||
), |
||||
]) |
||||
_sym_db.RegisterServiceDescriptor(_PHONE) |
||||
|
||||
DESCRIPTOR.services_by_name['Phone'] = _PHONE |
||||
|
||||
# @@protoc_insertion_point(module_scope) |
@ -0,0 +1,65 @@ |
||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! |
||||
import grpc |
||||
|
||||
import phone_pb2 as phone__pb2 |
||||
|
||||
|
||||
class PhoneStub(object): |
||||
"""Missing associated documentation comment in .proto file""" |
||||
|
||||
def __init__(self, channel): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
channel: A grpc.Channel. |
||||
""" |
||||
self.StreamCall = channel.stream_stream( |
||||
'/grpc.testing.Phone/StreamCall', |
||||
request_serializer=phone__pb2.StreamCallRequest.SerializeToString, |
||||
response_deserializer=phone__pb2.StreamCallResponse.FromString, |
||||
) |
||||
|
||||
|
||||
class PhoneServicer(object): |
||||
"""Missing associated documentation comment in .proto file""" |
||||
|
||||
def StreamCall(self, request_iterator, context): |
||||
"""Makes a phone call and communicate states via a stream. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
|
||||
def add_PhoneServicer_to_server(servicer, server): |
||||
rpc_method_handlers = { |
||||
'StreamCall': grpc.stream_stream_rpc_method_handler( |
||||
servicer.StreamCall, |
||||
request_deserializer=phone__pb2.StreamCallRequest.FromString, |
||||
response_serializer=phone__pb2.StreamCallResponse.SerializeToString, |
||||
), |
||||
} |
||||
generic_handler = grpc.method_handlers_generic_handler( |
||||
'grpc.testing.Phone', rpc_method_handlers) |
||||
server.add_generic_rpc_handlers((generic_handler,)) |
||||
|
||||
|
||||
# This class is part of an EXPERIMENTAL API. |
||||
class Phone(object): |
||||
"""Missing associated documentation comment in .proto file""" |
||||
|
||||
@staticmethod |
||||
def StreamCall(request_iterator, |
||||
target, |
||||
options=(), |
||||
channel_credentials=None, |
||||
call_credentials=None, |
||||
compression=None, |
||||
wait_for_ready=None, |
||||
timeout=None, |
||||
metadata=None): |
||||
return grpc.experimental.stream_stream(request_iterator, target, '/grpc.testing.Phone/StreamCall', |
||||
phone__pb2.StreamCallRequest.SerializeToString, |
||||
phone__pb2.StreamCallResponse.FromString, |
||||
options, channel_credentials, |
||||
call_credentials, compression, wait_for_ready, timeout, metadata) |
@ -0,0 +1,92 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import logging |
||||
import time |
||||
from concurrent.futures import ThreadPoolExecutor |
||||
from typing import Iterable |
||||
import threading |
||||
|
||||
import grpc |
||||
from google.protobuf.json_format import MessageToJson |
||||
|
||||
import phone_pb2 |
||||
import phone_pb2_grpc |
||||
|
||||
|
||||
def create_state_response(call_state: phone_pb2.CallState.State |
||||
) -> phone_pb2.StreamCallResponse: |
||||
response = phone_pb2.StreamCallResponse() |
||||
response.call_state.state = call_state |
||||
return response |
||||
|
||||
|
||||
class Phone(phone_pb2_grpc.PhoneServicer): |
||||
|
||||
def __init__(self): |
||||
self._id_counter = 0 |
||||
self._lock = threading.RLock() |
||||
|
||||
def _create_call_session(self) -> phone_pb2.CallInfo: |
||||
call_info = phone_pb2.CallInfo() |
||||
with self._lock: |
||||
call_info.session_id = str(self._id_counter) |
||||
self._id_counter += 1 |
||||
call_info.media = "https://link.to.audio.resources" |
||||
logging.info("Created a call session [%s]", MessageToJson(call_info)) |
||||
return call_info |
||||
|
||||
def _clean_call_session(self, call_info: phone_pb2.CallInfo) -> None: |
||||
logging.info("Call session cleaned [%s]", MessageToJson(call_info)) |
||||
|
||||
def StreamCall(self, |
||||
request_iterator: Iterable[phone_pb2.StreamCallRequest], |
||||
context: grpc.ServicerContext |
||||
) -> Iterable[phone_pb2.StreamCallResponse]: |
||||
try: |
||||
request = next(request_iterator) |
||||
logging.info("Received a phone call request for number [%s]", |
||||
request.phone_number) |
||||
except StopIteration: |
||||
raise RuntimeError("Failed to receive call request") |
||||
# Simulate the acceptance of call request |
||||
time.sleep(1) |
||||
yield create_state_response(phone_pb2.CallState.NEW) |
||||
# Simulate the start of the call session |
||||
time.sleep(1) |
||||
call_info = self._create_call_session() |
||||
context.add_callback(lambda: self._clean_call_session(call_info)) |
||||
response = phone_pb2.StreamCallResponse() |
||||
response.call_info.session_id = call_info.session_id |
||||
response.call_info.media = call_info.media |
||||
yield response |
||||
yield create_state_response(phone_pb2.CallState.ACTIVE) |
||||
# Simulate the end of the call |
||||
time.sleep(2) |
||||
yield create_state_response(phone_pb2.CallState.ENDED) |
||||
logging.info("Call finished [%s]", request.phone_number) |
||||
|
||||
|
||||
def serve(address: str) -> None: |
||||
server = grpc.server(ThreadPoolExecutor()) |
||||
phone_pb2_grpc.add_PhoneServicer_to_server(Phone(), server) |
||||
server.add_insecure_port(address) |
||||
server.start() |
||||
logging.info("Server serving at %s", address) |
||||
server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
logging.basicConfig(level=logging.INFO) |
||||
serve("[::]:50051") |
@ -0,0 +1,83 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The Python AsyncIO example of utilizing Channelz feature.""" |
||||
|
||||
import asyncio |
||||
import argparse |
||||
import logging |
||||
import random |
||||
|
||||
import grpc |
||||
|
||||
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( |
||||
"helloworld.proto") |
||||
|
||||
# TODO: Suppress until the macOS segfault fix rolled out |
||||
from grpc_channelz.v1 import channelz # pylint: disable=wrong-import-position |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
_RANDOM_FAILURE_RATE = 0.3 |
||||
|
||||
|
||||
class FaultInjectGreeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
def __init__(self, failure_rate): |
||||
self._failure_rate = failure_rate |
||||
|
||||
async def SayHello(self, request: helloworld_pb2.HelloRequest, |
||||
context: grpc.aio.ServicerContext |
||||
) -> helloworld_pb2.HelloReply: |
||||
if random.random() < self._failure_rate: |
||||
context.abort(grpc.StatusCode.UNAVAILABLE, |
||||
'Randomly injected failure.') |
||||
return helloworld_pb2.HelloReply(message=f'Hello, {request.name}!') |
||||
|
||||
|
||||
def create_server(addr: str, failure_rate: float) -> grpc.aio.Server: |
||||
server = grpc.aio.server() |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server( |
||||
FaultInjectGreeter(failure_rate), server) |
||||
|
||||
# Add Channelz Servicer to the gRPC server |
||||
channelz.add_channelz_servicer(server) |
||||
|
||||
server.add_insecure_port(addr) |
||||
return server |
||||
|
||||
|
||||
async def main() -> None: |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--addr', |
||||
nargs=1, |
||||
type=str, |
||||
default='[::]:50051', |
||||
help='the address to listen on') |
||||
parser.add_argument( |
||||
'--failure_rate', |
||||
nargs=1, |
||||
type=float, |
||||
default=0.3, |
||||
help='a float indicates the percentage of failed message injections') |
||||
args = parser.parse_args() |
||||
|
||||
server = create_server(addr=args.addr, failure_rate=args.failure_rate) |
||||
await server.start() |
||||
await server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.get_event_loop().run_until_complete(main()) |
@ -0,0 +1,46 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Poll statistics from the server.""" |
||||
|
||||
import asyncio |
||||
import logging |
||||
import argparse |
||||
import grpc |
||||
|
||||
from grpc_channelz.v1 import channelz_pb2 |
||||
from grpc_channelz.v1 import channelz_pb2_grpc |
||||
|
||||
|
||||
async def run(addr: str) -> None: |
||||
async with grpc.aio.insecure_channel(addr) as channel: |
||||
channelz_stub = channelz_pb2_grpc.ChannelzStub(channel) |
||||
response = await channelz_stub.GetServers( |
||||
channelz_pb2.GetServersRequest(start_server_id=0)) |
||||
print('Info for all servers: %s' % response) |
||||
|
||||
|
||||
async def main() -> None: |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--addr', |
||||
nargs=1, |
||||
type=str, |
||||
default='[::]:50051', |
||||
help='the address to request') |
||||
args = parser.parse_args() |
||||
run(addr=args.addr) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig() |
||||
asyncio.get_event_loop().run_until_complete(main()) |
@ -0,0 +1,61 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""Send multiple greeting messages to the backend.""" |
||||
|
||||
import asyncio |
||||
import logging |
||||
import argparse |
||||
import grpc |
||||
|
||||
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( |
||||
"helloworld.proto") |
||||
|
||||
|
||||
async def process(stub: helloworld_pb2_grpc.GreeterStub, |
||||
request: helloworld_pb2.HelloRequest) -> None: |
||||
try: |
||||
response = await stub.SayHello(request) |
||||
except grpc.aio.AioRpcError as rpc_error: |
||||
print(f'Received error: {rpc_error}') |
||||
else: |
||||
print(f'Received message: {response}') |
||||
|
||||
|
||||
async def run(addr: str, n: int) -> None: |
||||
async with grpc.aio.insecure_channel(addr) as channel: |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
request = helloworld_pb2.HelloRequest(name='you') |
||||
for _ in range(n): |
||||
await process(stub, request) |
||||
|
||||
|
||||
async def main() -> None: |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--addr', |
||||
nargs=1, |
||||
type=str, |
||||
default='[::]:50051', |
||||
help='the address to request') |
||||
parser.add_argument('-n', |
||||
nargs=1, |
||||
type=int, |
||||
default=10, |
||||
help='an integer for number of messages to sent') |
||||
args = parser.parse_args() |
||||
await run(addr=args.addr, n=args.n) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.get_event_loop().run_until_complete(main()) |
@ -0,0 +1 @@ |
||||
../../protos/helloworld.proto |
@ -0,0 +1,129 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The Python AsyncIO implementation of the gRPC route guide client.""" |
||||
|
||||
import asyncio |
||||
import random |
||||
import logging |
||||
from typing import List, Iterable |
||||
|
||||
import grpc |
||||
|
||||
import route_guide_pb2 |
||||
import route_guide_pb2_grpc |
||||
import route_guide_resources |
||||
|
||||
|
||||
def make_route_note(message: str, latitude: int, |
||||
longitude: int) -> route_guide_pb2.RouteNote: |
||||
return route_guide_pb2.RouteNote( |
||||
message=message, |
||||
location=route_guide_pb2.Point(latitude=latitude, longitude=longitude)) |
||||
|
||||
|
||||
# Performs an unary call |
||||
async def guide_get_one_feature(stub: route_guide_pb2_grpc.RouteGuideStub, |
||||
point: route_guide_pb2.Point) -> None: |
||||
feature = await stub.GetFeature(point) |
||||
if not feature.location: |
||||
print("Server returned incomplete feature") |
||||
return |
||||
|
||||
if feature.name: |
||||
print(f"Feature called {feature.name} at {feature.location}") |
||||
else: |
||||
print(f"Found no feature at {feature.location}") |
||||
|
||||
|
||||
async def guide_get_feature(stub: route_guide_pb2_grpc.RouteGuideStub) -> None: |
||||
await guide_get_one_feature( |
||||
stub, route_guide_pb2.Point(latitude=409146138, longitude=-746188906)) |
||||
await guide_get_one_feature(stub, |
||||
route_guide_pb2.Point(latitude=0, longitude=0)) |
||||
|
||||
|
||||
# Performs a server-streaming call |
||||
async def guide_list_features(stub: route_guide_pb2_grpc.RouteGuideStub |
||||
) -> None: |
||||
rectangle = route_guide_pb2.Rectangle( |
||||
lo=route_guide_pb2.Point(latitude=400000000, longitude=-750000000), |
||||
hi=route_guide_pb2.Point(latitude=420000000, longitude=-730000000)) |
||||
print("Looking for features between 40, -75 and 42, -73") |
||||
|
||||
features = stub.ListFeatures(rectangle) |
||||
|
||||
async for feature in features: |
||||
print(f"Feature called {feature.name} at {feature.location}") |
||||
|
||||
|
||||
def generate_route(feature_list: List[route_guide_pb2.Feature] |
||||
) -> Iterable[route_guide_pb2.Point]: |
||||
for _ in range(0, 10): |
||||
random_feature = random.choice(feature_list) |
||||
print(f"Visiting point {random_feature.location}") |
||||
yield random_feature.location |
||||
|
||||
|
||||
# Performs a client-streaming call |
||||
async def guide_record_route(stub: route_guide_pb2_grpc.RouteGuideStub) -> None: |
||||
feature_list = route_guide_resources.read_route_guide_database() |
||||
route_iterator = generate_route(feature_list) |
||||
|
||||
# gRPC AsyncIO client-streaming RPC API accepts both synchronous iterables |
||||
# and async iterables. |
||||
route_summary = await stub.RecordRoute(route_iterator) |
||||
print(f"Finished trip with {route_summary.point_count} points") |
||||
print(f"Passed {route_summary.feature_count} features") |
||||
print(f"Travelled {route_summary.distance} meters") |
||||
print(f"It took {route_summary.elapsed_time} seconds") |
||||
|
||||
|
||||
def generate_messages() -> Iterable[route_guide_pb2.RouteNote]: |
||||
messages = [ |
||||
make_route_note("First message", 0, 0), |
||||
make_route_note("Second message", 0, 1), |
||||
make_route_note("Third message", 1, 0), |
||||
make_route_note("Fourth message", 0, 0), |
||||
make_route_note("Fifth message", 1, 0), |
||||
] |
||||
for msg in messages: |
||||
print(f"Sending {msg.message} at {msg.location}") |
||||
yield msg |
||||
|
||||
|
||||
# Performs a bidi-streaming call |
||||
async def guide_route_chat(stub: route_guide_pb2_grpc.RouteGuideStub) -> None: |
||||
# gRPC AsyncIO bidi-streaming RPC API accepts both synchronous iterables |
||||
# and async iterables. |
||||
call = stub.RouteChat(generate_messages()) |
||||
async for response in call: |
||||
print(f"Received message {response.message} at {response.location}") |
||||
|
||||
|
||||
async def main() -> None: |
||||
async with grpc.aio.insecure_channel('localhost:50051') as channel: |
||||
stub = route_guide_pb2_grpc.RouteGuideStub(channel) |
||||
print("-------------- GetFeature --------------") |
||||
await guide_get_feature(stub) |
||||
print("-------------- ListFeatures --------------") |
||||
await guide_list_features(stub) |
||||
print("-------------- RecordRoute --------------") |
||||
await guide_record_route(stub) |
||||
print("-------------- RouteChat --------------") |
||||
await guide_route_chat(stub) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.get_event_loop().run_until_complete(main()) |
@ -0,0 +1,134 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The Python AsyncIO implementation of the gRPC route guide server.""" |
||||
|
||||
import asyncio |
||||
import time |
||||
import math |
||||
import logging |
||||
from typing import AsyncIterable, Iterable |
||||
|
||||
import grpc |
||||
|
||||
import route_guide_pb2 |
||||
import route_guide_pb2_grpc |
||||
import route_guide_resources |
||||
|
||||
|
||||
def get_feature(feature_db: Iterable[route_guide_pb2.Feature], |
||||
point: route_guide_pb2.Point) -> route_guide_pb2.Feature: |
||||
"""Returns Feature at given location or None.""" |
||||
for feature in feature_db: |
||||
if feature.location == point: |
||||
return feature |
||||
return None |
||||
|
||||
|
||||
def get_distance(start: route_guide_pb2.Point, |
||||
end: route_guide_pb2.Point) -> float: |
||||
"""Distance between two points.""" |
||||
coord_factor = 10000000.0 |
||||
lat_1 = start.latitude / coord_factor |
||||
lat_2 = end.latitude / coord_factor |
||||
lon_1 = start.longitude / coord_factor |
||||
lon_2 = end.longitude / coord_factor |
||||
lat_rad_1 = math.radians(lat_1) |
||||
lat_rad_2 = math.radians(lat_2) |
||||
delta_lat_rad = math.radians(lat_2 - lat_1) |
||||
delta_lon_rad = math.radians(lon_2 - lon_1) |
||||
|
||||
# Formula is based on http://mathforum.org/library/drmath/view/51879.html |
||||
a = (pow(math.sin(delta_lat_rad / 2), 2) + |
||||
(math.cos(lat_rad_1) * math.cos(lat_rad_2) * |
||||
pow(math.sin(delta_lon_rad / 2), 2))) |
||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) |
||||
R = 6371000 |
||||
# metres |
||||
return R * c |
||||
|
||||
|
||||
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer): |
||||
"""Provides methods that implement functionality of route guide server.""" |
||||
|
||||
def __init__(self) -> None: |
||||
self.db = route_guide_resources.read_route_guide_database() |
||||
|
||||
def GetFeature(self, request: route_guide_pb2.Point, |
||||
unused_context) -> route_guide_pb2.Feature: |
||||
feature = get_feature(self.db, request) |
||||
if feature is None: |
||||
return route_guide_pb2.Feature(name="", location=request) |
||||
else: |
||||
return feature |
||||
|
||||
async def ListFeatures(self, request: route_guide_pb2.Rectangle, |
||||
unused_context |
||||
) -> AsyncIterable[route_guide_pb2.Feature]: |
||||
left = min(request.lo.longitude, request.hi.longitude) |
||||
right = max(request.lo.longitude, request.hi.longitude) |
||||
top = max(request.lo.latitude, request.hi.latitude) |
||||
bottom = min(request.lo.latitude, request.hi.latitude) |
||||
for feature in self.db: |
||||
if (feature.location.longitude >= left and |
||||
feature.location.longitude <= right and |
||||
feature.location.latitude >= bottom and |
||||
feature.location.latitude <= top): |
||||
yield feature |
||||
|
||||
async def RecordRoute( |
||||
self, request_iterator: AsyncIterable[route_guide_pb2.Point], |
||||
unused_context) -> route_guide_pb2.RouteSummary: |
||||
point_count = 0 |
||||
feature_count = 0 |
||||
distance = 0.0 |
||||
prev_point = None |
||||
|
||||
start_time = time.time() |
||||
async for point in request_iterator: |
||||
point_count += 1 |
||||
if get_feature(self.db, point): |
||||
feature_count += 1 |
||||
if prev_point: |
||||
distance += get_distance(prev_point, point) |
||||
prev_point = point |
||||
|
||||
elapsed_time = time.time() - start_time |
||||
return route_guide_pb2.RouteSummary(point_count=point_count, |
||||
feature_count=feature_count, |
||||
distance=int(distance), |
||||
elapsed_time=int(elapsed_time)) |
||||
|
||||
async def RouteChat( |
||||
self, request_iterator: AsyncIterable[route_guide_pb2.RouteNote], |
||||
unused_context) -> AsyncIterable[route_guide_pb2.RouteNote]: |
||||
prev_notes = [] |
||||
async for new_note in request_iterator: |
||||
for prev_note in prev_notes: |
||||
if prev_note.location == new_note.location: |
||||
yield prev_note |
||||
prev_notes.append(new_note) |
||||
|
||||
|
||||
async def serve() -> None: |
||||
server = grpc.aio.server() |
||||
route_guide_pb2_grpc.add_RouteGuideServicer_to_server( |
||||
RouteGuideServicer(), server) |
||||
server.add_insecure_port('[::]:50051') |
||||
await server.start() |
||||
await server.wait_for_termination() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.get_event_loop().run_until_complete(serve()) |
@ -0,0 +1,109 @@ |
||||
# Copyright 2020 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
"""The Python example of utilizing wait-for-ready flag.""" |
||||
|
||||
import asyncio |
||||
import logging |
||||
from contextlib import contextmanager |
||||
import socket |
||||
from typing import Iterable |
||||
|
||||
import grpc |
||||
|
||||
helloworld_pb2, helloworld_pb2_grpc = grpc.protos_and_services( |
||||
"helloworld.proto") |
||||
|
||||
_LOGGER = logging.getLogger(__name__) |
||||
_LOGGER.setLevel(logging.INFO) |
||||
|
||||
|
||||
@contextmanager |
||||
def get_free_loopback_tcp_port() -> Iterable[str]: |
||||
if socket.has_ipv6: |
||||
tcp_socket = socket.socket(socket.AF_INET6) |
||||
else: |
||||
tcp_socket = socket.socket(socket.AF_INET) |
||||
tcp_socket.bind(('', 0)) |
||||
address_tuple = tcp_socket.getsockname() |
||||
yield f"localhost:{address_tuple[1]}" |
||||
tcp_socket.close() |
||||
|
||||
|
||||
class Greeter(helloworld_pb2_grpc.GreeterServicer): |
||||
|
||||
async def SayHello(self, request: helloworld_pb2.HelloRequest, |
||||
unused_context) -> helloworld_pb2.HelloReply: |
||||
return helloworld_pb2.HelloReply(message=f'Hello, {request.name}!') |
||||
|
||||
|
||||
def create_server(server_address: str): |
||||
server = grpc.aio.server() |
||||
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) |
||||
bound_port = server.add_insecure_port(server_address) |
||||
assert bound_port == int(server_address.split(':')[-1]) |
||||
return server |
||||
|
||||
|
||||
async def process(stub: helloworld_pb2_grpc.GreeterStub, |
||||
wait_for_ready: bool = None) -> None: |
||||
try: |
||||
response = await stub.SayHello(helloworld_pb2.HelloRequest(name='you'), |
||||
wait_for_ready=wait_for_ready) |
||||
message = response.message |
||||
except grpc.aio.AioRpcError as rpc_error: |
||||
assert rpc_error.code() == grpc.StatusCode.UNAVAILABLE |
||||
assert not wait_for_ready |
||||
message = rpc_error |
||||
else: |
||||
assert wait_for_ready |
||||
_LOGGER.info("Wait-for-ready %s, client received: %s", |
||||
"enabled" if wait_for_ready else "disabled", message) |
||||
|
||||
|
||||
async def main() -> None: |
||||
# Pick a random free port |
||||
with get_free_loopback_tcp_port() as server_address: |
||||
# Create gRPC channel |
||||
channel = grpc.aio.insecure_channel(server_address) |
||||
stub = helloworld_pb2_grpc.GreeterStub(channel) |
||||
|
||||
# Fire an RPC without wait_for_ready |
||||
fail_fast_task = asyncio.get_event_loop().create_task( |
||||
process(stub, wait_for_ready=False)) |
||||
# Fire an RPC with wait_for_ready |
||||
wait_for_ready_task = asyncio.get_event_loop().create_task( |
||||
process(stub, wait_for_ready=True)) |
||||
|
||||
# Wait for the channel entering TRANSIENT FAILURE state. |
||||
state = channel.get_state() |
||||
while state != grpc.ChannelConnectivity.TRANSIENT_FAILURE: |
||||
await channel.wait_for_state_change(state) |
||||
state = channel.get_state() |
||||
|
||||
# Start the server to handle the RPC |
||||
server = create_server(server_address) |
||||
await server.start() |
||||
|
||||
# Expected to fail with StatusCode.UNAVAILABLE. |
||||
await fail_fast_task |
||||
# Expected to success. |
||||
await wait_for_ready_task |
||||
|
||||
await server.stop(None) |
||||
await channel.close() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
logging.basicConfig(level=logging.INFO) |
||||
asyncio.get_event_loop().run_until_complete(main()) |
@ -0,0 +1 @@ |
||||
../../protos/helloworld.proto |
@ -0,0 +1,43 @@ |
||||
//
|
||||
//
|
||||
// Copyright 2020 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef GRPCPP_XDS_SERVER_BUILDER_H |
||||
#define GRPCPP_XDS_SERVER_BUILDER_H |
||||
|
||||
#include <grpc/impl/codegen/port_platform.h> |
||||
|
||||
#include <grpcpp/server_builder.h> |
||||
|
||||
namespace grpc { |
||||
namespace experimental { |
||||
|
||||
class XdsServerBuilder : public ::grpc::ServerBuilder { |
||||
public: |
||||
std::unique_ptr<Server> BuildAndStart() override { |
||||
grpc_server_config_fetcher* fetcher = |
||||
grpc_server_config_fetcher_xds_create(); |
||||
if (fetcher == nullptr) return nullptr; |
||||
set_fetcher(fetcher); |
||||
return ServerBuilder::BuildAndStart(); |
||||
} |
||||
}; |
||||
|
||||
} // namespace experimental
|
||||
} // namespace grpc
|
||||
|
||||
#endif /* GRPCPP_XDS_SERVER_BUILDER_H */ |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,186 @@ |
||||
//
|
||||
// Copyright 2020 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/ext/filters/client_channel/dynamic_filters.h" |
||||
|
||||
#include "src/core/lib/channel/channel_stack.h" |
||||
#include "src/core/lib/surface/lame_client.h" |
||||
|
||||
// Conversion between call and call stack.
|
||||
#define CALL_TO_CALL_STACK(call) \ |
||||
(grpc_call_stack*)((char*)(call) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
|
||||
sizeof(DynamicFilters::Call))) |
||||
#define CALL_STACK_TO_CALL(callstack) \ |
||||
(DynamicFilters::Call*)(((char*)(call_stack)) - \
|
||||
GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \
|
||||
sizeof(DynamicFilters::Call))) |
||||
|
||||
namespace grpc_core { |
||||
|
||||
//
|
||||
// DynamicFilters::Call
|
||||
//
|
||||
|
||||
DynamicFilters::Call::Call(Args args, grpc_error** error) |
||||
: channel_stack_(std::move(args.channel_stack)) { |
||||
grpc_call_stack* call_stack = CALL_TO_CALL_STACK(this); |
||||
const grpc_call_element_args call_args = { |
||||
call_stack, /* call_stack */ |
||||
nullptr, /* server_transport_data */ |
||||
args.context, /* context */ |
||||
args.path, /* path */ |
||||
args.start_time, /* start_time */ |
||||
args.deadline, /* deadline */ |
||||
args.arena, /* arena */ |
||||
args.call_combiner /* call_combiner */ |
||||
}; |
||||
*error = grpc_call_stack_init(channel_stack_->channel_stack_, 1, Destroy, |
||||
this, &call_args); |
||||
if (GPR_UNLIKELY(*error != GRPC_ERROR_NONE)) { |
||||
const char* error_string = grpc_error_string(*error); |
||||
gpr_log(GPR_ERROR, "error: %s", error_string); |
||||
return; |
||||
} |
||||
grpc_call_stack_set_pollset_or_pollset_set(call_stack, args.pollent); |
||||
} |
||||
|
||||
void DynamicFilters::Call::StartTransportStreamOpBatch( |
||||
grpc_transport_stream_op_batch* batch) { |
||||
grpc_call_stack* call_stack = CALL_TO_CALL_STACK(this); |
||||
grpc_call_element* top_elem = grpc_call_stack_element(call_stack, 0); |
||||
GRPC_CALL_LOG_OP(GPR_INFO, top_elem, batch); |
||||
top_elem->filter->start_transport_stream_op_batch(top_elem, batch); |
||||
} |
||||
|
||||
void DynamicFilters::Call::SetAfterCallStackDestroy(grpc_closure* closure) { |
||||
GPR_ASSERT(after_call_stack_destroy_ == nullptr); |
||||
GPR_ASSERT(closure != nullptr); |
||||
after_call_stack_destroy_ = closure; |
||||
} |
||||
|
||||
RefCountedPtr<DynamicFilters::Call> DynamicFilters::Call::Ref() { |
||||
IncrementRefCount(); |
||||
return RefCountedPtr<DynamicFilters::Call>(this); |
||||
} |
||||
|
||||
RefCountedPtr<DynamicFilters::Call> DynamicFilters::Call::Ref( |
||||
const grpc_core::DebugLocation& location, const char* reason) { |
||||
IncrementRefCount(location, reason); |
||||
return RefCountedPtr<DynamicFilters::Call>(this); |
||||
} |
||||
|
||||
void DynamicFilters::Call::Unref() { |
||||
GRPC_CALL_STACK_UNREF(CALL_TO_CALL_STACK(this), ""); |
||||
} |
||||
|
||||
void DynamicFilters::Call::Unref(const DebugLocation& /*location*/, |
||||
const char* reason) { |
||||
GRPC_CALL_STACK_UNREF(CALL_TO_CALL_STACK(this), reason); |
||||
} |
||||
|
||||
void DynamicFilters::Call::Destroy(void* arg, grpc_error* /*error*/) { |
||||
DynamicFilters::Call* self = static_cast<DynamicFilters::Call*>(arg); |
||||
// Keep some members before destroying the subchannel call.
|
||||
grpc_closure* after_call_stack_destroy = self->after_call_stack_destroy_; |
||||
RefCountedPtr<DynamicFilters> channel_stack = std::move(self->channel_stack_); |
||||
// Destroy the subchannel call.
|
||||
self->~Call(); |
||||
// Destroy the call stack. This should be after destroying the call, because
|
||||
// call->after_call_stack_destroy(), if not null, will free the call arena.
|
||||
grpc_call_stack_destroy(CALL_TO_CALL_STACK(self), nullptr, |
||||
after_call_stack_destroy); |
||||
// Automatically reset channel_stack. This should be after destroying the call
|
||||
// stack, because destroying call stack needs access to the channel stack.
|
||||
} |
||||
|
||||
void DynamicFilters::Call::IncrementRefCount() { |
||||
GRPC_CALL_STACK_REF(CALL_TO_CALL_STACK(this), ""); |
||||
} |
||||
|
||||
void DynamicFilters::Call::IncrementRefCount( |
||||
const grpc_core::DebugLocation& /*location*/, const char* reason) { |
||||
GRPC_CALL_STACK_REF(CALL_TO_CALL_STACK(this), reason); |
||||
} |
||||
|
||||
//
|
||||
// DynamicFilters
|
||||
//
|
||||
|
||||
namespace { |
||||
|
||||
void DestroyChannelStack(void* arg, grpc_error* /*error*/) { |
||||
grpc_channel_stack* channel_stack = static_cast<grpc_channel_stack*>(arg); |
||||
grpc_channel_stack_destroy(channel_stack); |
||||
gpr_free(channel_stack); |
||||
} |
||||
|
||||
std::pair<grpc_channel_stack*, grpc_error*> CreateChannelStack( |
||||
const grpc_channel_args* args, |
||||
std::vector<const grpc_channel_filter*> filters) { |
||||
// Allocate memory for channel stack.
|
||||
const size_t channel_stack_size = |
||||
grpc_channel_stack_size(filters.data(), filters.size()); |
||||
grpc_channel_stack* channel_stack = |
||||
reinterpret_cast<grpc_channel_stack*>(gpr_zalloc(channel_stack_size)); |
||||
// Initialize stack.
|
||||
grpc_error* error = grpc_channel_stack_init( |
||||
/*initial_refs=*/1, DestroyChannelStack, channel_stack, filters.data(), |
||||
filters.size(), args, /*optional_transport=*/nullptr, "DynamicFilters", |
||||
channel_stack); |
||||
if (error != GRPC_ERROR_NONE) { |
||||
gpr_log(GPR_ERROR, "error initializing client internal stack: %s", |
||||
grpc_error_string(error)); |
||||
grpc_channel_stack_destroy(channel_stack); |
||||
gpr_free(channel_stack); |
||||
return {nullptr, error}; |
||||
} |
||||
return {channel_stack, GRPC_ERROR_NONE}; |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
RefCountedPtr<DynamicFilters> DynamicFilters::Create( |
||||
const grpc_channel_args* args, |
||||
std::vector<const grpc_channel_filter*> filters) { |
||||
// Attempt to create channel stack from requested filters.
|
||||
auto p = CreateChannelStack(args, std::move(filters)); |
||||
if (p.second != GRPC_ERROR_NONE) { |
||||
// Initial pass failed. Create with lame filter.
|
||||
grpc_error* error = p.second; |
||||
p = CreateChannelStack(args, {&grpc_lame_filter}); |
||||
GPR_ASSERT(p.second == GRPC_ERROR_NONE); |
||||
grpc_channel_element* elem = grpc_channel_stack_element(p.first, 0); |
||||
SetLameFilterError(elem, error); |
||||
} |
||||
return MakeRefCounted<DynamicFilters>(p.first); |
||||
} |
||||
|
||||
DynamicFilters::~DynamicFilters() { |
||||
GRPC_CHANNEL_STACK_UNREF(channel_stack_, "~DynamicFilters"); |
||||
} |
||||
|
||||
RefCountedPtr<DynamicFilters::Call> DynamicFilters::CreateCall( |
||||
DynamicFilters::Call::Args args, grpc_error** error) { |
||||
size_t allocation_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(Call)) + |
||||
channel_stack_->call_stack_size; |
||||
Call* call = static_cast<Call*>(args.arena->Alloc(allocation_size)); |
||||
new (call) Call(std::move(args), error); |
||||
return call; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,99 @@ |
||||
//
|
||||
// Copyright 2020 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_DYNAMIC_FILTERS_H |
||||
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_DYNAMIC_FILTERS_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <vector> |
||||
|
||||
#include "src/core/lib/channel/channel_stack.h" |
||||
#include "src/core/lib/gpr/time_precise.h" |
||||
#include "src/core/lib/gprpp/arena.h" |
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/iomgr/polling_entity.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
class DynamicFilters : public RefCounted<DynamicFilters> { |
||||
public: |
||||
// Implements the interface of RefCounted<>.
|
||||
class Call { |
||||
public: |
||||
struct Args { |
||||
RefCountedPtr<DynamicFilters> channel_stack; |
||||
grpc_polling_entity* pollent; |
||||
grpc_slice path; |
||||
gpr_cycle_counter start_time; |
||||
grpc_millis deadline; |
||||
Arena* arena; |
||||
grpc_call_context_element* context; |
||||
CallCombiner* call_combiner; |
||||
}; |
||||
|
||||
Call(Args args, grpc_error** error); |
||||
|
||||
// Continues processing a transport stream op batch.
|
||||
void StartTransportStreamOpBatch(grpc_transport_stream_op_batch* batch); |
||||
|
||||
// Sets the 'then_schedule_closure' argument for call stack destruction.
|
||||
// Must be called once per call.
|
||||
void SetAfterCallStackDestroy(grpc_closure* closure); |
||||
|
||||
// Interface of RefCounted<>.
|
||||
RefCountedPtr<Call> Ref() GRPC_MUST_USE_RESULT; |
||||
RefCountedPtr<Call> Ref(const DebugLocation& location, |
||||
const char* reason) GRPC_MUST_USE_RESULT; |
||||
// When refcount drops to 0, destroys itself and the associated call stack,
|
||||
// but does NOT free the memory because it's in the call arena.
|
||||
void Unref(); |
||||
void Unref(const DebugLocation& location, const char* reason); |
||||
|
||||
private: |
||||
// Allow RefCountedPtr<> to access IncrementRefCount().
|
||||
template <typename T> |
||||
friend class RefCountedPtr; |
||||
|
||||
// Interface of RefCounted<>.
|
||||
void IncrementRefCount(); |
||||
void IncrementRefCount(const DebugLocation& location, const char* reason); |
||||
|
||||
static void Destroy(void* arg, grpc_error* error); |
||||
|
||||
RefCountedPtr<DynamicFilters> channel_stack_; |
||||
grpc_closure* after_call_stack_destroy_ = nullptr; |
||||
}; |
||||
|
||||
static RefCountedPtr<DynamicFilters> Create( |
||||
const grpc_channel_args* args, |
||||
std::vector<const grpc_channel_filter*> filters); |
||||
|
||||
explicit DynamicFilters(grpc_channel_stack* channel_stack) |
||||
: channel_stack_(channel_stack) {} |
||||
|
||||
~DynamicFilters() override; |
||||
|
||||
RefCountedPtr<Call> CreateCall(Call::Args args, grpc_error** error); |
||||
|
||||
private: |
||||
grpc_channel_stack* channel_stack_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_DYNAMIC_FILTERS_H
|
@ -1,908 +0,0 @@ |
||||
//
|
||||
// Copyright 2018 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <inttypes.h> |
||||
#include <limits.h> |
||||
|
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/types/optional.h" |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include "src/core/ext/filters/client_channel/client_channel.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy/address_filtering.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy/child_policy_handler.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy_factory.h" |
||||
#include "src/core/ext/filters/client_channel/lb_policy_registry.h" |
||||
#include "src/core/ext/filters/client_channel/server_address.h" |
||||
#include "src/core/ext/xds/xds_channel_args.h" |
||||
#include "src/core/ext/xds/xds_client.h" |
||||
#include "src/core/ext/xds/xds_client_stats.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/gprpp/orphanable.h" |
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||
#include "src/core/lib/iomgr/work_serializer.h" |
||||
#include "src/core/lib/transport/error_utils.h" |
||||
#include "src/core/lib/uri/uri_parser.h" |
||||
|
||||
#define GRPC_EDS_DEFAULT_FALLBACK_TIMEOUT 10000 |
||||
|
||||
namespace grpc_core { |
||||
|
||||
TraceFlag grpc_lb_eds_trace(false, "eds_lb"); |
||||
|
||||
const char* kXdsLocalityNameAttributeKey = "xds_locality_name"; |
||||
|
||||
namespace { |
||||
|
||||
constexpr char kEds[] = "eds_experimental"; |
||||
|
||||
// Config for EDS LB policy.
|
||||
class EdsLbConfig : public LoadBalancingPolicy::Config { |
||||
public: |
||||
EdsLbConfig(std::string cluster_name, std::string eds_service_name, |
||||
absl::optional<std::string> lrs_load_reporting_server_name, |
||||
Json locality_picking_policy, Json endpoint_picking_policy, |
||||
uint32_t max_concurrent_requests) |
||||
: cluster_name_(std::move(cluster_name)), |
||||
eds_service_name_(std::move(eds_service_name)), |
||||
lrs_load_reporting_server_name_( |
||||
std::move(lrs_load_reporting_server_name)), |
||||
locality_picking_policy_(std::move(locality_picking_policy)), |
||||
endpoint_picking_policy_(std::move(endpoint_picking_policy)), |
||||
max_concurrent_requests_(max_concurrent_requests) {} |
||||
|
||||
const char* name() const override { return kEds; } |
||||
|
||||
const std::string& cluster_name() const { return cluster_name_; } |
||||
const std::string& eds_service_name() const { return eds_service_name_; } |
||||
const absl::optional<std::string>& lrs_load_reporting_server_name() const { |
||||
return lrs_load_reporting_server_name_; |
||||
}; |
||||
const Json& locality_picking_policy() const { |
||||
return locality_picking_policy_; |
||||
} |
||||
const Json& endpoint_picking_policy() const { |
||||
return endpoint_picking_policy_; |
||||
} |
||||
const uint32_t max_concurrent_requests() const { |
||||
return max_concurrent_requests_; |
||||
} |
||||
|
||||
private: |
||||
std::string cluster_name_; |
||||
std::string eds_service_name_; |
||||
absl::optional<std::string> lrs_load_reporting_server_name_; |
||||
Json locality_picking_policy_; |
||||
Json endpoint_picking_policy_; |
||||
uint32_t max_concurrent_requests_; |
||||
}; |
||||
|
||||
// EDS LB policy.
|
||||
class EdsLb : public LoadBalancingPolicy { |
||||
public: |
||||
EdsLb(RefCountedPtr<XdsClient> xds_client, Args args); |
||||
|
||||
const char* name() const override { return kEds; } |
||||
|
||||
void UpdateLocked(UpdateArgs args) override; |
||||
void ResetBackoffLocked() override; |
||||
|
||||
private: |
||||
class EndpointWatcher : public XdsClient::EndpointWatcherInterface { |
||||
public: |
||||
explicit EndpointWatcher(RefCountedPtr<EdsLb> parent) |
||||
: parent_(std::move(parent)) {} |
||||
void OnEndpointChanged(XdsApi::EdsUpdate update) override { |
||||
new Notifier(parent_, std::move(update)); |
||||
} |
||||
void OnError(grpc_error* error) override { new Notifier(parent_, error); } |
||||
void OnResourceDoesNotExist() override { new Notifier(parent_); } |
||||
|
||||
private: |
||||
class Notifier { |
||||
public: |
||||
Notifier(RefCountedPtr<EdsLb> parent, XdsApi::EdsUpdate update); |
||||
Notifier(RefCountedPtr<EdsLb> parent, grpc_error* error); |
||||
explicit Notifier(RefCountedPtr<EdsLb> parent); |
||||
|
||||
private: |
||||
enum Type { kUpdate, kError, kDoesNotExist }; |
||||
|
||||
static void RunInExecCtx(void* arg, grpc_error* error); |
||||
void RunInWorkSerializer(grpc_error* error); |
||||
|
||||
RefCountedPtr<EdsLb> parent_; |
||||
grpc_closure closure_; |
||||
XdsApi::EdsUpdate update_; |
||||
Type type_; |
||||
}; |
||||
|
||||
RefCountedPtr<EdsLb> parent_; |
||||
}; |
||||
|
||||
class Helper : public ChannelControlHelper { |
||||
public: |
||||
explicit Helper(RefCountedPtr<EdsLb> eds_policy) |
||||
: eds_policy_(std::move(eds_policy)) {} |
||||
|
||||
~Helper() override { eds_policy_.reset(DEBUG_LOCATION, "Helper"); } |
||||
|
||||
RefCountedPtr<SubchannelInterface> CreateSubchannel( |
||||
ServerAddress address, const grpc_channel_args& args) override; |
||||
void UpdateState(grpc_connectivity_state state, const absl::Status& status, |
||||
std::unique_ptr<SubchannelPicker> picker) override; |
||||
// This is a no-op, because we get the addresses from the xds
|
||||
// client, which is a watch-based API.
|
||||
void RequestReresolution() override {} |
||||
void AddTraceEvent(TraceSeverity severity, |
||||
absl::string_view message) override; |
||||
|
||||
private: |
||||
RefCountedPtr<EdsLb> eds_policy_; |
||||
}; |
||||
|
||||
~EdsLb() override; |
||||
|
||||
void ShutdownLocked() override; |
||||
|
||||
void OnEndpointChanged(XdsApi::EdsUpdate update); |
||||
void OnError(grpc_error* error); |
||||
void OnResourceDoesNotExist(); |
||||
|
||||
void MaybeDestroyChildPolicyLocked(); |
||||
|
||||
void UpdatePriorityList(XdsApi::EdsUpdate::PriorityList priority_list); |
||||
void UpdateChildPolicyLocked(); |
||||
OrphanablePtr<LoadBalancingPolicy> CreateChildPolicyLocked( |
||||
const grpc_channel_args* args); |
||||
ServerAddressList CreateChildPolicyAddressesLocked(); |
||||
RefCountedPtr<Config> CreateChildPolicyConfigLocked(); |
||||
grpc_channel_args* CreateChildPolicyArgsLocked( |
||||
const grpc_channel_args* args_in); |
||||
|
||||
// Caller must ensure that config_ is set before calling.
|
||||
const absl::string_view GetEdsResourceName() const { |
||||
if (!is_xds_uri_) return server_name_; |
||||
if (!config_->eds_service_name().empty()) { |
||||
return config_->eds_service_name(); |
||||
} |
||||
return config_->cluster_name(); |
||||
} |
||||
|
||||
// Returns a pair containing the cluster and eds_service_name to use
|
||||
// for LRS load reporting.
|
||||
// Caller must ensure that config_ is set before calling.
|
||||
std::pair<absl::string_view, absl::string_view> GetLrsClusterKey() const { |
||||
if (!is_xds_uri_) return {server_name_, nullptr}; |
||||
return {config_->cluster_name(), config_->eds_service_name()}; |
||||
} |
||||
|
||||
// Server name from target URI.
|
||||
std::string server_name_; |
||||
bool is_xds_uri_; |
||||
|
||||
// Current channel args and config from the resolver.
|
||||
const grpc_channel_args* args_ = nullptr; |
||||
RefCountedPtr<EdsLbConfig> config_; |
||||
|
||||
// Internal state.
|
||||
bool shutting_down_ = false; |
||||
|
||||
// The xds client and endpoint watcher.
|
||||
RefCountedPtr<XdsClient> xds_client_; |
||||
// A pointer to the endpoint watcher, to be used when cancelling the watch.
|
||||
// Note that this is not owned, so this pointer must never be derefernced.
|
||||
EndpointWatcher* endpoint_watcher_ = nullptr; |
||||
// The latest data from the endpoint watcher.
|
||||
XdsApi::EdsUpdate::PriorityList priority_list_; |
||||
// State used to retain child policy names for priority policy.
|
||||
std::vector<size_t /*child_number*/> priority_child_numbers_; |
||||
|
||||
RefCountedPtr<XdsApi::EdsUpdate::DropConfig> drop_config_; |
||||
|
||||
OrphanablePtr<LoadBalancingPolicy> child_policy_; |
||||
}; |
||||
|
||||
//
|
||||
// EdsLb::Helper
|
||||
//
|
||||
|
||||
RefCountedPtr<SubchannelInterface> EdsLb::Helper::CreateSubchannel( |
||||
ServerAddress address, const grpc_channel_args& args) { |
||||
if (eds_policy_->shutting_down_) return nullptr; |
||||
return eds_policy_->channel_control_helper()->CreateSubchannel( |
||||
std::move(address), args); |
||||
} |
||||
|
||||
void EdsLb::Helper::UpdateState(grpc_connectivity_state state, |
||||
const absl::Status& status, |
||||
std::unique_ptr<SubchannelPicker> picker) { |
||||
if (eds_policy_->shutting_down_ || eds_policy_->child_policy_ == nullptr) { |
||||
return; |
||||
} |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] child policy updated state=%s (%s) picker=%p", |
||||
eds_policy_.get(), ConnectivityStateName(state), |
||||
status.ToString().c_str(), picker.get()); |
||||
} |
||||
eds_policy_->channel_control_helper()->UpdateState(state, status, |
||||
std::move(picker)); |
||||
} |
||||
|
||||
void EdsLb::Helper::AddTraceEvent(TraceSeverity severity, |
||||
absl::string_view message) { |
||||
if (eds_policy_->shutting_down_) return; |
||||
eds_policy_->channel_control_helper()->AddTraceEvent(severity, message); |
||||
} |
||||
|
||||
//
|
||||
// EdsLb::EndpointWatcher::Notifier
|
||||
//
|
||||
|
||||
EdsLb::EndpointWatcher::Notifier::Notifier(RefCountedPtr<EdsLb> parent, |
||||
XdsApi::EdsUpdate update) |
||||
: parent_(std::move(parent)), update_(std::move(update)), type_(kUpdate) { |
||||
GRPC_CLOSURE_INIT(&closure_, &RunInExecCtx, this, nullptr); |
||||
ExecCtx::Run(DEBUG_LOCATION, &closure_, GRPC_ERROR_NONE); |
||||
} |
||||
|
||||
EdsLb::EndpointWatcher::Notifier::Notifier(RefCountedPtr<EdsLb> parent, |
||||
grpc_error* error) |
||||
: parent_(std::move(parent)), type_(kError) { |
||||
GRPC_CLOSURE_INIT(&closure_, &RunInExecCtx, this, nullptr); |
||||
ExecCtx::Run(DEBUG_LOCATION, &closure_, error); |
||||
} |
||||
|
||||
EdsLb::EndpointWatcher::Notifier::Notifier(RefCountedPtr<EdsLb> parent) |
||||
: parent_(std::move(parent)), type_(kDoesNotExist) { |
||||
GRPC_CLOSURE_INIT(&closure_, &RunInExecCtx, this, nullptr); |
||||
ExecCtx::Run(DEBUG_LOCATION, &closure_, GRPC_ERROR_NONE); |
||||
} |
||||
|
||||
void EdsLb::EndpointWatcher::Notifier::RunInExecCtx(void* arg, |
||||
grpc_error* error) { |
||||
Notifier* self = static_cast<Notifier*>(arg); |
||||
GRPC_ERROR_REF(error); |
||||
self->parent_->work_serializer()->Run( |
||||
[self, error]() { self->RunInWorkSerializer(error); }, DEBUG_LOCATION); |
||||
} |
||||
|
||||
void EdsLb::EndpointWatcher::Notifier::RunInWorkSerializer(grpc_error* error) { |
||||
switch (type_) { |
||||
case kUpdate: |
||||
parent_->OnEndpointChanged(std::move(update_)); |
||||
break; |
||||
case kError: |
||||
parent_->OnError(error); |
||||
break; |
||||
case kDoesNotExist: |
||||
parent_->OnResourceDoesNotExist(); |
||||
break; |
||||
}; |
||||
delete this; |
||||
} |
||||
|
||||
//
|
||||
// EdsLb public methods
|
||||
//
|
||||
|
||||
EdsLb::EdsLb(RefCountedPtr<XdsClient> xds_client, Args args) |
||||
: LoadBalancingPolicy(std::move(args)), xds_client_(std::move(xds_client)) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] created -- using xds client %p", this, |
||||
xds_client_.get()); |
||||
} |
||||
// Record server name.
|
||||
const char* server_uri = |
||||
grpc_channel_args_find_string(args.args, GRPC_ARG_SERVER_URI); |
||||
GPR_ASSERT(server_uri != nullptr); |
||||
absl::StatusOr<URI> uri = URI::Parse(server_uri); |
||||
GPR_ASSERT(uri.ok() && !uri->path().empty()); |
||||
server_name_ = std::string(absl::StripPrefix(uri->path(), "/")); |
||||
is_xds_uri_ = uri->scheme() == "xds"; |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] server name from channel (is_xds_uri=%d): %s", |
||||
this, is_xds_uri_, server_name_.c_str()); |
||||
} |
||||
// EDS-only flow.
|
||||
if (!is_xds_uri_) { |
||||
// Setup channelz linkage.
|
||||
channelz::ChannelNode* parent_channelz_node = |
||||
grpc_channel_args_find_pointer<channelz::ChannelNode>( |
||||
args.args, GRPC_ARG_CHANNELZ_CHANNEL_NODE); |
||||
if (parent_channelz_node != nullptr) { |
||||
xds_client_->AddChannelzLinkage(parent_channelz_node); |
||||
} |
||||
// Couple polling.
|
||||
grpc_pollset_set_add_pollset_set(xds_client_->interested_parties(), |
||||
interested_parties()); |
||||
} |
||||
} |
||||
|
||||
EdsLb::~EdsLb() { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] destroying eds LB policy", this); |
||||
} |
||||
} |
||||
|
||||
void EdsLb::ShutdownLocked() { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] shutting down", this); |
||||
} |
||||
shutting_down_ = true; |
||||
MaybeDestroyChildPolicyLocked(); |
||||
// Cancel watcher.
|
||||
if (endpoint_watcher_ != nullptr) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] cancelling xds watch for %s", this, |
||||
std::string(GetEdsResourceName()).c_str()); |
||||
} |
||||
xds_client_->CancelEndpointDataWatch(GetEdsResourceName(), |
||||
endpoint_watcher_); |
||||
} |
||||
if (!is_xds_uri_) { |
||||
// Remove channelz linkage.
|
||||
channelz::ChannelNode* parent_channelz_node = |
||||
grpc_channel_args_find_pointer<channelz::ChannelNode>( |
||||
args_, GRPC_ARG_CHANNELZ_CHANNEL_NODE); |
||||
if (parent_channelz_node != nullptr) { |
||||
xds_client_->RemoveChannelzLinkage(parent_channelz_node); |
||||
} |
||||
// Decouple polling.
|
||||
grpc_pollset_set_del_pollset_set(xds_client_->interested_parties(), |
||||
interested_parties()); |
||||
} |
||||
xds_client_.reset(DEBUG_LOCATION, "EdsLb"); |
||||
// Destroy channel args.
|
||||
grpc_channel_args_destroy(args_); |
||||
args_ = nullptr; |
||||
} |
||||
|
||||
void EdsLb::MaybeDestroyChildPolicyLocked() { |
||||
if (child_policy_ != nullptr) { |
||||
grpc_pollset_set_del_pollset_set(child_policy_->interested_parties(), |
||||
interested_parties()); |
||||
child_policy_.reset(); |
||||
} |
||||
} |
||||
|
||||
void EdsLb::UpdateLocked(UpdateArgs args) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] Received update", this); |
||||
} |
||||
const bool is_initial_update = args_ == nullptr; |
||||
// Update config.
|
||||
auto old_config = std::move(config_); |
||||
config_ = std::move(args.config); |
||||
// Update args.
|
||||
grpc_channel_args_destroy(args_); |
||||
args_ = args.args; |
||||
args.args = nullptr; |
||||
// Update child policy if needed.
|
||||
if (child_policy_ != nullptr) UpdateChildPolicyLocked(); |
||||
// Create endpoint watcher if needed.
|
||||
if (is_initial_update) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] starting xds watch for %s", this, |
||||
std::string(GetEdsResourceName()).c_str()); |
||||
} |
||||
auto watcher = absl::make_unique<EndpointWatcher>( |
||||
Ref(DEBUG_LOCATION, "EndpointWatcher")); |
||||
endpoint_watcher_ = watcher.get(); |
||||
xds_client_->WatchEndpointData(GetEdsResourceName(), std::move(watcher)); |
||||
} |
||||
} |
||||
|
||||
void EdsLb::ResetBackoffLocked() { |
||||
// When the XdsClient is instantiated in the resolver instead of in this
|
||||
// LB policy, this is done via the resolver, so we don't need to do it here.
|
||||
if (!is_xds_uri_ && xds_client_ != nullptr) xds_client_->ResetBackoff(); |
||||
if (child_policy_ != nullptr) { |
||||
child_policy_->ResetBackoffLocked(); |
||||
} |
||||
} |
||||
|
||||
void EdsLb::OnEndpointChanged(XdsApi::EdsUpdate update) { |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] Received EDS update from xds client", this); |
||||
} |
||||
// Update the drop config.
|
||||
drop_config_ = std::move(update.drop_config); |
||||
// If priority list is empty, add a single priority, just so that we
|
||||
// have a child in which to create the xds_cluster_impl policy.
|
||||
if (update.priorities.empty()) update.priorities.emplace_back(); |
||||
// Update child policy.
|
||||
UpdatePriorityList(std::move(update.priorities)); |
||||
} |
||||
|
||||
void EdsLb::OnError(grpc_error* error) { |
||||
gpr_log(GPR_ERROR, "[edslb %p] xds watcher reported error: %s", this, |
||||
grpc_error_string(error)); |
||||
// Go into TRANSIENT_FAILURE if we have not yet created the child
|
||||
// policy (i.e., we have not yet received data from xds). Otherwise,
|
||||
// we keep running with the data we had previously.
|
||||
if (child_policy_ == nullptr) { |
||||
channel_control_helper()->UpdateState( |
||||
GRPC_CHANNEL_TRANSIENT_FAILURE, grpc_error_to_absl_status(error), |
||||
absl::make_unique<TransientFailurePicker>(error)); |
||||
} else { |
||||
GRPC_ERROR_UNREF(error); |
||||
} |
||||
} |
||||
|
||||
void EdsLb::OnResourceDoesNotExist() { |
||||
gpr_log( |
||||
GPR_ERROR, |
||||
"[edslb %p] EDS resource does not exist -- reporting TRANSIENT_FAILURE", |
||||
this); |
||||
grpc_error* error = grpc_error_set_int( |
||||
GRPC_ERROR_CREATE_FROM_STATIC_STRING("EDS resource does not exist"), |
||||
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); |
||||
channel_control_helper()->UpdateState( |
||||
GRPC_CHANNEL_TRANSIENT_FAILURE, grpc_error_to_absl_status(error), |
||||
absl::make_unique<TransientFailurePicker>(error)); |
||||
MaybeDestroyChildPolicyLocked(); |
||||
} |
||||
|
||||
//
|
||||
// child policy-related methods
|
||||
//
|
||||
|
||||
void EdsLb::UpdatePriorityList(XdsApi::EdsUpdate::PriorityList priority_list) { |
||||
// Build some maps from locality to child number and the reverse from
|
||||
// the old data in priority_list_ and priority_child_numbers_.
|
||||
std::map<XdsLocalityName*, size_t /*child_number*/, XdsLocalityName::Less> |
||||
locality_child_map; |
||||
std::map<size_t, std::set<XdsLocalityName*>> child_locality_map; |
||||
for (size_t priority = 0; priority < priority_list_.size(); ++priority) { |
||||
size_t child_number = priority_child_numbers_[priority]; |
||||
const auto& localities = priority_list_[priority].localities; |
||||
for (const auto& p : localities) { |
||||
XdsLocalityName* locality_name = p.first; |
||||
locality_child_map[locality_name] = child_number; |
||||
child_locality_map[child_number].insert(locality_name); |
||||
} |
||||
} |
||||
// Construct new list of children.
|
||||
std::vector<size_t> priority_child_numbers; |
||||
for (size_t priority = 0; priority < priority_list.size(); ++priority) { |
||||
const auto& localities = priority_list[priority].localities; |
||||
absl::optional<size_t> child_number; |
||||
// If one of the localities in this priority already existed, reuse its
|
||||
// child number.
|
||||
for (const auto& p : localities) { |
||||
XdsLocalityName* locality_name = p.first; |
||||
if (!child_number.has_value()) { |
||||
auto it = locality_child_map.find(locality_name); |
||||
if (it != locality_child_map.end()) { |
||||
child_number = it->second; |
||||
locality_child_map.erase(it); |
||||
// Remove localities that *used* to be in this child number, so
|
||||
// that we don't incorrectly reuse this child number for a
|
||||
// subsequent priority.
|
||||
for (XdsLocalityName* old_locality : |
||||
child_locality_map[*child_number]) { |
||||
locality_child_map.erase(old_locality); |
||||
} |
||||
} |
||||
} else { |
||||
// Remove all localities that are now in this child number, so
|
||||
// that we don't accidentally reuse this child number for a
|
||||
// subsequent priority.
|
||||
locality_child_map.erase(locality_name); |
||||
} |
||||
} |
||||
// If we didn't find an existing child number, assign a new one.
|
||||
if (!child_number.has_value()) { |
||||
for (child_number = 0; |
||||
child_locality_map.find(*child_number) != child_locality_map.end(); |
||||
++(*child_number)) { |
||||
} |
||||
// Add entry so we know that the child number is in use.
|
||||
// (Don't need to add the list of localities, since we won't use them.)
|
||||
child_locality_map[*child_number]; |
||||
} |
||||
priority_child_numbers.push_back(*child_number); |
||||
} |
||||
// Save update.
|
||||
priority_list_ = std::move(priority_list); |
||||
priority_child_numbers_ = std::move(priority_child_numbers); |
||||
// Update child policy.
|
||||
UpdateChildPolicyLocked(); |
||||
} |
||||
|
||||
ServerAddressList EdsLb::CreateChildPolicyAddressesLocked() { |
||||
ServerAddressList addresses; |
||||
for (size_t priority = 0; priority < priority_list_.size(); ++priority) { |
||||
const auto& localities = priority_list_[priority].localities; |
||||
std::string priority_child_name = |
||||
absl::StrCat("child", priority_child_numbers_[priority]); |
||||
for (const auto& p : localities) { |
||||
const auto& locality_name = p.first; |
||||
const auto& locality = p.second; |
||||
std::vector<std::string> hierarchical_path = { |
||||
priority_child_name, locality_name->AsHumanReadableString()}; |
||||
for (const auto& endpoint : locality.endpoints) { |
||||
addresses.emplace_back( |
||||
endpoint |
||||
.WithAttribute(kHierarchicalPathAttributeKey, |
||||
MakeHierarchicalPathAttribute(hierarchical_path)) |
||||
.WithAttribute(kXdsLocalityNameAttributeKey, |
||||
absl::make_unique<XdsLocalityAttribute>( |
||||
locality_name->Ref()))); |
||||
} |
||||
} |
||||
} |
||||
return addresses; |
||||
} |
||||
|
||||
RefCountedPtr<LoadBalancingPolicy::Config> |
||||
EdsLb::CreateChildPolicyConfigLocked() { |
||||
const auto lrs_key = GetLrsClusterKey(); |
||||
Json::Object priority_children; |
||||
Json::Array priority_priorities; |
||||
for (size_t priority = 0; priority < priority_list_.size(); ++priority) { |
||||
const auto& localities = priority_list_[priority].localities; |
||||
Json::Object weighted_targets; |
||||
for (const auto& p : localities) { |
||||
XdsLocalityName* locality_name = p.first; |
||||
const auto& locality = p.second; |
||||
// Construct JSON object containing locality name.
|
||||
Json::Object locality_name_json; |
||||
if (!locality_name->region().empty()) { |
||||
locality_name_json["region"] = locality_name->region(); |
||||
} |
||||
if (!locality_name->zone().empty()) { |
||||
locality_name_json["zone"] = locality_name->zone(); |
||||
} |
||||
if (!locality_name->sub_zone().empty()) { |
||||
locality_name_json["subzone"] = locality_name->sub_zone(); |
||||
} |
||||
// Add weighted target entry.
|
||||
weighted_targets[locality_name->AsHumanReadableString()] = Json::Object{ |
||||
{"weight", locality.lb_weight}, |
||||
{"childPolicy", config_->endpoint_picking_policy()}, |
||||
}; |
||||
} |
||||
// Construct locality-picking policy.
|
||||
// Start with field from our config and add the "targets" field.
|
||||
Json locality_picking_config = config_->locality_picking_policy(); |
||||
Json::Object& config = |
||||
*(*locality_picking_config.mutable_array())[0].mutable_object(); |
||||
auto it = config.begin(); |
||||
GPR_ASSERT(it != config.end()); |
||||
(*it->second.mutable_object())["targets"] = std::move(weighted_targets); |
||||
// Wrap it in the drop policy.
|
||||
Json::Array drop_categories; |
||||
for (const auto& category : drop_config_->drop_category_list()) { |
||||
drop_categories.push_back(Json::Object{ |
||||
{"category", category.name}, |
||||
{"requests_per_million", category.parts_per_million}, |
||||
}); |
||||
} |
||||
Json::Object xds_cluster_impl_config = { |
||||
{"clusterName", std::string(lrs_key.first)}, |
||||
{"childPolicy", std::move(locality_picking_config)}, |
||||
{"dropCategories", std::move(drop_categories)}, |
||||
{"maxConcurrentRequests", config_->max_concurrent_requests()}, |
||||
}; |
||||
if (!lrs_key.second.empty()) { |
||||
xds_cluster_impl_config["edsServiceName"] = std::string(lrs_key.second); |
||||
} |
||||
if (config_->lrs_load_reporting_server_name().has_value()) { |
||||
xds_cluster_impl_config["lrsLoadReportingServerName"] = |
||||
config_->lrs_load_reporting_server_name().value(); |
||||
} |
||||
Json locality_picking_policy = Json::Array{Json::Object{ |
||||
{"xds_cluster_impl_experimental", std::move(xds_cluster_impl_config)}, |
||||
}}; |
||||
// Add priority entry.
|
||||
const size_t child_number = priority_child_numbers_[priority]; |
||||
std::string child_name = absl::StrCat("child", child_number); |
||||
priority_priorities.emplace_back(child_name); |
||||
priority_children[child_name] = Json::Object{ |
||||
{"config", std::move(locality_picking_policy)}, |
||||
{"ignore_reresolution_requests", true}, |
||||
}; |
||||
} |
||||
Json json = Json::Array{Json::Object{ |
||||
{"priority_experimental", |
||||
Json::Object{ |
||||
{"children", std::move(priority_children)}, |
||||
{"priorities", std::move(priority_priorities)}, |
||||
}}, |
||||
}}; |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
std::string json_str = json.Dump(/*indent=*/1); |
||||
gpr_log(GPR_INFO, "[edslb %p] generated config for child policy: %s", this, |
||||
json_str.c_str()); |
||||
} |
||||
grpc_error* error = GRPC_ERROR_NONE; |
||||
RefCountedPtr<LoadBalancingPolicy::Config> config = |
||||
LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(json, &error); |
||||
if (error != GRPC_ERROR_NONE) { |
||||
// This should never happen, but if it does, we basically have no
|
||||
// way to fix it, so we put the channel in TRANSIENT_FAILURE.
|
||||
gpr_log(GPR_ERROR, |
||||
"[edslb %p] error parsing generated child policy config -- " |
||||
"will put channel in TRANSIENT_FAILURE: %s", |
||||
this, grpc_error_string(error)); |
||||
error = grpc_error_set_int( |
||||
grpc_error_add_child( |
||||
GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"eds LB policy: error parsing generated child policy config"), |
||||
error), |
||||
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_INTERNAL); |
||||
channel_control_helper()->UpdateState( |
||||
GRPC_CHANNEL_TRANSIENT_FAILURE, grpc_error_to_absl_status(error), |
||||
absl::make_unique<TransientFailurePicker>(error)); |
||||
return nullptr; |
||||
} |
||||
return config; |
||||
} |
||||
|
||||
void EdsLb::UpdateChildPolicyLocked() { |
||||
if (shutting_down_) return; |
||||
UpdateArgs update_args; |
||||
update_args.config = CreateChildPolicyConfigLocked(); |
||||
if (update_args.config == nullptr) return; |
||||
update_args.addresses = CreateChildPolicyAddressesLocked(); |
||||
update_args.args = CreateChildPolicyArgsLocked(args_); |
||||
if (child_policy_ == nullptr) { |
||||
child_policy_ = CreateChildPolicyLocked(update_args.args); |
||||
} |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p] Updating child policy %p", this, |
||||
child_policy_.get()); |
||||
} |
||||
child_policy_->UpdateLocked(std::move(update_args)); |
||||
} |
||||
|
||||
grpc_channel_args* EdsLb::CreateChildPolicyArgsLocked( |
||||
const grpc_channel_args* args) { |
||||
grpc_arg args_to_add[] = { |
||||
// A channel arg indicating if the target is a backend inferred from an
|
||||
// xds load balancer.
|
||||
// TODO(roth): This isn't needed with the new fallback design.
|
||||
// Remove as part of implementing the new fallback functionality.
|
||||
grpc_channel_arg_integer_create( |
||||
const_cast<char*>(GRPC_ARG_ADDRESS_IS_BACKEND_FROM_XDS_LOAD_BALANCER), |
||||
1), |
||||
// Inhibit client-side health checking, since the balancer does
|
||||
// this for us.
|
||||
grpc_channel_arg_integer_create( |
||||
const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1), |
||||
}; |
||||
return grpc_channel_args_copy_and_add(args, args_to_add, |
||||
GPR_ARRAY_SIZE(args_to_add)); |
||||
} |
||||
|
||||
OrphanablePtr<LoadBalancingPolicy> EdsLb::CreateChildPolicyLocked( |
||||
const grpc_channel_args* args) { |
||||
LoadBalancingPolicy::Args lb_policy_args; |
||||
lb_policy_args.work_serializer = work_serializer(); |
||||
lb_policy_args.args = args; |
||||
lb_policy_args.channel_control_helper = |
||||
absl::make_unique<Helper>(Ref(DEBUG_LOCATION, "Helper")); |
||||
OrphanablePtr<LoadBalancingPolicy> lb_policy = |
||||
LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy( |
||||
"priority_experimental", std::move(lb_policy_args)); |
||||
if (GPR_UNLIKELY(lb_policy == nullptr)) { |
||||
gpr_log(GPR_ERROR, "[edslb %p] failure creating child policy", this); |
||||
return nullptr; |
||||
} |
||||
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_eds_trace)) { |
||||
gpr_log(GPR_INFO, "[edslb %p]: Created new child policy %p", this, |
||||
lb_policy.get()); |
||||
} |
||||
// Add our interested_parties pollset_set to that of the newly created
|
||||
// child policy. This will make the child policy progress upon activity on
|
||||
// this policy, which in turn is tied to the application's call.
|
||||
grpc_pollset_set_add_pollset_set(lb_policy->interested_parties(), |
||||
interested_parties()); |
||||
return lb_policy; |
||||
} |
||||
|
||||
//
|
||||
// factory
|
||||
//
|
||||
|
||||
class EdsLbFactory : public LoadBalancingPolicyFactory { |
||||
public: |
||||
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy( |
||||
LoadBalancingPolicy::Args args) const override { |
||||
grpc_error* error = GRPC_ERROR_NONE; |
||||
RefCountedPtr<XdsClient> xds_client = XdsClient::GetOrCreate(&error); |
||||
if (error != GRPC_ERROR_NONE) { |
||||
gpr_log(GPR_ERROR, |
||||
"cannot get XdsClient to instantiate eds LB policy: %s", |
||||
grpc_error_string(error)); |
||||
GRPC_ERROR_UNREF(error); |
||||
return nullptr; |
||||
} |
||||
return MakeOrphanable<EdsChildHandler>(std::move(xds_client), |
||||
std::move(args)); |
||||
} |
||||
|
||||
const char* name() const override { return kEds; } |
||||
|
||||
RefCountedPtr<LoadBalancingPolicy::Config> ParseLoadBalancingConfig( |
||||
const Json& json, grpc_error** error) const override { |
||||
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE); |
||||
if (json.type() == Json::Type::JSON_NULL) { |
||||
// eds was mentioned as a policy in the deprecated loadBalancingPolicy
|
||||
// field or in the client API.
|
||||
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:loadBalancingPolicy error:eds policy requires configuration. " |
||||
"Please use loadBalancingConfig field of service config instead."); |
||||
return nullptr; |
||||
} |
||||
std::vector<grpc_error*> error_list; |
||||
// EDS service name.
|
||||
std::string eds_service_name; |
||||
auto it = json.object_value().find("edsServiceName"); |
||||
if (it != json.object_value().end()) { |
||||
if (it->second.type() != Json::Type::STRING) { |
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:edsServiceName error:type should be string")); |
||||
} else { |
||||
eds_service_name = it->second.string_value(); |
||||
} |
||||
} |
||||
// Cluster name.
|
||||
std::string cluster_name; |
||||
it = json.object_value().find("clusterName"); |
||||
if (it == json.object_value().end()) { |
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:clusterName error:required field missing")); |
||||
} else if (it->second.type() != Json::Type::STRING) { |
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:clusterName error:type should be string")); |
||||
} else { |
||||
cluster_name = it->second.string_value(); |
||||
} |
||||
// LRS load reporting server name.
|
||||
absl::optional<std::string> lrs_load_reporting_server_name; |
||||
it = json.object_value().find("lrsLoadReportingServerName"); |
||||
if (it != json.object_value().end()) { |
||||
if (it->second.type() != Json::Type::STRING) { |
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:lrsLoadReportingServerName error:type should be string")); |
||||
} else { |
||||
lrs_load_reporting_server_name.emplace(it->second.string_value()); |
||||
} |
||||
} |
||||
// Locality-picking policy.
|
||||
Json locality_picking_policy; |
||||
it = json.object_value().find("localityPickingPolicy"); |
||||
if (it == json.object_value().end()) { |
||||
locality_picking_policy = Json::Array{ |
||||
Json::Object{ |
||||
{"weighted_target_experimental", |
||||
Json::Object{ |
||||
{"targets", Json::Object()}, |
||||
}}, |
||||
}, |
||||
}; |
||||
} else { |
||||
locality_picking_policy = it->second; |
||||
} |
||||
grpc_error* parse_error = GRPC_ERROR_NONE; |
||||
if (LoadBalancingPolicyRegistry::ParseLoadBalancingConfig( |
||||
locality_picking_policy, &parse_error) == nullptr) { |
||||
GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE); |
||||
error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
||||
"localityPickingPolicy", &parse_error, 1)); |
||||
GRPC_ERROR_UNREF(parse_error); |
||||
} |
||||
// Endpoint-picking policy. Called "childPolicy" for xds policy.
|
||||
Json endpoint_picking_policy; |
||||
it = json.object_value().find("endpointPickingPolicy"); |
||||
if (it == json.object_value().end()) { |
||||
endpoint_picking_policy = Json::Array{ |
||||
Json::Object{ |
||||
{"round_robin", Json::Object()}, |
||||
}, |
||||
}; |
||||
} else { |
||||
endpoint_picking_policy = it->second; |
||||
} |
||||
parse_error = GRPC_ERROR_NONE; |
||||
if (LoadBalancingPolicyRegistry::ParseLoadBalancingConfig( |
||||
endpoint_picking_policy, &parse_error) == nullptr) { |
||||
GPR_DEBUG_ASSERT(parse_error != GRPC_ERROR_NONE); |
||||
error_list.push_back(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( |
||||
"endpointPickingPolicy", &parse_error, 1)); |
||||
GRPC_ERROR_UNREF(parse_error); |
||||
} |
||||
// Max concurrent requests.
|
||||
uint32_t max_concurrent_requests = 1024; |
||||
it = json.object_value().find("max_concurrent_requests"); |
||||
if (it != json.object_value().end()) { |
||||
if (it->second.type() != Json::Type::NUMBER) { |
||||
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
||||
"field:max_concurrent_requests error:must be of type number")); |
||||
} else { |
||||
max_concurrent_requests = |
||||
gpr_parse_nonnegative_int(it->second.string_value().c_str()); |
||||
} |
||||
} |
||||
// Construct config.
|
||||
if (error_list.empty()) { |
||||
return MakeRefCounted<EdsLbConfig>( |
||||
std::move(cluster_name), std::move(eds_service_name), |
||||
std::move(lrs_load_reporting_server_name), |
||||
std::move(locality_picking_policy), |
||||
std::move(endpoint_picking_policy), max_concurrent_requests); |
||||
} else { |
||||
*error = GRPC_ERROR_CREATE_FROM_VECTOR( |
||||
"eds_experimental LB policy config", &error_list); |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
class EdsChildHandler : public ChildPolicyHandler { |
||||
public: |
||||
EdsChildHandler(RefCountedPtr<XdsClient> xds_client, Args args) |
||||
: ChildPolicyHandler(std::move(args), &grpc_lb_eds_trace), |
||||
xds_client_(std::move(xds_client)) {} |
||||
|
||||
bool ConfigChangeRequiresNewPolicyInstance( |
||||
LoadBalancingPolicy::Config* old_config, |
||||
LoadBalancingPolicy::Config* new_config) const override { |
||||
GPR_ASSERT(old_config->name() == kEds); |
||||
GPR_ASSERT(new_config->name() == kEds); |
||||
EdsLbConfig* old_eds_config = static_cast<EdsLbConfig*>(old_config); |
||||
EdsLbConfig* new_eds_config = static_cast<EdsLbConfig*>(new_config); |
||||
return old_eds_config->cluster_name() != new_eds_config->cluster_name() || |
||||
old_eds_config->eds_service_name() != |
||||
new_eds_config->eds_service_name() || |
||||
old_eds_config->lrs_load_reporting_server_name() != |
||||
new_eds_config->lrs_load_reporting_server_name(); |
||||
} |
||||
|
||||
OrphanablePtr<LoadBalancingPolicy> CreateLoadBalancingPolicy( |
||||
const char* name, LoadBalancingPolicy::Args args) const override { |
||||
return MakeOrphanable<EdsLb>(xds_client_, std::move(args)); |
||||
} |
||||
|
||||
private: |
||||
RefCountedPtr<XdsClient> xds_client_; |
||||
}; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
//
|
||||
// Plugin registration
|
||||
//
|
||||
|
||||
void grpc_lb_policy_eds_init() { |
||||
grpc_core::LoadBalancingPolicyRegistry::Builder:: |
||||
RegisterLoadBalancingPolicyFactory( |
||||
absl::make_unique<grpc_core::EdsLbFactory>()); |
||||
} |
||||
|
||||
void grpc_lb_policy_eds_shutdown() {} |
@ -0,0 +1,24 @@ |
||||
//
|
||||
// Copyright 2020 gRPC authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_ARGS_H |
||||
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_ARGS_H |
||||
|
||||
// Channel arg indicating the xDS cluster name.
|
||||
// Set by xds_cluster_impl LB policy and used by GoogleDefaultCredentials.
|
||||
#define GRPC_ARG_XDS_CLUSTER_NAME "grpc.internal.xds_cluster_name" |
||||
|
||||
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_ARGS_H
|
File diff suppressed because it is too large
Load Diff
@ -1,485 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2016 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/iomgr/port.h" |
||||
#if GRPC_ARES == 1 |
||||
|
||||
#include <ares.h> |
||||
#include <string.h> |
||||
|
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
#include <grpc/support/time.h> |
||||
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" |
||||
#include "src/core/lib/gpr/string.h" |
||||
#include "src/core/lib/iomgr/iomgr_internal.h" |
||||
#include "src/core/lib/iomgr/sockaddr_utils.h" |
||||
#include "src/core/lib/iomgr/timer.h" |
||||
|
||||
typedef struct fd_node { |
||||
/** the owner of this fd node */ |
||||
grpc_ares_ev_driver* ev_driver; |
||||
/** a closure wrapping on_readable_locked, which should be
|
||||
invoked when the grpc_fd in this node becomes readable. */ |
||||
grpc_closure read_closure; |
||||
/** a closure wrapping on_writable_locked, which should be
|
||||
invoked when the grpc_fd in this node becomes writable. */ |
||||
grpc_closure write_closure; |
||||
/** next fd node in the list */ |
||||
struct fd_node* next; |
||||
|
||||
/** wrapped fd that's polled by grpc's poller for the current platform */ |
||||
grpc_core::GrpcPolledFd* grpc_polled_fd; |
||||
/** if the readable closure has been registered */ |
||||
bool readable_registered; |
||||
/** if the writable closure has been registered */ |
||||
bool writable_registered; |
||||
/** if the fd has been shutdown yet from grpc iomgr perspective */ |
||||
bool already_shutdown; |
||||
} fd_node; |
||||
|
||||
struct grpc_ares_ev_driver { |
||||
/** the ares_channel owned by this event driver */ |
||||
ares_channel channel; |
||||
/** pollset set for driving the IO events of the channel */ |
||||
grpc_pollset_set* pollset_set; |
||||
/** refcount of the event driver */ |
||||
gpr_refcount refs; |
||||
|
||||
/** work_serializer to synchronize c-ares and I/O callbacks on */ |
||||
std::shared_ptr<grpc_core::WorkSerializer> work_serializer; |
||||
/** a list of grpc_fd that this event driver is currently using. */ |
||||
fd_node* fds; |
||||
/** is this event driver currently working? */ |
||||
bool working; |
||||
/** is this event driver being shut down */ |
||||
bool shutting_down; |
||||
/** request object that's using this ev driver */ |
||||
grpc_ares_request* request; |
||||
/** Owned by the ev_driver. Creates new GrpcPolledFd's */ |
||||
std::unique_ptr<grpc_core::GrpcPolledFdFactory> polled_fd_factory; |
||||
/** query timeout in milliseconds */ |
||||
int query_timeout_ms; |
||||
/** alarm to cancel active queries */ |
||||
grpc_timer query_timeout; |
||||
/** cancels queries on a timeout */ |
||||
grpc_closure on_timeout_locked; |
||||
/** alarm to poll ares_process on in case fd events don't happen */ |
||||
grpc_timer ares_backup_poll_alarm; |
||||
/** polls ares_process on a periodic timer */ |
||||
grpc_closure on_ares_backup_poll_alarm_locked; |
||||
}; |
||||
|
||||
static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver); |
||||
|
||||
static grpc_ares_ev_driver* grpc_ares_ev_driver_ref( |
||||
grpc_ares_ev_driver* ev_driver) { |
||||
GRPC_CARES_TRACE_LOG("request:%p Ref ev_driver %p", ev_driver->request, |
||||
ev_driver); |
||||
gpr_ref(&ev_driver->refs); |
||||
return ev_driver; |
||||
} |
||||
|
||||
static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver) { |
||||
GRPC_CARES_TRACE_LOG("request:%p Unref ev_driver %p", ev_driver->request, |
||||
ev_driver); |
||||
if (gpr_unref(&ev_driver->refs)) { |
||||
GRPC_CARES_TRACE_LOG("request:%p destroy ev_driver %p", ev_driver->request, |
||||
ev_driver); |
||||
GPR_ASSERT(ev_driver->fds == nullptr); |
||||
ares_destroy(ev_driver->channel); |
||||
grpc_ares_complete_request_locked(ev_driver->request); |
||||
delete ev_driver; |
||||
} |
||||
} |
||||
|
||||
static void fd_node_destroy_locked(fd_node* fdn) { |
||||
GRPC_CARES_TRACE_LOG("request:%p delete fd: %s", fdn->ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
GPR_ASSERT(!fdn->readable_registered); |
||||
GPR_ASSERT(!fdn->writable_registered); |
||||
GPR_ASSERT(fdn->already_shutdown); |
||||
delete fdn->grpc_polled_fd; |
||||
gpr_free(fdn); |
||||
} |
||||
|
||||
static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) { |
||||
if (!fdn->already_shutdown) { |
||||
fdn->already_shutdown = true; |
||||
fdn->grpc_polled_fd->ShutdownLocked( |
||||
GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason)); |
||||
} |
||||
} |
||||
|
||||
static void on_timeout(void* arg, grpc_error* error); |
||||
static void on_timeout_locked(grpc_ares_ev_driver* driver, grpc_error* error); |
||||
|
||||
static void on_ares_backup_poll_alarm(void* arg, grpc_error* error); |
||||
static void on_ares_backup_poll_alarm_locked(grpc_ares_ev_driver* driver, |
||||
grpc_error* error); |
||||
|
||||
static void noop_inject_channel_config(ares_channel /*channel*/) {} |
||||
|
||||
void (*grpc_ares_test_only_inject_config)(ares_channel channel) = |
||||
noop_inject_channel_config; |
||||
|
||||
grpc_error* grpc_ares_ev_driver_create_locked( |
||||
grpc_ares_ev_driver** ev_driver, grpc_pollset_set* pollset_set, |
||||
int query_timeout_ms, |
||||
std::shared_ptr<grpc_core::WorkSerializer> work_serializer, |
||||
grpc_ares_request* request) { |
||||
*ev_driver = new grpc_ares_ev_driver(); |
||||
ares_options opts; |
||||
memset(&opts, 0, sizeof(opts)); |
||||
opts.flags |= ARES_FLAG_STAYOPEN; |
||||
int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS); |
||||
grpc_ares_test_only_inject_config((*ev_driver)->channel); |
||||
GRPC_CARES_TRACE_LOG("request:%p grpc_ares_ev_driver_create_locked", request); |
||||
if (status != ARES_SUCCESS) { |
||||
grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING( |
||||
absl::StrCat("Failed to init ares channel. C-ares error: ", |
||||
ares_strerror(status)) |
||||
.c_str()); |
||||
gpr_free(*ev_driver); |
||||
return err; |
||||
} |
||||
(*ev_driver)->work_serializer = std::move(work_serializer); |
||||
gpr_ref_init(&(*ev_driver)->refs, 1); |
||||
(*ev_driver)->pollset_set = pollset_set; |
||||
(*ev_driver)->fds = nullptr; |
||||
(*ev_driver)->working = false; |
||||
(*ev_driver)->shutting_down = false; |
||||
(*ev_driver)->request = request; |
||||
(*ev_driver)->polled_fd_factory = |
||||
grpc_core::NewGrpcPolledFdFactory((*ev_driver)->work_serializer); |
||||
(*ev_driver) |
||||
->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel); |
||||
(*ev_driver)->query_timeout_ms = query_timeout_ms; |
||||
return GRPC_ERROR_NONE; |
||||
} |
||||
|
||||
void grpc_ares_ev_driver_on_queries_complete_locked( |
||||
grpc_ares_ev_driver* ev_driver) { |
||||
// We mark the event driver as being shut down. If the event driver
|
||||
// is working, grpc_ares_notify_on_event_locked will shut down the
|
||||
// fds; if it's not working, there are no fds to shut down.
|
||||
ev_driver->shutting_down = true; |
||||
grpc_timer_cancel(&ev_driver->query_timeout); |
||||
grpc_timer_cancel(&ev_driver->ares_backup_poll_alarm); |
||||
grpc_ares_ev_driver_unref(ev_driver); |
||||
} |
||||
|
||||
void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver) { |
||||
ev_driver->shutting_down = true; |
||||
fd_node* fn = ev_driver->fds; |
||||
while (fn != nullptr) { |
||||
fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown"); |
||||
fn = fn->next; |
||||
} |
||||
} |
||||
|
||||
// Search fd in the fd_node list head. This is an O(n) search, the max possible
|
||||
// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests.
|
||||
static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) { |
||||
fd_node dummy_head; |
||||
dummy_head.next = *head; |
||||
fd_node* node = &dummy_head; |
||||
while (node->next != nullptr) { |
||||
if (node->next->grpc_polled_fd->GetWrappedAresSocketLocked() == as) { |
||||
fd_node* ret = node->next; |
||||
node->next = node->next->next; |
||||
*head = dummy_head.next; |
||||
return ret; |
||||
} |
||||
node = node->next; |
||||
} |
||||
return nullptr; |
||||
} |
||||
|
||||
static grpc_millis calculate_next_ares_backup_poll_alarm_ms( |
||||
grpc_ares_ev_driver* driver) { |
||||
// An alternative here could be to use ares_timeout to try to be more
|
||||
// accurate, but that would require using "struct timeval"'s, which just makes
|
||||
// things a bit more complicated. So just poll every second, as suggested
|
||||
// by the c-ares code comments.
|
||||
grpc_millis ms_until_next_ares_backup_poll_alarm = 1000; |
||||
GRPC_CARES_TRACE_LOG( |
||||
"request:%p ev_driver=%p. next ares process poll time in " |
||||
"%" PRId64 " ms", |
||||
driver->request, driver, ms_until_next_ares_backup_poll_alarm); |
||||
return ms_until_next_ares_backup_poll_alarm + |
||||
grpc_core::ExecCtx::Get()->Now(); |
||||
} |
||||
|
||||
static void on_timeout(void* arg, grpc_error* error) { |
||||
grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg); |
||||
GRPC_ERROR_REF(error); // ref owned by lambda
|
||||
driver->work_serializer->Run( |
||||
[driver, error]() { on_timeout_locked(driver, error); }, DEBUG_LOCATION); |
||||
} |
||||
|
||||
static void on_timeout_locked(grpc_ares_ev_driver* driver, grpc_error* error) { |
||||
GRPC_CARES_TRACE_LOG( |
||||
"request:%p ev_driver=%p on_timeout_locked. driver->shutting_down=%d. " |
||||
"err=%s", |
||||
driver->request, driver, driver->shutting_down, grpc_error_string(error)); |
||||
if (!driver->shutting_down && error == GRPC_ERROR_NONE) { |
||||
grpc_ares_ev_driver_shutdown_locked(driver); |
||||
} |
||||
grpc_ares_ev_driver_unref(driver); |
||||
GRPC_ERROR_UNREF(error); |
||||
} |
||||
|
||||
static void on_ares_backup_poll_alarm(void* arg, grpc_error* error) { |
||||
grpc_ares_ev_driver* driver = static_cast<grpc_ares_ev_driver*>(arg); |
||||
GRPC_ERROR_REF(error); |
||||
driver->work_serializer->Run( |
||||
[driver, error]() { on_ares_backup_poll_alarm_locked(driver, error); }, |
||||
DEBUG_LOCATION); |
||||
} |
||||
|
||||
/* In case of non-responsive DNS servers, dropped packets, etc., c-ares has
|
||||
* intelligent timeout and retry logic, which we can take advantage of by |
||||
* polling ares_process_fd on time intervals. Overall, the c-ares library is |
||||
* meant to be called into and given a chance to proceed name resolution: |
||||
* a) when fd events happen |
||||
* b) when some time has passed without fd events having happened |
||||
* For the latter, we use this backup poller. Also see |
||||
* https://github.com/grpc/grpc/pull/17688 description for more details. */
|
||||
static void on_ares_backup_poll_alarm_locked(grpc_ares_ev_driver* driver, |
||||
grpc_error* error) { |
||||
GRPC_CARES_TRACE_LOG( |
||||
"request:%p ev_driver=%p on_ares_backup_poll_alarm_locked. " |
||||
"driver->shutting_down=%d. " |
||||
"err=%s", |
||||
driver->request, driver, driver->shutting_down, grpc_error_string(error)); |
||||
if (!driver->shutting_down && error == GRPC_ERROR_NONE) { |
||||
fd_node* fdn = driver->fds; |
||||
while (fdn != nullptr) { |
||||
if (!fdn->already_shutdown) { |
||||
GRPC_CARES_TRACE_LOG( |
||||
"request:%p ev_driver=%p on_ares_backup_poll_alarm_locked; " |
||||
"ares_process_fd. fd=%s", |
||||
driver->request, driver, fdn->grpc_polled_fd->GetName()); |
||||
ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); |
||||
ares_process_fd(driver->channel, as, as); |
||||
} |
||||
fdn = fdn->next; |
||||
} |
||||
if (!driver->shutting_down) { |
||||
grpc_millis next_ares_backup_poll_alarm = |
||||
calculate_next_ares_backup_poll_alarm_ms(driver); |
||||
grpc_ares_ev_driver_ref(driver); |
||||
GRPC_CLOSURE_INIT(&driver->on_ares_backup_poll_alarm_locked, |
||||
on_ares_backup_poll_alarm, driver, |
||||
grpc_schedule_on_exec_ctx); |
||||
grpc_timer_init(&driver->ares_backup_poll_alarm, |
||||
next_ares_backup_poll_alarm, |
||||
&driver->on_ares_backup_poll_alarm_locked); |
||||
} |
||||
grpc_ares_notify_on_event_locked(driver); |
||||
} |
||||
grpc_ares_ev_driver_unref(driver); |
||||
GRPC_ERROR_UNREF(error); |
||||
} |
||||
|
||||
static void on_readable_locked(fd_node* fdn, grpc_error* error) { |
||||
GPR_ASSERT(fdn->readable_registered); |
||||
grpc_ares_ev_driver* ev_driver = fdn->ev_driver; |
||||
const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); |
||||
fdn->readable_registered = false; |
||||
GRPC_CARES_TRACE_LOG("request:%p readable on %s", fdn->ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
if (error == GRPC_ERROR_NONE) { |
||||
do { |
||||
ares_process_fd(ev_driver->channel, as, ARES_SOCKET_BAD); |
||||
} while (fdn->grpc_polled_fd->IsFdStillReadableLocked()); |
||||
} else { |
||||
// If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
|
||||
// timed out. The pending lookups made on this ev_driver will be cancelled
|
||||
// by the following ares_cancel() and the on_done callbacks will be invoked
|
||||
// with a status of ARES_ECANCELLED. The remaining file descriptors in this
|
||||
// ev_driver will be cleaned up in the follwing
|
||||
// grpc_ares_notify_on_event_locked().
|
||||
ares_cancel(ev_driver->channel); |
||||
} |
||||
grpc_ares_notify_on_event_locked(ev_driver); |
||||
grpc_ares_ev_driver_unref(ev_driver); |
||||
GRPC_ERROR_UNREF(error); |
||||
} |
||||
|
||||
static void on_readable(void* arg, grpc_error* error) { |
||||
fd_node* fdn = static_cast<fd_node*>(arg); |
||||
GRPC_ERROR_REF(error); /* ref owned by lambda */ |
||||
fdn->ev_driver->work_serializer->Run( |
||||
[fdn, error]() { on_readable_locked(fdn, error); }, DEBUG_LOCATION); |
||||
} |
||||
|
||||
static void on_writable_locked(fd_node* fdn, grpc_error* error) { |
||||
GPR_ASSERT(fdn->writable_registered); |
||||
grpc_ares_ev_driver* ev_driver = fdn->ev_driver; |
||||
const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); |
||||
fdn->writable_registered = false; |
||||
GRPC_CARES_TRACE_LOG("request:%p writable on %s", ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
if (error == GRPC_ERROR_NONE) { |
||||
ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, as); |
||||
} else { |
||||
// If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or
|
||||
// timed out. The pending lookups made on this ev_driver will be cancelled
|
||||
// by the following ares_cancel() and the on_done callbacks will be invoked
|
||||
// with a status of ARES_ECANCELLED. The remaining file descriptors in this
|
||||
// ev_driver will be cleaned up in the follwing
|
||||
// grpc_ares_notify_on_event_locked().
|
||||
ares_cancel(ev_driver->channel); |
||||
} |
||||
grpc_ares_notify_on_event_locked(ev_driver); |
||||
grpc_ares_ev_driver_unref(ev_driver); |
||||
GRPC_ERROR_UNREF(error); |
||||
} |
||||
|
||||
static void on_writable(void* arg, grpc_error* error) { |
||||
fd_node* fdn = static_cast<fd_node*>(arg); |
||||
GRPC_ERROR_REF(error); /* ref owned by lambda */ |
||||
fdn->ev_driver->work_serializer->Run( |
||||
[fdn, error]() { on_writable_locked(fdn, error); }, DEBUG_LOCATION); |
||||
} |
||||
|
||||
ares_channel* grpc_ares_ev_driver_get_channel_locked( |
||||
grpc_ares_ev_driver* ev_driver) { |
||||
return &ev_driver->channel; |
||||
} |
||||
|
||||
// Get the file descriptors used by the ev_driver's ares channel, register
|
||||
// driver_closure with these filedescriptors.
|
||||
static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) { |
||||
fd_node* new_list = nullptr; |
||||
if (!ev_driver->shutting_down) { |
||||
ares_socket_t socks[ARES_GETSOCK_MAXNUM]; |
||||
int socks_bitmask = |
||||
ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); |
||||
for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { |
||||
if (ARES_GETSOCK_READABLE(socks_bitmask, i) || |
||||
ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { |
||||
fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]); |
||||
// Create a new fd_node if sock[i] is not in the fd_node list.
|
||||
if (fdn == nullptr) { |
||||
fdn = static_cast<fd_node*>(gpr_malloc(sizeof(fd_node))); |
||||
fdn->grpc_polled_fd = |
||||
ev_driver->polled_fd_factory->NewGrpcPolledFdLocked( |
||||
socks[i], ev_driver->pollset_set, ev_driver->work_serializer); |
||||
GRPC_CARES_TRACE_LOG("request:%p new fd: %s", ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
fdn->ev_driver = ev_driver; |
||||
fdn->readable_registered = false; |
||||
fdn->writable_registered = false; |
||||
fdn->already_shutdown = false; |
||||
} |
||||
fdn->next = new_list; |
||||
new_list = fdn; |
||||
// Register read_closure if the socket is readable and read_closure has
|
||||
// not been registered with this socket.
|
||||
if (ARES_GETSOCK_READABLE(socks_bitmask, i) && |
||||
!fdn->readable_registered) { |
||||
grpc_ares_ev_driver_ref(ev_driver); |
||||
GRPC_CARES_TRACE_LOG("request:%p notify read on: %s", |
||||
ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable, fdn, |
||||
grpc_schedule_on_exec_ctx); |
||||
fdn->grpc_polled_fd->RegisterForOnReadableLocked(&fdn->read_closure); |
||||
fdn->readable_registered = true; |
||||
} |
||||
// Register write_closure if the socket is writable and write_closure
|
||||
// has not been registered with this socket.
|
||||
if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && |
||||
!fdn->writable_registered) { |
||||
GRPC_CARES_TRACE_LOG("request:%p notify write on: %s", |
||||
ev_driver->request, |
||||
fdn->grpc_polled_fd->GetName()); |
||||
grpc_ares_ev_driver_ref(ev_driver); |
||||
GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable, fdn, |
||||
grpc_schedule_on_exec_ctx); |
||||
fdn->grpc_polled_fd->RegisterForOnWriteableLocked( |
||||
&fdn->write_closure); |
||||
fdn->writable_registered = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
// Any remaining fds in ev_driver->fds were not returned by ares_getsock() and
|
||||
// are therefore no longer in use, so they can be shut down and removed from
|
||||
// the list.
|
||||
while (ev_driver->fds != nullptr) { |
||||
fd_node* cur = ev_driver->fds; |
||||
ev_driver->fds = ev_driver->fds->next; |
||||
fd_node_shutdown_locked(cur, "c-ares fd shutdown"); |
||||
if (!cur->readable_registered && !cur->writable_registered) { |
||||
fd_node_destroy_locked(cur); |
||||
} else { |
||||
cur->next = new_list; |
||||
new_list = cur; |
||||
} |
||||
} |
||||
ev_driver->fds = new_list; |
||||
// If the ev driver has no working fd, all the tasks are done.
|
||||
if (new_list == nullptr) { |
||||
ev_driver->working = false; |
||||
GRPC_CARES_TRACE_LOG("request:%p ev driver stop working", |
||||
ev_driver->request); |
||||
} |
||||
} |
||||
|
||||
void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) { |
||||
if (!ev_driver->working) { |
||||
ev_driver->working = true; |
||||
grpc_ares_notify_on_event_locked(ev_driver); |
||||
// Initialize overall DNS resolution timeout alarm
|
||||
grpc_millis timeout = |
||||
ev_driver->query_timeout_ms == 0 |
||||
? GRPC_MILLIS_INF_FUTURE |
||||
: ev_driver->query_timeout_ms + grpc_core::ExecCtx::Get()->Now(); |
||||
GRPC_CARES_TRACE_LOG( |
||||
"request:%p ev_driver=%p grpc_ares_ev_driver_start_locked. timeout in " |
||||
"%" PRId64 " ms", |
||||
ev_driver->request, ev_driver, timeout); |
||||
grpc_ares_ev_driver_ref(ev_driver); |
||||
GRPC_CLOSURE_INIT(&ev_driver->on_timeout_locked, on_timeout, ev_driver, |
||||
grpc_schedule_on_exec_ctx); |
||||
grpc_timer_init(&ev_driver->query_timeout, timeout, |
||||
&ev_driver->on_timeout_locked); |
||||
// Initialize the backup poll alarm
|
||||
grpc_millis next_ares_backup_poll_alarm = |
||||
calculate_next_ares_backup_poll_alarm_ms(ev_driver); |
||||
grpc_ares_ev_driver_ref(ev_driver); |
||||
GRPC_CLOSURE_INIT(&ev_driver->on_ares_backup_poll_alarm_locked, |
||||
on_ares_backup_poll_alarm, ev_driver, |
||||
grpc_schedule_on_exec_ctx); |
||||
grpc_timer_init(&ev_driver->ares_backup_poll_alarm, |
||||
next_ares_backup_poll_alarm, |
||||
&ev_driver->on_ares_backup_poll_alarm_locked); |
||||
} |
||||
} |
||||
|
||||
#endif /* GRPC_ARES == 1 */ |
@ -1,68 +0,0 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2016-2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#if GRPC_ARES != 1 |
||||
|
||||
#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" |
||||
|
||||
struct grpc_ares_request { |
||||
char val; |
||||
}; |
||||
|
||||
static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( |
||||
const char* dns_server, const char* name, const char* default_port, |
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done, |
||||
std::unique_ptr<grpc_core::ServerAddressList>* addrs, |
||||
std::unique_ptr<grpc_core::ServerAddressList>* balancer_addrs, |
||||
char** service_config_json, int query_timeout_ms, |
||||
std::shared_ptr<grpc_core::WorkSerializer> work_serializer) { |
||||
return NULL; |
||||
} |
||||
|
||||
grpc_ares_request* (*grpc_dns_lookup_ares_locked)( |
||||
const char* dns_server, const char* name, const char* default_port, |
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done, |
||||
std::unique_ptr<grpc_core::ServerAddressList>* addrs, |
||||
std::unique_ptr<grpc_core::ServerAddressList>* balancer_addrs, |
||||
char** service_config_json, int query_timeout_ms, |
||||
std::shared_ptr<grpc_core::WorkSerializer> work_serializer) = |
||||
grpc_dns_lookup_ares_locked_impl; |
||||
|
||||
static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {} |
||||
|
||||
void (*grpc_cancel_ares_request_locked)(grpc_ares_request* r) = |
||||
grpc_cancel_ares_request_locked_impl; |
||||
|
||||
grpc_error* grpc_ares_init(void) { return GRPC_ERROR_NONE; } |
||||
|
||||
void grpc_ares_cleanup(void) {} |
||||
|
||||
static void grpc_resolve_address_ares_impl(const char* name, |
||||
const char* default_port, |
||||
grpc_pollset_set* interested_parties, |
||||
grpc_closure* on_done, |
||||
grpc_resolved_addresses** addrs) {} |
||||
|
||||
void (*grpc_resolve_address_ares)( |
||||
const char* name, const char* default_port, |
||||
grpc_pollset_set* interested_parties, grpc_closure* on_done, |
||||
grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl; |
||||
|
||||
#endif /* GRPC_ARES != 1 */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue