【小沐学Python】Python实现Web服务器(Flask,gevent )

文章目录

  • 1、简介
    • 1.1 功能列表
    • 1.2 支持平台
    • 1.3 安装
  • 2、gevent入门示例
    • 2.1 文件IO
    • 2.2 MySQL
    • 2.3 redis
    • 2.4 time
    • 2.5 requests
    • 2.6 socket
    • 2.7 并发抓取文字
    • 2.8 并发抓取图片
    • 2.9 生产者 - 消费者
  • 3、gevent其他示例
    • 3.1 StreamServer
    • 3.2 WSGI server
    • 3.3 flask
    • 3.4 websocket
    • 3.5 udp
  • 结语

1、简介

官网地址:
https://www.gevent.org/
https://github.com/gevent/gevent

gevent 是一个基于协程的 Python 网络库,它使用 greenlet 在 libev 或 libuv 事件循环之上提供高级同步 API。

在这里插入图片描述

1.1 功能列表

  • 基于 libev 或 libuv 的快速事件循环。
  • 基于绿粒的轻量级执行单元。
  • 重用 Python 标准库中概念的 API(用于 示例有事件和队列)。
  • 支持 SSL 的协作套接字
  • 通过线程池执行的合作 DNS 查询, DNSpython,或C-ARES。
  • 猴子补丁实用程序,让第三方模块变得合作
  • TCP/UDP/HTTP 服务器
  • 子进程支持(通过 gevent.subprocess)
  • 线程池

1.2 支持平台

gevent在Windows,macOS和Linux上进行了测试,应该可以在大多数上运行。 其他类Unix操作系统(如FreeBSD、Solaris等)

1.3 安装

pip install gevent

在这里插入图片描述

  1. ModuleNotFoundError: No module named ‘gevent.wsgi’
    gevent.wsgi模块已被弃用, 并在gevent 1.3发布时被删除 。 它的替代品是gevent.pywsgi模块,它已经存在了一段时间。
from gevent.wsgi import WSGIServer

改为:

from gevent.pywsgi import WSGIServer

2、gevent入门示例

2.1 文件IO


from gevent import monkey
monkey.patch_all()
import gevent
import os
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")

def func(fn):
    logging.info("func: start " + fn)
    with open(fn, "w") as f:
        f.write("*"*100000000)
    with open(fn) as f:
        print(len(f.read()))
    logging.info("func: end " + fn)
    gevent.sleep(0.1)
    
g1 = gevent.spawn(func, "text1")
g2 = gevent.spawn(func, "text2")
g3 = gevent.spawn(func, "text3")
g1.join()
g2.join()
g3.join()

在这里插入图片描述
gevent里文件IO操作是不做切换的。可以试着用gevent.fileobject.FileObjectThread 来包装 open 返回的文件对象;

2.2 MySQL

from gevent import monkey
monkey.patch_all()
import gevent
import os
import MySQLdb
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")

def func(no, name):
    logging.info("func: start " + no)
    conn = MySQLdb.connect(host="localhost",user="root",passwd="root",db="employees")
    cur = conn.cursor()
    cur.execute("insert into departments (dept_no, dept_name) values(%s, %s)", (no, name,))
    conn.commit()
    logging.info("func: end " + no)
    gevent.sleep(1)

g1 = gevent.spawn(func, "a001", "test1")
g2 = gevent.spawn(func, "a002", "test2")
g3 = gevent.spawn(func, "a003", "test3")
gevent.joinall([g1, g2, g3])

在这里插入图片描述
MySQL是阻塞的,因为,MySQL是用C写的,patch的socket补丁,并不生效。

2.3 redis

from gevent import monkey
monkey.patch_all()
import gevent
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")
import redis
r = redis.Redis(host="localhost",port=6379)

def func(key):
    logging.info("func: start " + key)

    v = r.get(key)

    logging.info("func: end " + key)
    gevent.sleep(0.1)

g1 = gevent.spawn(func, "a001")
g2 = gevent.spawn(func, "a002")
g3 = gevent.spawn(func, "a003")
gevent.joinall([g1, g2, g3])

在这里插入图片描述
monkey.patch_all将socket变成非阻塞了,那么进行redis操作请求,也会建立socket连接,自然也是非阻塞的。

2.4 time

from gevent import monkey
monkey.patch_all()
import gevent
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")
import time

def func(key):
    logging.info("func: start " + key)

    time.sleep(3)

    logging.info("func: end " + key)
    gevent.sleep(0.1)

