Richard Belleville
7b2c8c27b0
By popular demand, we'll now be offering separate py_grpc_library and py_proto_library targets sharing the same interface as within google3. This change necessitated some modifications to how we pull in our own Python-level dependencies and how we make those available to those pulling in our project via Bazel. There is now a grpc_python_deps() Bazel workspace rule that pulls in the appropriate dependencies, which should be called from the client project's WORKSPACE file. A test has been added to the bazel/test/ directory to verify that this behavior works as intended. It's worth noting that the protobuf repository's usage of Starlark bind() caused a great deal of trouble in ensuring that we could also pull in six. This change also required a change in the way generated proto code is imported in the channelz and health-check modules, as well as in their associated tests. We were importing them two different ways, each relative. This resulted in two different module objects being imported into the process, which were incompatible. I am not sure exactly what caused this behavior to begin, as this should have been possible before this PR. As a workaround, I am simply trying two different absolute imports and using the one that works. This should function both inside and outside of Bazel environments. |
5 years ago | |
---|---|---|
.. | ||
test | Fix lint errors | 6 years ago |
BUILD | Separate py_grpc_library and py_proto_library. | 5 years ago |
README.md | Remove a statement proven wrong by science | 6 years ago |
client.py | Properly import protos | 6 years ago |
prime.proto | Add basic multiprocessing-based server | 6 years ago |
server.py | Fix multiprocessing example for MacOS. | 6 years ago |
README.md
Multiprocessing with gRPC Python
Multiprocessing allows application developers to sidestep the Python global
interpreter lock and achieve true concurrency 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
, 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
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
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
.
_PROCESS_COUNT = multiprocessing.cpu_count()
On the server side, we detect the number of CPUs available on the system and spawn exactly that many child processes. If we spin up fewer, we won't be taking full advantage of the hardware resources available.
Running the Example
To run the server,
ensure bazel
is installed
and run:
bazel run //examples/python/multiprocessing:server &
Note the address at which the server is running. For example,
...
[PID 107153] Binding to '[::]:33915'
[PID 107507] Starting new server.
[PID 107508] Starting new server.
...
Note that several servers have been started, each with its own PID.
Now, start the client by running
bazel run //examples/python/multiprocessing:client -- [SERVER_ADDRESS]
For example,
bazel run //examples/python/multiprocessing:client -- [::]:33915