[flask] Understand several difficulties of flask, difficult bones, thread isolation and so on

1. Three routes and their respective comparisons
2. All letters in the configuration file must be capitalized
3. The role of if __name__
4. Several solutions to core object circular references – difficult
5. Classic errors of Flask
6. Context manager
7 .flask's multithreading and thread isolation

three routes

Method 1: decorator python C#, java can use this method

from flask import Flask
app = Flask(__name__)

@app.route('/hello')
def hello():
	return 'Hello world!'

app.run(debug=True)

Method 2: Register routing php python

from flask import Flask
app = Flask(__name__)

//@app.route('/hello')
def hello():
	return 'Hello world!'

app.add_url_rule('/hello', view_func=hello)

app.run(debug=True)

Method 3: python-specific rules

from flask.views import View, MethodView
from flask import Flask, render_template, request

app = Flask(__name__)


class MyView(MethodView):
    def get(self):
        return render_template('index.html')

    def post(self):
        username = request.form.get('username')
        password = request.form.get('password')
        if username == "gp" and password == "mypassword":
            return '密码正确'
        else:
            return '密码错误'


app.add_url_rule('/', endpoint='login', view_func=MyView.as_view('login'))


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

Its process is through View's as_view -> dispatch_request in MethodView -> specific get, post and other methods.

Take a look at how flask is implemented

class View:
   methods: t.Optional[t.List[str]] = None
   provide_automatic_options: t.Optional[bool] = None
   decorators: t.List[t.Callable] = []def dispatch_request(self) -> ResponseReturnValue:
       raise NotImplementedError()@classmethod
   def as_view(
       cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
  ) -> t.Callable:
       def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
           self = view.view_class(*class_args, **class_kwargs)  # type: ignore
           return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)if cls.decorators:
           view.__name__ = name
           view.__module__ = cls.__module__
           for decorator in cls.decorators:
               view = decorator(view)
               
       view.view_class = cls  # type: ignore
       view.__name__ = name
       view.__doc__ = cls.__doc__
       view.__module__ = cls.__module__
       view.methods = cls.methods  # type: ignore
       view.provide_automatic_options = cls.provide_automatic_options  # type: ignore
       return view

In fact, flask returns a function through the as_view method. This function is the view function we need to bind to realize the change process from class to function.

By binding cls to the view_class attribute of the view function, implement view.view_class(*class_args, **class_kwargs)

To achieve the purpose of passing parameters, this is the charm of python. It is the closure or decorator used. And the first method type, but one is explicit and the other is implicit.

All letters in the configuration file must be capitalized

The parameters of the development environment, the test environment, and the production environment are different, so how to distinguish these three environments, if else get different environments? No, the three environments should be similar, and the configuration files should be used to separate them, and then set git ignore, each with its own configuration file.
Set config.py under the project root path

DEBUG=True

start-up file

app = Flask(__name__)
# 加载配置项
app.config.from_object("config")
# 读取
app.config["DEBUG"]

Note that flask stipulates that only all capital letters can be used, otherwise key error

The role of if __name__

It is an entry file, adding this judgment can ensure that the code in the entry file is only executed in the entry file.

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

Manual start is easy to understand, because he made the entry.
But the production environment is nginx+uwsgi, which is a module loaded by uwsgi. The entry file becomes uwsgi. If there is no if __name__ judgment at this time, the web server will be started twice. One is for uwsgi, the other is for the python entry file under development, and the built-in server of flask.

Is there any difference between the return of a view function and the return of a normal function?

Yes, it is not a return of a simple ordinary function, but a response object that returns a response.
The view function will not only return a string similar to helloworld,
but return a text containing status code 200, 404, 301, content-type http headers, default text/html
and return.

it is equal to

@app.route('/hello')
def hello():
	return 'helloworld'
@app.route('/hello')
def hello():
	return '<html></html>'
@app.route('/hello')
def hello():
	headers = {
    
    
		'content-type':'text/plain'
	}
	response = make_response('<html></html>',200)
	response.headers=headers
	return response 

The last one will parse out html because he treats it as text/plain ordinary text, not html text.

@app.route('/hello')
def hello():
	
	return '<html></html>', 301,headers

It is equivalent to this, flask still returns the response object.

Solve repeated references to core objects

In flask, it is a very bad experience to write the core object of the app and the registration route in the same file

