并发编程(八)在web服务中使用线程池加速

并发编程专栏系列博客

并发编程(一)python并发编程简介

并发编程(二)怎样选择多线程多进程和多协程

并发编程(三)Python编程慢的罪魁祸首。全局解释器锁GIL

并发编程(四)如何使用多线程,使用多线程对爬虫程序进行修改及比较

并发编程(五)python实现生产者消费者模式多线程爬虫

并发编程(六)线程安全问题以及lock解决方案

并发编程(七)好用的线程池ThreadPoolExecutor

并发编程(八)在web服务中使用线程池加速

并发编程(九)使用多进程multiprocessing加速程序运行

并发编程(十)在Flask服务中使用进程池加速

并发编程(十一)python异步IO实现并发编程

并发编程(十二)使用subprocess启动电脑任意程序(听歌、解压缩、自动下载等等)

 
 

web服务的架构以及特点

在这里插入图片描述

从上图可以看出,我们使用服务器访问网页时,浏览器会将我们的请求发送给比如Flask或者Django搭建的web服务器。web服务器会进行磁盘文件读取、数据库操作、调用远程服务API等操作,然后将它们的结果进行组合再操作并返回给我们,这就是web服务的大概介绍。

总结来说,web服务有以下特点:

  • web服务对响应时间要求非常高,比如要求200ms返回。

  • web服务有大量的依赖IO操作的调用,比如磁盘文件、数据库、远程API。

  • web服务经常需要处理几万人、几百万人的同时请求。

 

使用线程池ThreadPoolExecutor加速

使用线程池ThreadPoolExecutor的好处:

  • 方便的将磁盘文件、数据库、远程API的IO调用并发执行。
  • 线程池的数目不会无限创建(导致系统崩溃挂掉),具有防御功能。

 

示例(用Flask实现web服务并实现加速)

首先,我们先新建一个flask接口,模拟服务器执行调用。

# -*- coding: utf-8 -*-
# @Time    : 2021-03-22 13:59:48
# @Author  : wlq
# @FileName: flask_thread_pool.py
# @Email   :rd_wlq@163.com
# 导包
import json
import flask
import time

app = flask.Flask(__name__)

# 磁盘文件读取
def read_file():
    time.sleep(0.1)
    return "file rst"

# 数据库操作
def read_db():
    time.sleep(0.2)
    return "db rst"

# 远程API调用
def read_api():
    time.sleep(0.3)
    return "api rst"


@app.route('/')
def index():
    rst_file = read_file()
    rst_db = read_db()
    rst_api = read_api()

    rst = json.dumps({
        "result_file": rst_file,
        "result_db": rst_db,
        "result_api": rst_api
    })

    return rst


if __name__ == '__main__':
    app.run()

运行该接口文件后,就可以在本地浏览器进行请求
在这里插入图片描述

接下来,我们用python查看一下调用接口所需时间。

在这里插入图片描述

可以看出,网页运行时间是三个操作时间加上框架运行的一点时间。主要还是三个操作耗时。接下来,我们将利用进程池对接口进行修改:

# -*- coding: utf-8 -*-
# @Time    : 2021-03-22 13:59:48
# @Author  : wlq
# @FileName: flask_thread_pool.py
# @Email   :rd_wlq@163.com
import json
import flask
import time
from concurrent.futures import ThreadPoolExecutor

app = flask.Flask(__name__)
# 定义全局变量的线程池
pool = ThreadPoolExecutor()


def read_file():
    time.sleep(0.1)
    return "file rst"


def read_db():
    time.sleep(0.2)
    return "db rst"


def read_api():
    time.sleep(0.3)
    return "api rst"


@app.route('/')
def index():
    rst_file = pool.submit(read_file)
    rst_db = pool.submit(read_db)
    rst_api = pool.submit(read_api)

    rst = json.dumps({
        "result_file": rst_file.result(),
        "result_db": rst_db.result(),
        "result_api": rst_api.result()
    })

    return rst


if __name__ == '__main__':
    app.run()

以上就是修改后的代码,首先我们需要定义一个全局的线程池,便于全局使用。接着,我们使用线程池,就是直接修改调用函数和输出值就可以了。

在这里插入图片描述

上图是修改后的代码运行时间,可以看出时间缩短为305ms。较上次缩短300ms。这是为什么呢?

因为,修改前,执行三个操作我们设定时间分别是100ms、200ms、300ms累加起来再加框架时间就是605ms。现在我们增加线程池后,三个操作同时进行。所以耗时就是最长的那个操作加框架运行时间,就是300ms+5ms等于305ms。可以看出,如果同时有更多的操作,请求时长也是占用时长最长的操作时间,而不是所有操作加起来的时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值