| ## Multiprocessing with gRPC Python |
| |
| Multiprocessing allows application developers to sidestep the Python global |
| 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), 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. |
| |
| ```python |
| _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](https://docs.bazel.build/versions/master/install.html) |
| 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 |
| ``` |
| |
| Alternatively, generate code using the following and then run the client and server |
| directly: |
| |
| ```python |
| cd examples/python/multiprocessing |
| python -m grpc_tools.protoc -I . prime.proto --python_out=. --grpc_python_out=. |
| ``` |