from flask import Flask
app = Flask(__name__)

@app.route('/hello')
def hello():
	return 'Hello world!'

app.run(debug=True)

There is no problem with only 1 or 2 routes. However, when there are hundreds of routes and 10 developers are developing at the same time, everyone modifies this entry file, which will cause a poor experience.
So we need to be able to split the route, so how can we split it?
You can manually register in the way of add_url, but this is not elegant.
I think there are two better
insert image description here
ways 1: keep only one core object app, define a function in util_help.py,

from flask import Flask
def my_function():
    # 定义静态变量
    if not hasattr(my_function, "my_static_variable"):
        my_function.my_static_variable = Flask(__name__)
    
    return my_function.my_static_variable

Anyone who uses the app object can go here to get it, because it defines a static variable, so it is unique and will not be built multiple times with package references. Registering routes is no problem.

Method 2: Normally, there will be a circular reference, because the entry file will import the app.web.book file, and the book file will go to the entry file to import the app, resulting in two apps. The app object in the book file registers the routing function , but not the app object of the entry file. At this time, the blueprint works.

Flask classic error

Working outside application context
AppContext, RequestContext, the direct relationship between Flask and Request.
The application context object Flask
request context object Request
Flask AppContext
Request RequestContext
insert image description here
insert image description here
is the source code for context management!
Use these two sentences to push current_app into the
insert image description here
logical use case of context management:
database:
1. Connect to the database
2. Execute sql
3. Release resources
Solution 1:
try
...
except
...
finall
...
Solution 2:
with statement, you can compare whith open() as f:
...

File reading and writing:

try: 
	f = open(r"D:/t.txt")
	print(f.read())
finally:
	f.close()
with open(r"D:/t.txt") as f:
	print(f.read())

As long as the __enter__ and __exit__ methods are implemented, it is a context manager.

class A:
	def __enter__(self):
	    a = 1
	def __exit__(self,exc_type, exc_value, tb):	
		if tb:
			print("process exception")
		else:
			print('no exception')
		b = 2
		return True
		# 或者返回 False, 代表异常还需要接着抛出去,
		# True 异常里面出来,外面不抛出异常
		# 什么都不返回 和False 的逻辑一致
		

with A() as obj_A:
     pass

The current obj_A is None
as what is returned is not the context manager, but the value returned by the enter method.

The variable value wrapped by the with statement has value, and after running exit, it has no value. Because push and pop are executed respectively,
push is to put the variable in, and pop will pop up.

Flask's multithreading and thread isolation

Resources are scarce, and computer resources compete for computer resources.
A process consists of at least one process, and a process is the basic unit of competing computers.

Single-core cpu, can only execute one application forever?
switch between different applications

Process scheduling Switching to another process Operating system principle
Process/thread overhead is very large Context
thread is part of a process 1 thread Multiple threads
cpu Granularity is too large Smaller unit cpu resource
thread

The process allocates resources, memory resources,
and threads use the cpu to execute code

The code instructs the cpu to execute raw materials.
Threads cannot own resources, but threads belong to processes and can access process resources.

main thread


def worker():
	print("i am thread")
	t = threading.current_thread()
	print(t.get.getName())
import threading
print("i  am  7 月")
t = threading.current_thread()
print(t.getName())

new_t = threading.Thread(target=work)

insert image description here
Thread names can be changed.
Multi-threading is to make full use of the performance advantages of the cpu.
Asynchronously becomes
a single-core cpu
4 core A core B core parallel execution order
Python cannot make full use of the advantages of multi-core cpu, this sentence is correct. Because python has GIL global interpreter lock global interpreter lock

Is python's multithreading tasteless? no. IO intensive is useful.

The lock is responsible for
the fine-grained lock of thread safety. The interpreter GIL multi-core CPU executes with 1 thread, which guarantees thread safety
a+=1
bytecode to a certain extent.

Is python multithreading tasteless?

Python has GIL, which is a single thread based on bytecode bytecode.
Node,js is single-process and single-threaded.
For CPU-intensive programs, multithreading is garbage, and for
IO-intensive programs, query databases, request network resources, and read and write files. Then python's multithreading makes sense.

The relationship between the flask web framework and client requests

