当前位置:   article > 正文

Python 使用轻量级 Flask 框架搭建 Web 服务器详细教程(基础篇)_flask搭建web服务器

flask搭建web服务器

01、Flask框架服务器

render_template模板页面传参

@app.route('/index')
def index():
    user = {'username':'duke'}
    #将需要展示的数据传递给模板进行显示
    return render_template('index.html',title='我的',user=user)
  • 1
  • 2
  • 3
  • 4
  • 5
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{ title }} - 博客</title>
</head>
<body>
        <h1> Hello ,{{ user.username }} !</h1>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

session机制的介绍:

RuntimeError: The session is unavailable because no secret key was set.  Set the secret_key on the application to something unique and secret. 

运行错误:会话不可用,因为没有设置密钥。将应用程序上的secret_key设置为一些独特且秘密的内容。
  • 1
  • 2
  • 3
from flask import Flask, request, session

app = Flask(__name__)
app.secret_key = "wristwaking"

@app.route("/")
def index():
    session["login_user"] = "唤醒手腕"
    print(session.get("login_user"))
    return "index"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

request的基本用途介绍

TypeError: The view function for 'index' did not return a valid response. The function either returned None or ended without a return statement.
错误类型:“索引”的视图函数没有返回有效的响应。函数返回None或没有rerun语句。
  • 1
  • 2

这是因为没有在URL参数中找到infor,所以request.args.get('infor')返回Python内置的None,而Flask不允许返回None。

解决方法很简单,我们先判断下它是不是None:

@app.route('/')
def hello_world():
    data = request.args.get('infor')
    if data==None:
        # do something
        return ''
    return data
	# 第二种方案,就是设置初始值
	# data = request.args.get("infor") # 设置默认值
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

还记得上面有一次请求是这样的吗? http://127.0.0.1:5000?infor=1&infor=2,仔细看下,infor有两个值。

@app.route('/')
def hello_world():
    data = request.args.getlist('infor')  # 返回一个list
    return str(data)
  • 1
  • 2
  • 3
  • 4

在request请求中,解析POST数据

# username = wristwaking  password = 5201314
@app.route("/form", methods=["POST"])
def form():
    print(request.form)
    # Immutable Multi Dict 不可改变的多个字典
    # ImmutableMultiDict([('username', 'wristwaking'), ('password', '5201314')])
    print(request.stream)
    # <werkzeug.wsgi.LimitedStream object at 0x000002A9AF2CE4F0>
    print(request.args)
    # ImmutableMultiDict([])
    return "success"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

我们要想办法把我们要的username、password提取出来,怎么做呢?自己写?不用,Flask已经内置了解析器request.form

@app.route("/form", methods=["POST"])
def form():
    print(request.stream.read())
    # b'username=wristwaking&password=5201314'
    print(request.stream.read())
    # b'' 说明流只读取到一遍了
    return "success"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

解释原因:request.stream.read()读取到的是字节流,我们进行解码,规则是utf-8,结果如下所示:

@app.route("/form", methods=["POST"])
def form():
    data = request.stream.read().decode(encoding="utf-8")
    print(data)
    # username=wristwaking&password=5201314
    return "success"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

@app.route('/register', methods=['POST'])
def register():
    print(request.headers)
    # print(request.stream.read()) # 不要用,否则下面的form取不到数据
    print(request.form)
    print(request.form['name'])
    print(request.form.get('name'))
    print(request.form.getlist('name'))
    print(request.form.get('nickname', default='唤醒手腕'))
    return 'welcome'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

响应JSON时,除了要把响应体改成JSON格式,响应头的Content-Type也要设置为 application/json

@app.route('/add', methods=['POST'])
def add():
    result = {'sum': request.json['a'] + request.json['b']}
    return Response(json.dumps(result),  mimetype='application/json')

# 使用 jsonify 工具函数即可

