Directory
0. Preface:
1. Introduction to gevent.monkey:
2. Introduction to grpc.gevent:
3. Flask Demo code
4. Pressure test results
5. Stress test report
6 Conclusion
0. Foreword:
Flask itself is not an asynchronous framework, so there will be performance bottlenecks when processing high concurrent requests. However, Flask can improve concurrency performance through integration with other asynchronous frameworks and libraries.
So the following will introduce the two coroutine libraries of gevent and gprc, how to combine them into the Flask project to improve the concurrency performance of the project itself.
In this article, I will write a simple interface code, use the ab (Apache bench) tool to perform request pressure testing, and observe how the two modules improve the performance of the Flask project.
1. gevent.monkey introduction:
gevent is a coroutine-based Python network programming library that makes writing asynchronous code easier and more efficient. In gevent, an efficient coroutine and event loop mechanism are implemented through greenlet and libev.
The monkey module is an important module in the gevent library. It can dynamically replace the blocking I/O operation in the standard library with the non-blocking I/O operation of gevent, so that the original synchronous blocking program becomes asynchronous non-blocking program of. By using the monkey module, we can easily change the program into an asynchronous program based on coroutines without modifying the original code, and improve the concurrent performance of the program.
Specifically, the monkey module can replace the blocking I/O operations in the standard library through the following functions:
- patch_all(): Used to automatically replace all replaceable blocking I/O operations in the standard library with gevent’s non-blocking I/O operations.
- patch_socket(): Used to replace the blocking I/O operation in the socket module with the non-blocking I/O operation of gevent.
- patch_select(): Used to replace the blocking I/O operation in the select module with the non-blocking I/O operation of gevent.
- patch_time(): Used to replace the blocking sleep operation in the time module with the non-blocking sleep operation of gevent.
It should be noted that when using the monkey module, some side effects may occur, such as modifying the global variables and functions of the standard library, resulting in unpredictable behavior of other modules. Therefore, when using the monkey module, you should read the documentation carefully and perform necessary tests and verifications.
2. grpc.gevent introduction:
grpc.experimental.gevent is a Python module for using gRPC’s asynchronous calls with the gevent coroutine library. It provides a gRPC asynchronous call implementation based on gevent coroutines, and can be easily used with other gevent-based network programming libraries.
When using the grpc.experimental.gevent module, the combination of gevent and gRPC can be realized through the following steps:
- Create a gevent coroutine pool to manage all gRPC asynchronous calls.
- Use the grpc.experimental.gevent interceptor to convert all gRPC client calls to gevent coroutines.
- Run all gRPC asynchronous calls in the gevent coroutine pool.
- Handle all gRPC responses in a coroutine.
Using the grpc.experimental.gevent module can effectively improve the concurrent processing capability of gRPC applications, and can make gRPC applications more compatible with other gevent-based network programming libraries. It should be noted that when using the grpc.experimental.gevent module, you should read the documentation carefully and perform necessary tests and verifications to ensure the correctness and stability of the program.
3. Flask Demo code
Sleep for 2 seconds by time.sleep(2) to simulate the blocking situation, and then we start to test the concurrency separately
import time from flask import Flask # Method of using gevent # from gevent import monkey # monkey. patch_all() # How to use grpc # import grpc.experimental.gevent as grpc_gevent # grpc_gevent.init_gevent() app = Flask(__name__) @app.route('/') #Match routing def hello(): # Simulate blocking time. sleep(2) return "Hello World" if __name__ == '__main__': app.run(port=7779, threaded=True)
4. Pressure test results
Command ab -c 100 -n 5000 http://127.0.0.1:7779/ pressure test
Flask + gevent | Flask + grpc_gevent | |
requests per second | 49.77 | 49.83 |
Test time spent | 100.455 | 100.334 |
5. Pressure test report
# Flask + grpc_gevent Server Software: Werkzeug/2.2.3 Server Hostname: 127.0.0.1 Server Port: 7779 Document Path: / Document Length: 11 bytes Concurrency Level: 100 Time taken for tests: 100.334 seconds Complete requests: 5000 Failed requests: 0 Total transferred: 920000 bytes HTML transferred: 55000 bytes Requests per second: 49.83 [#/sec] (mean) Time per request: 2006.675 [ms] (mean) Time per request: 20.067 [ms] (mean, across all concurrent requests) Transfer rate: 8.95 [Kbytes/sec] received Connection Times (ms) min mean[ + /-sd] median max Connect: 0 0 0.3 0 3 Processing: 2001 2005 9.3 2004 2101 Waiting: 2001 2005 9.2 2003 2101 Total: 2001 2005 9.5 2004 2102 Percentage of the requests served within a certain time (ms) 50% 2004 66% 2004 75% 2005 80% 2005 90% 2006 95% 2009 98% 2028 99% 2067 100% 2102 (longest request) # Flask + gevent Server Software: Werkzeug/2.2.3 Server Hostname: 127.0.0.1 Server Port: 7779 Document Path: / Document Length: 11 bytes Concurrency Level: 100 Time taken for tests: 100.455 seconds Complete requests: 5000 Failed requests: 0 Total transferred: 920000 bytes HTML transferred: 55000 bytes Requests per second: 49.77 [#/sec] (mean) Time per request: 2009.092 [ms] (mean) Time per request: 20.091 [ms] (mean, across all concurrent requests) Transfer rate: 8.94 [Kbytes/sec] received Connection Times (ms) min mean[ + /-sd] median max Connect: 0 0 0.3 0 3 Processing: 2001 2006 11.2 2002 2050 Waiting: 2001 2006 11.2 2002 2050 Total: 2001 2006 11.2 2002 2051 Percentage of the requests served within a certain time (ms) 50% 2002 66% 2003 75% 2003 80% 2003 90% 2024 95% 2038 98% 2046 99% 2048 100% 2051 (longest request)
6. Conclusion
Through the pressure test, it is found that grpc_gevent seems to improve the performance of flask better. It may be because I only use sleep to simulate blocking in my code, but the I/O operations in real projects are more complicated, such as database query, file reading and writing, and other operations. , using gevent may be more suitable, because it can convert these I/O operations to non-blocking, thereby improving the concurrent performance of the server.
If your Flask project uses pymilvus, and you use gevent to implement asynchronous non-blocking, you will get stuck when the project starts, stuck in the milvus connection method. At this time, you can replace it with grpc_gevent to solve this problem.