g1 = gevent.spawn(func, "a001")
g2 = gevent.spawn(func, "a002")
g3 = gevent.spawn(func, "a003")
gevent.joinall([g1, g2, g3])

在这里插入图片描述

Monkey.patch_all会将time库也变成非阻塞的,也就是说monkey.patch_all之后,time.sleep等同等于gevent.sleep。

2.5 requests

from gevent import monkey
monkey.patch_all()
import gevent
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")
import requests

def func(url):
    logging.info("func: start " + url)

    requests.get(url, timeout=3)

    logging.info("func: end " + url)
    gevent.sleep(0.1)

g1 = gevent.spawn(func, "http://www.bing.com")
g2 = gevent.spawn(func, "http://www.baidu.com")
g3 = gevent.spawn(func, "http://www.google.com")
gevent.joinall([g1, g2, g3])

在这里插入图片描述

  • or
from gevent import monkey; monkey.patch_all()
import gevent
import requests
 
 
def get_url(url):
    res = requests.get(url)
    print(url, res.status_code, len(res.text))
 
 
url_l = [
    'http://www.baidu.com',
    'http://www.python.org',
    'http://www.cnblogs.com'
]
g_l = []
for i in url_l:
    g_l.append(gevent.spawn(get_url, i))
gevent.joinall(g_l)
  • or

# -*- coding: utf-8 -*-
 
from gevent import monkey;
 
monkey.patch_all()
import gevent
import requests
from datetime import datetime
 
def func(url):
    print(f'time: {datetime.now()}, GET: {url}')
    resp = requests.get(url)
    print(f'time: {datetime.now()}, {len(resp.text)} bytes received from {url}.')
 
 