@app.route('/add', methods=['POST'])
def add():
    result = {'sum': request.json['a'] + request.json['b']}
    return jsonify(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Flask中appcurrent_app的理解

在Flask内部维护者两个线程隔离的栈,current_app指向了AppContext(应用上下文)中的栈顶。

线程有个叫做ThreadLocal的类,也就是通常实现线程隔离的类。而werkzeug自己实现了它的线程隔离类:werkzeug.local.Local,LocalStack就是用Local实现的。

LocalStack是flask定义的线程隔离的栈存储对象,分别用来保存应用和请求上下文。它是线程隔离的意思就是说,对于不同的线程,它们访问这两个对象看到的结果是不一样的、完全隔离的。这是根据pid的不同实现的,类似于门牌号。

而每个传给flask对象的请求,都是在不同的线程中处理,而且同一时刻每个线程只处理一个请求。所以对于每个请求来说,它们完全不用担心自己上下文中的数据被别的请求所修改。
在这里插入图片描述
request指向了RequestContext(请求上下文)栈顶,当请求进入的时候,Request对象被压入栈,从而request有了指向处理请求,接下来会判断AppContext栈顶是否为空,若为空则向栈中压入一个AppContext对象,即app

从而current_app就有了指向,所以我们在项目中使用是没有报错的,而我们上面的代码不是在请求中实现的,所以AppContext栈顶为空,current_app并没有指向一个AppContext对象。

02、Flask文件传输

from flask import Flask, request
 
from werkzeug.utils import secure_filename
import os
 
app = Flask(__name__)
 
# 文件上传目录
app.config['UPLOAD_FOLDER'] = 'static/uploads/'
# 支持的文件格式
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}  # 集合类型
 
 
# 判断文件名是否是我们支持的格式
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']
 
 
@app.route('/')
def hello_world():
    return 'hello world'
 
 
@app.route('/upload', methods=['POST'])
def upload():
    upload_file = request.files['image']
    if upload_file and allowed_file(upload_file.filename):
        filename = secure_filename(upload_file.filename)
        # 将文件保存到 static/uploads 目录,文件名同上传时使用的文件名
        upload_file.save(os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], filename))
        return 'info is '+request.form.get('info', '')+'. success'
    else:
        return 'failed'
 
 