Framework code/webserver/code written by oneself should be distinguished from
Java PHP nginx Apache Tomcat IIS
app.run is to start a flask built-in webserver
in the real environment, do not use the built-in flask, because it is single-process and single-threaded by default form of service. It is possible in the development stage, which is convenient for debugging, and it is not used when it is actually deployed. In fact, the server that comes with flask can also open its own multi-threaded model, which can open single-threaded multi-process, or multi-process, multi-threaded model.

app.run(host='0.0.0.0', debug=True, port=81,threaded=True,)

The default value of process is 1, and multiple processes can also be enabled, which is also a parameter.

For an interface, the actual request is different, how to ensure that the returned results are isolated.

For example, user a visits the bibliography list whose author is Jin Yong page=1. . User b is another one.
So, how is the instantiated request request information isolated?
It is queued under single thread, no problem. But how to do it under multithreading?
Answer: Thread isolation technology
insert image description here
A variable name points to multiple instantiated Requests, how to solve this problem.

import threading
request = None
request1 = Request()
request2 = Request()
request3 = Request()

## python 字典 php 数组
request = {
    
    
	key1:v1
	k2:v2
	k3:v3
}

所以用线程的标识 作为key,文件就解决了。
request = {
    
    
	thread_key1:v1
	thread_key2:v2
	thread_key3:v3
}

Use the basic data structure of dictionary to complete thread isolation.
Flask is accomplished with the help of the Local object of the local module under werkzeug.
The essence of the local object is to use a dictionary to solve it.
Follow the source code:
insert image description here

Get the id number of the current thread
insert image description here
Local object
insert image description here

How to get the value of the thread in the current thread through the thread id number?
L Thread-isolated objects
t1 La and t2 La are completely isolated and do not interfere with each other.
This Local object does not have to be used under flask, but can also be used by itself.



class A:
	b = 1

my_obj = Local()

def worker():
	#新线程
	my_obj.b = 2


new_t = threading.Thread(target=work)
new_t.start()
time.sleep(1)
from werkzeug.local import Local


my_obj = Local()
my_obj.b = 1
def worker():
	#新线程
	my_obj.b = 2
	print("in new thread b is :"+ str(my_obj.b))


new_t = threading.Thread(target=work)
new_t.start()
time.sleep(1)
print("in main thread b is :"+ str(my_obj.b))

Relationship between Local, LocalStack and Dictionary

insert image description here

The core methods push/pop and top of LocalStack, the core attribute local inherited from localStack is the object of Local().
insert image description here

insert image description here
Local uses a dictionary to achieve on-site isolation, and localstack is a thread-isolated stack structure.

Basic usage of LocalStack

The following explanation reflects the usage of LocalStack as a feature of stack.

from werkzeug.local import LocalStack
# 重点是要实现push, pop和top
s = LocalStack()
s.push(1)
s.top  # 读取栈顶元素,但不会删除, 需要注意点是,以属性的形式读取的,不需要加括号
s.pop() # 获取并且弹出栈顶

insert image description here
And the stack is last in first out.

The following explanation reflects the usage of LocalStack as a feature of Local.
insert image description here

from werkzeug.local import LocalStack

my_stack = LocalStack()
my_Stack.push(1)
print("in main thread after push, value is:"+ str(my_stack.pop))
def worker():
	# 新线程
	print("in main thread after push, value is:"+ str(my_stack.pop))
	my_Stack.push(2)
	print("in main thread after push, value is:"+ str(my_stack.pop))

new_t = threading.Thread(target=work)
new_t.start()
time.sleep(1)
print("in main thread b is :"+ str(my_Stack.top))

Print the result:
insert image description here
It’s good to know this feature of localstack before. At that time, it was so hard to write multiple processes by hand~~~~ Solve the problem of insufficient graphics card.

Why does flask need to use localstack?

insert image description here
1. A stack structure is required, and the two contexts of AppContext and RequestContext are pushed into the corresponding stack structure.
2. This stack structure also needs to be thread-isolated.
It is to isolate which objects to write: 1.app, 2.Request
The essential reason is that a variable name points to the instantiation request of different threads, which is impossible. Therefore, thread isolation is required. Variables will not be polluted each other. There are three
insert image description here
thread-isolated objects!!!
insert image description here
app = create_app() and current_app have No thread isolation???
No need, because it will only be generated once in the main thread, and then bound to localProxy.

Guess you like

Origin blog.csdn.net/weixin_40293999/article/details/130437435