gevent.joinall([
    gevent.spawn(func, 'https://www.python.org/'),
    gevent.spawn(func, 'https://www.yahoo.com/'),
    gevent.spawn(func, 'https://github.com/'),

2.6 socket

import gevent
from gevent import socket
 
urls = ['www.baidu.com', 'www.example.com', 'www.python.org']
 
jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
 
gevent.joinall(jobs, timeout=2)
 
result = [job.value for job in jobs]
print(result)

在这里插入图片描述
gevent.socket.gethostbyname() 函数与标准的socket.gethotbyname() 有相同的接口,但它不会阻塞整个解释器,因此会使得其他的 greenlets 跟随着无阻的请求而执行。

2.7 并发抓取文字


from gevent import monkey
monkey.patch_all()
 
import requests
import gevent
import io
import sys
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")

# 解决console显示乱码的编码问题
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
 
 
class Douban(object):
    """A class containing interface test method of Douban object"""
 
    def __init__(self):
        self.host = 'movie.douban.com'
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0',
            'Referer': 'https://movie.douban.com/',
        }
 
    def get_response(self, url, data):
        resp = requests.post(url=url, data=data, headers=self.headers).content.decode('utf-8')
        return resp
 
    def test_search_tags_movie(self):
        logging.info("func: start ")
        method = 'search_tags'
        url = 'https://%s/j/%s' % (self.host, method)
        post_data = {
            'type': 'movie',
            'source': 'index'
        }
        resp = self.get_response(url=url, data=post_data)
        logging.info("func: end " + resp)
        return resp
 
 
if __name__ == '__main__':
    douban = Douban()
    jobs = []
    for i in range(6):
        job = gevent.spawn(douban.test_search_tags_movie)
        jobs.append(job)
 
    gevent.joinall(jobs)

在这里插入图片描述
可以。

2.8 并发抓取图片

from gevent import monkey
monkey.patch_all()
import requests
import gevent
from lxml import etree
import  logging
logging.basicConfig(level=logging.DEBUG,format= "%(asctime)s - %(levelname)s - %(message)s")

def downloader(img_name, img_url):
    logging.info("downloader: " + img_name + ", " + img_url)
    req = requests.get(img_url)
    img_content = req.content
    with open(img_name, "wb") as f:
        f.write(img_content)
 
 
def main():
    r = requests.get('https://huaban.com/')
    if r.status_code == 200:
        img_src_xpath = '//img/@src'
        s_html = etree.HTML(text=r.text)
        all_img_src = s_html.xpath(img_src_xpath)
 
        count = 0
        for img_src in all_img_src:
            count += 1
            url = img_src
            gevent.joinall(
                [gevent.spawn(downloader, f"{count}.png", url), ]
            )
 
if __name__ == '__main__':
    main()

在这里插入图片描述
可以。

2.9 生产者 - 消费者


from gevent import monkey
monkey.patch_all()
from gevent.queue import Queue
import gevent
import random

task_queue = Queue(3)
 
def producer(index=1):
    while True:
        print(f'生产者 [{index}]', end='')
        item = random.randint(0, 99)
        task_queue.put(item)
        print(f"生产 ---> {item}")
 
 
def consumer(index=1):
    while True:
        print(f'消费者 [{index}]', end='')
        item = task_queue.get()
        print(f"消费 ---> {item}")
 
 
def main():
    job1 = gevent.spawn(producer)
    job2 = gevent.spawn(consumer)
    job3 = gevent.spawn(consumer, 2)
    thread_list = [job1, job2, job3]
    gevent.joinall(thread_list)
 
 
if __name__ == '__main__':
    main()

在这里插入图片描述

  • or
import gevent
from gevent.queue import Queue
 
tasks = Queue()
 
 
def worker(n):
    while not tasks.empty():
        task = tasks.get()
        print('Worker %s got task %s' % (n, task))
        gevent.sleep(0)
 
    print('Quitting time!')
 
 
def boss():
    for i in range(1, 25):
        tasks.put_nowait(i)
 
 
gevent.spawn(boss).join()
 
gevent.joinall([
    gevent.spawn(worker, 'steve'),
    gevent.spawn(worker, 'john'),
    gevent.spawn(worker, 'nancy'),
])

在这里插入图片描述

3、gevent其他示例

3.1 StreamServer

from gevent.server import StreamServer
 
def handle(socket, address):
    socket.send("Hello from a telnet!\n")
    for i in range(5):
        socket.send(str(i) + '\n')
    socket.close()
 
server = StreamServer(('127.0.0.1', 5000), handle)
server.serve_forever()

3.2 WSGI server

Gevent为HTTP内容服务提供了两种WSGI server。

  • gevent.wsgi.WSGIServer(gevent.wsgi模块已被弃用, 并在gevent 1.3发布时被删除 。)
  • gevent.pywsgi.WSGIServer

3.3 flask

from gevent.pywsgi import WSGIServer
from flask import Flask

app = Flask(__name__)

@app.route('/',methods=['GET'])
def home():
    return 'hello 爱看书的小沐!'
    
if __name__ == "__main__":   
    WSGIServer(('127.0.0.1',5000),app).serve_forever()
  • or
from gevent import monkey
monkey.patch_all() 
from flask import Flask

app = Flask( __name__ )

@app.route( '/')
def hello():
    return 'Hello World, 爱看书的小沐!'

if __name__ == '__main__':
    from gevent import pywsgi
    server = pywsgi.WSGIServer( ('127.0.0.1', 5000 ), app )
    server.serve_forever()
  • or
#!/usr/bin/python
"""WSGI server example"""
from gevent.pywsgi import WSGIServer

def application(env, start_response):
    if env['PATH_INFO'] == '/':
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [b"<b>hello world</b>"]

    start_response('404 Not Found', [('Content-Type', 'text/html')])
    return [b'<h1>Not Found</h1>']


if __name__ == '__main__':
    print('Serving on 8088...')
    WSGIServer(('127.0.0.1', 5000), application).serve_forever()

3.4 websocket

  • ws_server.py
# Simple gevent-websocket server
import json
import random
 
from gevent import pywsgi, sleep
from geventwebsocket.handler import WebSocketHandler
 
 
class WebSocketApp(object):
    '''Send random data to the websocket'''
 
    def __call__(self, environ, start_response):
        ws = environ['wsgi.websocket']
        x = 0
        while True:
            data = json.dumps({'x': x, 'y': random.randint(1, 5)})
            ws.send(data)
            x += 1
            sleep(0.5)
 
 
server = pywsgi.WSGIServer(
    ("127.0.0.1", 9090), WebSocketApp(),
    handler_class=WebSocketHandler
)
server.serve_forever()
  • ws_client.html
<html>
    <head>
        <title>Minimal websocket application</title>
        <script type="text/javascript" src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
        <script type="text/javascript">
        $(function() {
            // Open up a connection to our server
            var ws = new WebSocket("ws://localhost:9090/");
 
            // What do we do when we get a message?
            ws.onmessage = function(evt) {
                $("#placeholder").append('<p>' + evt.data + '</p>')
            }
            // Just update our conn_status field with the connection status
            ws.onopen = function(evt) {
                $('#conn_status').html('<b>Connected</b>');
            }
            ws.onerror = function(evt) {
                $('#conn_status').html('<b>Error</b>');
            }
            ws.onclose = function(evt) {
                $('#conn_status').html('<b>Closed</b>');
            }
        });
    </script>
    </head>
    <body>
        <h1>WebSocket Example</h1>
        <div id="conn_status">Not Connected</div>
        <div id="placeholder" style="width:600px;height:300px;"></div>
    </body>
</html>

3.5 udp

  • udp_server.py:

# Copyright (c) 2012 Denis Bilenko. See LICENSE for details.
"""A simple UDP server.
For every message received, it sends a reply back.
You can use udp_client.py to send a message.
"""
from gevent.server import DatagramServer


class EchoServer(DatagramServer):

    def handle(self, data, address): # pylint:disable=method-hidden
        print('%s: got %r' % (address[0], data))
        self.socket.sendto(('Received %s bytes' % len(data)).encode('utf-8'), address)


if __name__ == '__main__':
    print('Receiving datagrams on :9000')
    EchoServer(':9000').serve_forever()
  • udp_client.py:
"""Send a datagram to localhost:9000 and receive a datagram back.
Usage: python udp_client.py MESSAGE
Make sure you're running a UDP server on port 9001 (see udp_server.py).
There's nothing gevent-specific here.
"""

from __future__ import print_function
import sys
from gevent import socket

address = ('127.0.0.1', 9001)
message = ' '.join(sys.argv[1:])
sock = socket.socket(type=socket.SOCK_DGRAM)
sock.connect(address)
print('Sending %s bytes to %s:%s' % ((len(message), ) + address))
sock.send(message.encode())
data, address = sock.recvfrom(8192)
print('%s:%s: got %r' % (address + (data, )))
sock.close()

结语

如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位大佬童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/31596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

压测工具Jmeter学习

压测工具Jmeter Jmeter介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于Web应用测试&#xff0c;但后来扩展到其他测试领域。 它可以用于测试静态和动态资源&#xff0c;例如静态文件、Java 小服务程序、CGI …

三层交换机与路由互联配置(华为设备)

#三层交换机与路由器配置配置 #三层交换机与路由器配置配置 路由器配置 #进入系统视图 <Huawei>system-view #关闭系统提示信息 [Huawei]undo info-center enable #配置一个环回口 [Huawei]int LoopBack 0 #配置IP地址 与 掩码 [Huawei-LoopBack0]ip address 1.1.…

源码编译LAMP与论坛安装

目录 前言 LAMP工作过程 Apache 1&#xff09;Apache主要特点 如何创建论坛 第一步 关闭防火墙和安全机制 第二、安装相关的Apache服务 1&#xff09;解压压缩包 2&#xff09;安装依赖环境 3&#xff09;配置安装路经等 4&#xff09;编译并安装 5&#xff09;优化…

C++【STL】之反向迭代器

反向迭代器 前面在vector和list的模拟实现中都有讲到正向迭代器&#xff0c;今天我们就来讲解一下反向迭代器的思想和模拟实现&#xff0c;在某些场景下还是很实用的&#xff0c;下面正文直接开始。 文章目录&#xff1a; 反向迭代器1. 反向迭代器结构2. 反向迭代器实现2.1 多…

基于Springboot+vue的汽车租赁系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

5.4.2 网络地址转换NAT

5.4.2 网络地址转换NAT 我们知道为了缓解IPv4地址紧缺的问题&#xff0c;相继出现了一系列缓解地址耗尽的解决方案&#xff0c;比如通过子网划分&#xff08;5.2.8 子网编址&#xff09;实现网络地址在多个物理网络之间的复用&#xff0c;通过无分类编址&#xff08;5.2.9 无分…

Android PagerSnapHelper改造RecyclerView为ViewPage,kotlin

Android PagerSnapHelper改造RecyclerView为ViewPage&#xff0c;kotlin <?xml version"1.0" encoding"utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tool…

端午节粽子(python)

目录 前言 正文 粽叶绘制 粽叶绳绘制 祝福语绘制 源代码 总结 前言 今天端午节&#xff0c;然后昨天也学习一下绘图的相关知识&#xff0c;然后就想看一下能不能画一下&#xff0c;结果还是有点困难的&#xff0c;用CharAI生成简直一言难尽了。后面是找了一个改了一下。 …

【剑指offer刷题记录 java版】数组双指针 之 其它题目

本系列文章记录labuladong的算法小抄中剑指offer题目 【剑指offer刷题记录 java版】数组双指针 之 其它题目 剑指 Offer II 018. 有效的回⽂剑指 Offer 58 - I. 翻转单词顺序剑指 Offer 21. 调整数组顺序使奇数位于偶数前⾯剑指 Offer 57. 和为s的两个数字剑指 Offer II 007. 数…

STM32单片机LED显示屏驱动原理与实现

STM32单片机驱动LED显示屏的原理与实现方法与Arduino类似&#xff0c;但涉及到的具体硬件资源和库函数可能会有所不同。下面是一个详细的介绍&#xff1a; 原理&#xff1a; STM32单片机驱动LED显示屏的原理是通过控制GPIO引脚的电平状态来控制LED的亮灭。通过设置引脚的输出电…

Mybatis源码分析_Mapper接口是如何实例化的 (2)

我们在使用Springmybatis的时候&#xff0c;经常都是直接写一个接口和一个对应的 ***Mapper.xml文件&#xff0c;然后业务代码就可以直接注入这个接口了。它是如何做到的呢&#xff1f; 接口&#xff1a; xml 想搞清楚这个问题&#xff0c;那还是要从Mybatis底层源码进行分析的…

轻量级的深度学习框架Tinygrad

Tinygrad是一个轻量级的深度学习库&#xff0c;它提供了一种简化和直观的方法来理解和实现神经网络。在本文中&#xff0c;我们将探讨Tinygrad及其主要功能&#xff0c;以及它如何成为那些开始深度学习之旅的人的有价值的工具。 什么是Tinygrad? Tinygrad是一个开源的深度学习…

Redis 通用命令

通用命令介绍 Redis 通用命令是一些 Redis 下可以作用在常用数据结构上的常用命令和一些基础的命令&#xff0c;比如删除键、对键进行改名、判断键是否存在等。简单说&#xff0c;就是 keys 分类的命令&#xff0c;如下图。 上图中圈中的部分&#xff0c;就是所谓的通用的命令…

【C语言初阶】带你轻松玩转所有常用操作符(1)

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,这里是君兮_&#xff0c;最近要准备期末复习了&#xff0c;可能更新的就不会那么频繁了&#xff0c;下个星期回复正常更新。 操作符详解1 前言一.操作符的分类二.算数操作符三.移位操作符1.二进制表示的三种形式2.…

matlab实现语音信号的频域分析及应用

1.语音信号本质上是非平稳信号。但我们可以假设语音信号在一个短时间内是平稳的&#xff0c;这样我们用稳态分析方法处理非平稳信号。应用在傅立叶分析就是短时傅立叶变换。 语音的频域分析&#xff1a;包括语音信号的频谱、功率谱、倒频谱、频谱包络等. 常用频域分析方法&am…

mySql和VSC++

确认主机服务里的mysql服务已打开 使用组合键“winR”运行“services.msc”&#xff0c;进入本地服务窗口&#xff1b; 2.进入本地服务窗口后&#xff0c;在右侧服务列表中&#xff0c;查找到“ mysql ”服务选项&#xff1b; 3.查找到mysql服务选项后&#xff0c;双击打开mysq…

linux安装anaconda

linux安装anaconda 1、下载anaconda&#xff1a; Conda 是一个强大的包管理器和环境管理器&#xff0c;您可以在 Windows 的 Anaconda Prompt 或 macOS 或 Linux 的终端窗口中与命令行命令一起使用。 换句话说&#xff0c;我把Conda理解为前端的npm或yarn&#xff0c;后端的…

二进制部署k8集群(上)搭建单机matser和etcd集群

1. 单机matser预部署设计 组件部署&#xff1a; 2.操作系统初始化配置 注意&#xff1a;该操作在所有node节点上进行&#xff0c;为k8s集群提供适合的初始化部署环境 #关闭防火墙 systemctl stop firewalld systemctl disable firewalld iptables -F && iptables -t n…

Java-三种基本控制结构及相关面试题

文章目录 前言一、 顺序控制结构1.1 概念1.2 代码1.3 NS图中体现 二、分支控制结构2.1 概念2.2 if语句2.3 switch语句2.4 NS图中的体现 三、循环控制结构3.1 概念3.2 for循环3.3 while循环3.4 do-while循环3.5 增强 for 循环NS图中的体现 四、相关面试题什么是控制流语句&#…

从零开始:如何用Python建立你的第一个人工智能模型

1. 摘要&#xff1a; 在这篇文章中&#xff0c;我们将介绍如何从零开始使用Python建立你的第一个人工智能模型。无论你是刚接触编程的新手&#xff0c;还是有经验的开发者想进一步探索人工智能领域&#xff0c;这篇文章都将为你提供清晰、详细的指南。我们将一步步探索数据预处…