Merge pull request #22643 from gnossen/manylinux2010_soreuseport

Support SO_REUSEPORT on manylinux2010
pull/22626/head
Richard Belleville 5 years ago committed by GitHub
commit 2974b5c816
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      examples/python/multiprocessing/BUILD
  2. 29
      examples/python/multiprocessing/README.md
  3. 4
      examples/python/multiprocessing/client.py
  4. 10
      examples/python/multiprocessing/server.py
  5. 2
      src/core/lib/iomgr/socket_utils_common_posix.cc

@ -37,6 +37,7 @@ py_binary(
name = "client",
testonly = 1,
srcs = ["client.py"],
imports = ["."],
python_version = "PY3",
srcs_version = "PY3",
deps = [
@ -50,6 +51,7 @@ py_binary(
name = "server",
testonly = 1,
srcs = ["server.py"],
imports = ["."],
python_version = "PY3",
srcs_version = "PY3",
deps = [

@ -1,28 +1,27 @@
## Multiprocessing with gRPC Python
Multiprocessing allows application developers to sidestep the Python global
interpreter lock and achieve true concurrency on multicore systems.
interpreter lock and achieve true parallelism on multicore systems.
Unfortunately, using multiprocessing and gRPC Python is not yet as simple as
instantiating your server with a `futures.ProcessPoolExecutor`.
The library is implemented as a C extension, maintaining much of the state that
drives the system in native code. As such, upon calling
[`fork`](http://man7.org/linux/man-pages/man2/fork.2.html), much of the
state copied into the child process is invalid, leading to hangs and crashes.
However, calling `fork` without `exec` in your python process is supported
*before* any gRPC servers have been instantiated. Application developers can
[`fork`](http://man7.org/linux/man-pages/man2/fork.2.html), any threads in a
critical section may leave the state of the gRPC library invalid in the child
process. See this [excellent research
paper](https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf)
for a thorough discussion of the topic.
Calling `fork` without `exec` in your process *is* supported
before any gRPC servers have been instantiated. Application developers can
take advantage of this to parallelize their CPU-intensive operations.
## Calculating Prime Numbers with Multiple Processes
This example calculates the first 10,000 prime numbers as an RPC. We instantiate
one server per subprocess, balancing requests between the servers using the
[`SO_REUSEPORT`](https://lwn.net/Articles/542629/) socket option. Note that this
option is not available in `manylinux1` distributions, which are, as of the time
of writing, the only gRPC Python wheels available on PyPI. To take advantage of this
feature, you'll need to build from source, either using bazel (as we do for
these examples) or via pip, using `pip install grpcio --no-binary grpcio`.
[`SO_REUSEPORT`](https://lwn.net/Articles/542629/) socket option.
```python
_PROCESS_COUNT = multiprocessing.cpu_count()
@ -65,3 +64,11 @@ For example,
```
bazel run //examples/python/multiprocessing:client -- [::]:33915
```
Alternatively, generate code using the following and then run the client and server
directly:
```python
cd examples/python/helloworld
python -m grpc_tools.protoc -I . prime.proto --python_out=. --grpc_python_out=.
```

@ -26,8 +26,8 @@ import sys
import grpc
from examples.python.multiprocessing import prime_pb2
from examples.python.multiprocessing import prime_pb2_grpc
import prime_pb2
import prime_pb2_grpc
_PROCESS_COUNT = 8
_MAXIMUM_CANDIDATE = 10000

@ -29,8 +29,8 @@ import sys
import grpc
from examples.python.multiprocessing import prime_pb2
from examples.python.multiprocessing import prime_pb2_grpc
import prime_pb2
import prime_pb2_grpc
_LOGGER = logging.getLogger(__name__)
@ -67,12 +67,6 @@ def _run_server(bind_address):
_LOGGER.info('Starting new server.')
options = (('grpc.so_reuseport', 1),)
# WARNING: This example takes advantage of SO_REUSEPORT. Due to the
# limitations of manylinux1, none of our precompiled Linux wheels currently
# support this option. (https://github.com/grpc/grpc/issues/18210). To take
# advantage of this feature, install from source with
# `pip install grpcio --no-binary grpcio`.
server = grpc.server(futures.ThreadPoolExecutor(
max_workers=_THREAD_CONCURRENCY,),
options=options)

@ -210,7 +210,6 @@ static gpr_once g_probe_so_reuesport_once = GPR_ONCE_INIT;
static int g_support_so_reuseport = false;
void probe_so_reuseport_once(void) {
#ifndef GPR_MANYLINUX1
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
/* This might be an ipv6-only environment in which case 'socket(AF_INET,..)'
@ -222,7 +221,6 @@ void probe_so_reuseport_once(void) {
"check for SO_REUSEPORT", grpc_set_socket_reuse_port(s, 1));
close(s);
}
#endif
}
bool grpc_is_socket_reuse_port_supported() {

Loading…
Cancel
Save