if __name__ == '__main__':
    app.run(port=5000, debug=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

app.config中的config是字典的子类,可以用来设置自有的配置信息,也可以设置自己的配置信息。函数allowed_file(filename)用来判断filename是否有后缀以及后缀是否在app.config['ALLOWED_EXTENSIONS']中。

客户端上传的图片必须以image标识。upload_file是上传文件对应的对象。

  1. app.root_path获取server.py所在目录在文件系统中的绝对路径。

  2. upload_file.save(path)用来将upload_file保存在服务器的文件系统中,参数最好是绝对路径,否则会报错(网上很多代码都是使用相对路径,但是笔者在使用相对路径时总是报错,说找不到路径)

  3. os.path.join()用来将使用合适的路径分隔符将路径组合起来。

我们用python客户端测试下:

import requests
 
file_data = {'image': open('Lenna.jpg', 'rb')}
 
user_info = {'info': 'Lenna'}
 
resp = requests.post("http://127.0.0.1:5000/upload", data=user_info, files=file_data)
 
print(resp.text)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

要控制上产文件的大小,可以设置请求实体的大小,例如:

app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB
  • 1

不过,在处理上传文件时候,需要使用try:... except:...

如果要获取上传文件的内容可以:

file_content = request.files['image'].stream.read()
  • 1

03、Flask后端鉴权方案

Cookie是存储在客户端的记录访问者状态的数据。具体原理,请见 http://zh.wikipedia.org/wiki/Cookie ,常用的用于记录用户登录状态的session大多是基于cookie实现的。

@app.route('/add')
def login():
    resp = Response('add cookies')
    resp.set_cookie(key='name', value='唤醒手腕', expires=time.time()+6*60)
    return resp

@app.route('/show')
def show():
    return request.cookies.__str__()
 
@app.route('/delete')
def del_cookie():
    resp = Response('delete cookies')
    resp.set_cookie('name', '', expires=0)
    return resp
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

由上可以看到,可以使用Response.set_cookie添加和删除cookie,expires参数用来设置cookie有效时间,它的值可以是datetime对象或者unix时间戳,笔者使用的是unix时间戳。

res.set_cookie(key='name', value='letian', expires=time.time()+6*60)
# 上面的expire参数的值表示cookie在从现在开始的6分钟内都是有效的。
  • 1
  • 2

04、代理IP的原理

当我们对某些网站进行爬取的时候,我们经常会换IP来避免爬虫程序被封锁。这些代理IP地址是如何获取的呢?其实很简单,目前网络上有很多IP代理商,例如神龙,天启,芝麻等等,这些代理商一般都会提供透明代理,匿名代理,高匿代理。本文就讲讲各种代理 IP 背后的原理

代理IP的介绍

代理实际上指的就是代理服务器,英文叫作 proxy server,它的功能是代理网络用户去取得网络信息。

形象地说,它是网络信息的中转站。在我们正常请求一个网站时,是发送了请求给 Web 服务器,Web 服务器把响应传回给我们。

如果设置了代理服务器,实际上就是在本机和服务器之间搭建了一个桥,此时本机不是直接向 Web 服务器发起请求,而是向代理服务器发出请求,请求会发送给代理服务器,然后由代理服务器再发送给 Web 服务器,接着由代理服务器再把 Web 服务器返回的响应转发给本机。

这样我们同样可以正常访问网页,但这个过程中 Web 服务器识别出的真实 IP 就不再是我们本机的 IP 了,就成功实现了 IP 伪装,这就是代理的基本原理。

代理类型:高匿 > 混淆 > 匿名 > 透明

代理IP一共可以分成4种类型,除了前面提到过的透明代理IP,匿名代理IP,高匿名代理IP,还有一种就是混淆代理IP。从基础的安全程度来说呢,他们的排列顺序是高匿 > 混淆 > 匿名 > 透明。

代理原理

代理类型主要取决于代理服务器端的配置,不同配置会形成不同的代理类型。在配置中,这三个变量REMOTE_ADDR,HTTP_VIA,HTTP_X_FORWARDED_FOR 是决定性因素。

(一)REMOTE_ADDR

REMOTE_ADDR 表示客户端的 IP,但是它的值不是由客户端提供的,而是服务器根据客户端的 IP 指定的。

如果使用浏览器直接访问某个网站,那么网站的 web 服务器(Nginx、Apache等)就会把 REMOTE_ADDR 设为客户端的 IP 地址。

如果我们给浏览器设置代理,我们访问目标网站的请求会先经过代理服务器,然后由代理服务器将请求转化到目标网站。那么网站的 web 代理服务器就会把 REMOTE_ADDR 设为代理服务器的 IP。

(二)X-Forwarded-For(XFF)

X-Forwarded-For 是一个 HTTP 扩展头部,用来表示 HTTP 请求端真实 IP。当客户端使用了代理时,web 代理服务器就不知道客户端的真实 IP 地址。为了避免这个情况,代理服务器通常会增加一个 X-Forwarded-For 的头信息,把客户端的 IP 添加到头信息里面。

X-Forwarded-For 请求头格式如下:

X-Forwarded-For: client, proxy1, proxy2
  • 1

client 表示客户端的 IP 地址;proxy1 是离服务端最远的设备 IP; proxy2 是次级代理设备的 IP;从格式中,可以看出从 client 到 server 是可以有多层代理的。

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

X-Forwarded-For: IP0, IP1, IP2
  • 1

Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得。我们知道 HTTP 连接基于 TCP 连接,HTTP 协议中没有 IP 的概念,Remote Address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。

(三)HTTP_VIA

via 是 HTTP 协议里面的一个header,记录了一次 HTTP 请求所经过的代理和网关,经过1个代理服务器,就添加一个代理服务器的信息,经过2个就添加2个。

正向代理 和 反向代理: 原理介绍

反向代理

1、用户发送请求到服务器(访问的其实是反向代理服务器,但用户不知道)

2、反向代理服务器发送请求到真正的服务器

3、真正的服务器将数据返回给反向代理服务器

4、反向代理服务器再将数据返回给用户

在这里插入图片描述
反向代理有什么用为什么要这么做:

作用:用户请求过多,服务器会有一个处理的极限。所以使用反向代理服务器接受请求,再用均衡负载将请求分布给多个真实的服务器。既能提高效率还有一定的安全性。

用途:如果不采用代理,用户的IP、端口号直接暴露在Internet(尽管地址转换NAT),外部主机依然可以根据IP、端口号来开采主机安全漏洞,所以在企业网,一般都是采用代理服务器访问互联网。

正向代理与反向代理最简单的区别:

正向代理隐藏的是用户,反向代理隐藏的是服务器

05、正向代理服务器

vue proxy 原理介绍

浏览器会因为同源策略跨域,但服务端不禁止,npm run dev 本来就是运行了服务器,所有利用服务器发送请求即可(将所有请求转发到自己的node服务器然后发送请求,即 代理

使用Node开发http正向代理服务器:

var http = require("http");
var url = require("url");

// 会建立一个http服务器,并监听 8080 端口:

http.createServer(function(req,res){
    console.log("start request:",req.url);
 
    var option = url.parse(req.url);
    option.headers = req.headers;
 
 	// 当接收到请求信息时,从请求头发获取信息并进行转发:
    var proxyRequest = http.request(options, function(proxyResponse){
 
 		// 其余就是对信息输出,以方便我们了解到代理是否生效、代理内容如何等:
        proxyResponse.on("data", function(chunk){
            console.log("proxyResponse length",chunk.length);
        });
        proxyResponse.on("end", function(){
            console.log("proxyed request ended");
            res.end();
        })
 
        res.writeHead(proxyResponse.statusCode,proxyResponse.headers);
    });
 
    
    req.on("data",function(chunk){
        console.log("in request length:",chunk.length);
        proxyRequest.write(chunk,"binary");
    })
 
    req.on("end",function(){
        console.log("original request ended");
        proxyRequest.end();
    })
 
}).listen(8080);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

06、Flask.py底层

个人总结吧,以前学东西总是不喜欢看源码,主要是英语不行,后来才知道源码的重要性,学会看源码才能真正的去探究底层的实现,况且现在很多第三方库都把源码的注释写的很清楚。

Flask.py文件相关底层的介绍
在这里插入图片描述
源码中Flask类进行继承了Scaffold类(脚手架),关于Scaffold类的__init__展示:

def __init__(
        self,
        import_name: str, 
        #: The name of the package or module that this object belongs
        static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
        static_url_path: t.Optional[str] = None,
        template_folder: t.Optional[str] = None,
        #: The path to the templates folder, relative to ···
        root_path: t.Optional[str] = None,
    ):
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

:attr:root_path, to add to the template loader.
在这里插入图片描述
Flask对象实现了一个WSGI应用程序,并作为中心对象。它被传递给应用程序的模块或软件包的名称。一旦创建了它,它将作为视图函数、URL规则、模板配置等的中心注册表。
在这里插入图片描述
装饰器@route,装饰视图函数

底层原理:@app.route("/") def index(): -> app.add_url_rule("/", view_func=index)
在这里插入图片描述
项目启动app.run(),源码介绍

 def run(
        self,
        host: t.Optional[str] = None,
        # host: the hostname to listen on. Set this to ``'0.0.0.0'`` to have the server available externally as well.
        # 要收听的主机名。将其设置为``‘0.0.0.0’``,以使服务器在外部也可用。
        port: t.Optional[int] = None,
        # the port of the webserver. Defaults to ``5000`` or the port defined in the ``SERVER_NAME`` config variable if present.
        # webserver的端口。默认为``5000``或在``SERVER_NAME``配置变量中定义的端口。
        debug: t.Optional[bool] = None,
        # ebug: if given, enable or disable debug mode. See :attr:`debug`.
        load_dotenv: bool = True,
        # Load the nearest :file:`.env` and :file:`.flaskenv` files to set environment variables. Will also change the working directory to the directory containing the first file found.
        # 加载最接近的:file:`.env`和:file:`.flaskenv`文件,以设置环境变量。还将将该工作目录更改为包含找到的第一个文件的目录。
        **options: t.Any,
    ) -> None:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

07、app.testing底层

Note that if you are testing for assertions or exceptions in your application code, you must set app.testing = True in order for the exceptions to propagate to the test client.

请注意,如果您正在测试应用程序代码中的断言或异常,则必须设置app.testing = True,以便将异常传播到测试客户端。

Otherwise, the exception will be handled by the application (not visible to the test client) and the only indication of an AssertionError or other exception will be a 500 status code response to the test client. See the :attr:testing attribute. For example::

app.testing = True
client = app.test_client()
  • 1
  • 2

The test client can be used in a with block to defer the closing down of the context until the end of the with block. This is useful if you want to access the context locals for testing::

测试客户端可以在带有with中使用,以将上下文的关闭推迟到带有with结束为止。如果您想访问上下文本地语言进行测试,这一点非常有用::

with app.test_client() as c:
	rv = c.get('/?vodka=42')
	assert request.args['vodka'] == '42'
  • 1
  • 2
  • 3

Additionally, you may pass optional keyword arguments that will then be passed to the application’s :attr:test_client_class constructor. For example::

from flask.testing import FlaskClient

class CustomClient(FlaskClient):
    def __init__(self, *args, **kwargs):
        self._authentication = kwargs.pop("authentication")
        super(CustomClient,self).__init__( *args, **kwargs)

app.test_client_class = CustomClient
client = app.test_client(authentication='Basic ....')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

08、env环境变量

os.getenv(key, default=None) 函数介绍:
在这里插入图片描述
如果环境变量的字典集没有存在对应的KEY,会读取默认值:“唤醒手腕”

import os
print(os.getenv("CONF", default="唤醒手腕"))
  • 1
  • 2

09、Flask线程底层

Flask 默认是单进程,单线程阻塞的任务模式,在项目上线的时候可以通过nginx+gunicorn 的方式部署flask任务

但是在开发的过程中如果想通过延迟的方式测试高并发怎么实现呢,其实非常简单:

app.run()中可以接受两个参数,分别是threaded和processes,用于开启线程支持和进程支持。

Flask 单线程阻塞的任务模式 测试:同时打开三个浏览器访问

from datetime import datetime

from flask import Flask

@app.route("/index")
def index():
    print('start : ', datetime.now())
    time.sleep(10)  # 模拟阻塞
    """
    Flask 默认是单进程,单线程阻塞的任务模式
    """
    print('end: : ', datetime.now())
    return "hello world"



if __name__ == "__main__":
    app.run(load_dotenv=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行结果:总共需要的时间是30多秒,说明是单线程阻塞的模式

start :  2022-02-15 14:57:42.714942
end: :  2022-02-15 14:57:52.728294
start :  2022-02-15 14:57:52.730206
127.0.0.1 - - [15/Feb/2022 14:57:52] "GET /index HTTP/1.1" 200 -
end: :  2022-02-15 14:58:02.730972
start :  2022-02-15 14:58:02.733863
127.0.0.1 - - [15/Feb/2022 14:58:02] "GET /index HTTP/1.1" 200 -
end: :  2022-02-15 14:58:12.745196
127.0.0.1 - - [15/Feb/2022 14:58:12] "GET /index HTTP/1.1" 200 -
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

源码写的太明白了,原来是ThreadingMixIn的实例以多线程的方式去处理每一个请求,这样对开发者来说,只有在启动app时将threaded参数设定为True,flask才会真正以多线程的方式去处理每一个请求。

  1. threaded : 多线程支持,默认为False,即不开启多线程;
  2. processes:进程数量,默认为1.

开启方式:

if __name__ == "__main__":
    app.run(load_dotenv=True, threaded=True)
  • 1
  • 2

10、Flask数据库案例

这个案例是进行与数据库的连接,启动Flask服务器应用,构建视图函数映射到响应的地址目录,并且建立pymysql的连接对象,进行数据库的操作,这边就是添加单词的相应的操作。

from datetime import datetime
import threading
import time

from flask import Flask, request, redirect

app = Flask(__name__)

import pymysql

app.config['mysql'] = pymysql.connect(
    host='localhost',  # 连接名称,默认127.0.0.1
    user='pythondb',  # 用户名
    passwd='root',  # 密码
    port=3306,  # 端口,默认为3306
    db='pythondb',  # 数据库名称
    charset='utf8',  # 字符编码
)


@app.route('/')
def Login():
    with open('index.html', 'r', encoding='utf-8') as f:
        html = f.read()
    return html


@app.route('/data')
def ShowData():
    english = request.args['english'].strip().replace("\n", "")
    chinese = request.args['chinese'].strip().replace("\n", "")
    sentence_eng = request.args['sentence_eng'].strip().replace("\n", "")
    sentence_chi = request.args['sentence_chi'].strip().replace("\n", "")
    cursor = app.config['mysql'].cursor()
    sql = "SELECT * FROM word ORDER BY id DESC limit 1"
    cursor.execute(sql)
    id = cursor.fetchone()[0] + 1
    sql = "insert into word values(%s,%s,%s,%s,%s)"
    print(cursor.execute(sql, (id, english, chinese, sentence_eng, sentence_chi)))
    app.config['mysql'].commit()
    return redirect("http://localhost:8080/")


if __name__ == "__main__":
    app.run(host='localhost', port=8080, load_dotenv=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

对应的前端页面代码展示,其实就是简单构造一个表单进行提交的操作:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="http://localhost:8080/data">
        <p><input name="english" placeholder="Word:"></p>
        <p><input name="chinese"  placeholder="Chinese:"></p>
        <p><input name="sentence_eng"  placeholder="sentence_eng:"></p>
        <p><input name="sentence_chi"  placeholder="sentence_chi:"></p>
        <input type="submit" value="Submit">
    </form>
    <style>
        input{ width: 300px; height: 40px }
    </style>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

11、Flask服务器部署

Gunicorn 是一个被广泛使用的高性能的 Python WSGI UNIX HTTP 服务组件 (WSGI: Web Server Gateway Interface),移植自 Ruby 的独角兽(Unicorn )项目,具有使用非常简单、轻量级的资源消耗、高性能等特点。

在这里插入图片描述

在项目中 Gunicorn 和 Flask 框架一同使用, 能够开启服务,处理请求,因其高性能的特点能够有效减少服务丢包率。

使用 pip 安装 gunicorn

pip install gunicorn
  • 1

启动 Flask 项目

(base) [root@ server]# gunicorn -w 1 -b 0.0.0.0:5000 app:app
[2021-03-22 11:43:15 +0800] [6266] [INFO] Starting gunicorn 20.0.4
[2021-03-22 11:43:15 +0800] [6266] [INFO] Listening at: http://0.0.0.0:5000 (6266)
[2021-03-22 11:43:15 +0800] [6266] [INFO] Using worker: sync
[2021-03-22 11:43:15 +0800] [6269] [INFO] Booting worker with pid: 6269
  • 1
  • 2
  • 3
  • 4
  • 5

启动 Flask 项目 + SSL 证书

if __name__ == '__main__':
    context = ('./word.willwaking.com_bundle.crt', './word.willwaking.com.key')  
    app.run(host="0.0.0.0", port=5000, ssl_context=context)
  • 1
  • 2
  • 3
gunicorn --certfile='/home/word_willwaking_com_2024/word.willwaking.com_bundle.crt'  --keyfile='/home/word_willwaking_com_2024/word.willwaking.com.key' -w 4 -b 0.0.0.0:5000 app:app
  • 1

gunicorn 如何实现高并发的?

对于 gunicorn 而言,当启动时就已经把 worker 进程预先 fork 出来了。当多个请求到来的时候,会轮流复用这些 worker 进程,从而能提高服务器的并发负载能力。对于 worker 数的配置,一般推荐2CPU数 + 1。这样一来,在任何时间,都有大概一般的 worker 是在做 I/O,剩下一般才是需要 CPU 的。

如果在开多进程的同时,也开多线程(也就是选择 gthread 类型的 worker),那么配置总的并发数(worker 进程数线程数),仍然建议 2CPU 数 + 1。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/509781
推荐阅读
相关标签
  

闽ICP备14008679号