python Web框架tonado

tonado Tornado全称Tornado Web Server,是一个用Python语言写成的Web服务器兼Web应用框架,由FriendFeed公司在自己的网站FriendFeed中使用,被Facebook收购以后框架在2009年9月以开源软件形式开放给大众。
Tornado与其他Web框架的区别以Django为代表的python web应用部署时采用wsgi协议与服务器对接(被服务器托管),而这类服务器通常都是基于多线程的,也就是说每一个网络请求服务器都会有一个对应的线程来用web应用(如Django)进行处理。
考虑两类应用场景
用户量大,高并发如秒杀抢购、双十一某宝购物、春节抢火车票
大量的HTTP持久连接
使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。
对于HTTP 1.0,可以在请求的包头(Header)中添加Connection: Keep-Alive。
对于HTTP 1.1,所有的连接默认都是持久连接。
对于这两种场景,通常基于多线程的服务器很难应对。
对于前面提出的这种高并发问题,我们通常用C10K这一概念来描述。C10K—— Concurrently handling ten thousandconnections,即并发10000个连接。对于单台服务器而言,根本无法承担,而采用多台服务器分布式又意味着高昂的成本。如何解决C10K问题?
Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的解决方案(服务器与框架的集合体)。
什么是 Python Tornado?
Python Tornado 是一个基于非阻塞 I/O 的 web 服务器框架,它使用了事件循环来处理并发请求。Tornado 适用于高并发的场景,如实时聊天、推送服务等。
安装
首先,确保已经安装了 Python 和 pip。然后,使用以下命令安装 Tornado:

pip3 install tornado

下面是一个简单的 Tornado Hello World 示例:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, World!")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

在命令行中运行该脚本,然后在浏览器中访问 http://localhost:8888,将会看到 “Hello, World!” 的输出。

路由
Tornado 使用正则表达式来定义路由。在上面的示例中,(r"/", MainHandler) 定义了根路由,将请求映射到 MainHandler 类。

可以通过正则表达式捕获 URL 中的参数,并将其传递给处理函数。例如,(r"/user/(\d+)", UserHandler) 将匹配 /user/123,并将 123 作为参数传递给 UserHandler 类。

异步处理
Tornado 支持异步处理,以提高性能。在请求处理函数中,可以使用 tornado.gen 模块的装饰器来标记异步函数。以下是一个示例:

import tornado.ioloop
import tornado.web
import tornado.gen

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        result = yield self.async_function()
        self.write(result)

    @tornado.gen.coroutine
    def async_function(self):
        # 模拟一个异步操作
        yield tornado.gen.sleep(1)
        raise tornado.gen.Return("Async Function Result")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

在上面的示例中,get 方法使用了 tornado.gen.coroutine 装饰器来标记为异步函数。在异步函数中,可以使用 yield 关键字来暂停函数的执行,等待异步操作完成。
tornado中为什么不能写同步方法
tornado程序是基于单线程的协程模式,所以如果写同步方法会导致同时请求多个url时出现阻塞现象。

搭建项目:后台管理系统Tornado+Layui框架搭建教程

集成模块
用户管理:用于维护管理系统的用户,常规信息的维护与账号设置。
角色管理:角色菜单管理与权限分配、设置角色所拥有的菜单权限。
菜单管理:配置系统菜单,操作权限,按钮权限标识等。
职级管理:主要管理用户的职级。
岗位管理:主要管理用户担任职务。
部门管理:配置系统组织机构,树结构展现支持数据权限。
字典管理:对系统中常用的较为固定的数据进行统一维护。
配置管理:对系统的配置信息管理维护。
通知公告:系统通知公告信息发布维护。
软件信息
软件名称:DjangoAdmin敏捷开发框架Tornado+Layui版本
官网网址:https://www.djangoadmin.cn
文档网址:http://docs.tornado.layui.djangoadmin.cn
演示地址:http://manage.tornado.layui.djangoadmin.cn

核心组件
单图上传组件

{{ "avatar|头像|90x90|建议上传尺寸450x450|450x450"|image(data.avatar, "jpg|png|gif", 0) }}

多图上传组件

{{ "imgs|图集|90x90|20|建议上传尺寸450x450"|album(data.imgsList, "jpg|png|gif", 10) }}

下拉选择组件

{{ "gender|1|性别|name|id"|select("1=男,2=女,3=保密", data.gender) }}

单选按钮组件

{{ "gender|name|id"|radio("1=男,2=女,3=保密", 1) }}

复选框组件

{{ "gender|name|id"|checkbox("1=男,2=女,3=保密", 1) }}

城市选择组件

{{ data.district_code|default("")|city(3, 1) }}

开关组件

{{ "status"|switch("在用|禁用", data.status|default(1)) }}

日期组件

{{ "birthday|1|出生日期|date"|date(data.birthday) }}

图标组件

{{ "icon"|icon(data.icon|default("layui-icon-component")) }}

穿梭组件

{% transfer "func|0|全部节点,已赋予节点|name|id|220x350" "1=列表,5=添加,10=修改,15=删除,20=详情,25=状态,30=批量删除,35=添加子级,40=全部展开,45=全部折叠,50=导出数据,55=导入数据,60=分配权限,65=重置密码" funcList %}

模板布局
Layout布局

<!DOCTYPE html>
<html>
<!-- 头部开始 -->
{% include "public/header.html" %}
<!-- 头部结束 -->
<body>

<!-- 主体部分开始 -->
<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-body">
            <!-- 内容区 -->
            {% block content %}
            {% endblock %}
        </div>
    </div>
</div>
<!-- 主体部分结束 -->

<!-- 脚部开始 -->
{% include "public/footer.html" %}
<!-- 脚部结束 -->
</body>
</html>

头部模板

<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>Tornado+Layui【旗舰版】敏捷开发框架</title>
    <link href="/static/assets/images/favicon.ico" rel="icon">
    <link type="text/css" rel="stylesheet" href="/static/assets/libs/layui/css/layui.css"/>
    <link type="text/css" rel="stylesheet" href="/static/assets/module/admin.css"/>
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js' %}"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js' %}"></script>
    <![endif]-->
    <script type="text/javascript" src="/static/assets/libs/layui/layui.js"></script>
    <script type="text/javascript" src="/static/assets/js/common.js"></script>
    <script type="text/javascript">
        var url = window.location.pathname.substring(1);
        var item = url.split("/");
        var C = item[0];
        var A = item[1];
        var cUrl = "/" + C;
    </script>
</head>

脚部模板

<!-- JS部分 -->
<script type="text/javascript">
    var url = window.location.pathname.substring(1);
    var item = url.split("/");
    var jsUrl = "/static/module/djangoadmin_" + item[0] + ".js";
    document.write("<script src='" + jsUrl + "'><\/script>");
</script>

搭建http服务
完整代码:
服务端:

import traceback
from io import BytesIO

import tornado
import tornado.ioloop
import tornado.web
from tornado.escape import json_encode
from PIL import Image
import pickle
from base64 import b64encode
import importlib,sys
importlib.reload(sys)

class Handler(tornado.web.RequestHandler):

    def post(self):
        result = {}
        content = self.request.arguments["content"][0]
        if content is None:
            result["msg"] = "no content"
        else:
            byte_array, msg = self.content_process(content)
            result["image"] = byte_array
            result["msg"] = msg
        self.write(json_encode(result))

    def content_process(self, image_content):
        try:
            content = pickle.loads(image_content)
            image = Image.open(BytesIO(content))
            image.save("./server.png")
            image = image.rotate(90)
            
            byte_array = BytesIO()
            image.save(byte_array, format='PNG')
            byte_array = byte_array.getvalue()
            byte_array = b64encode(byte_array).decode('utf-8')
            return byte_array, "sucess"
        
        except Exception as e:
            print(traceback.format_exc())
            return str(e), "wrong"

class ImageServer(object):

    def __init__(self, port, server_address):
        self.port = port
        self.address = server_address

    def process(self):
        app = tornado.web.Application([(r"/image_server?", Handler)], )
        app.listen(self.port, address=self.address)
        tornado.ioloop.IOLoop.current().start()

if __name__ == "__main__":
    server_port = "8080"
    server_address = "192.168.10.1"
    server = ImageServer(server_port, server_address)
    print("begin server")
    server.process()

客户端

import json
import urllib
import requests
import pickle
import urllib.request
from PIL import Image
from io import BytesIO
from base64 import b64decode

def post(server_url, params):
    data = urllib.parse.urlencode(params).encode('utf-8')
    request = urllib.request.Request(server_url, data)
    return json.loads(urllib.request.urlopen(request, timeout=100).read())

def local_image(server_url, image_path):
    r_file = open(image_path, "rb")
    content = pickle.dumps(r_file.read())
    params = {"content": content}

    json_return = post(server_url, params)
    msg = json_return["msg"]

    image_bytes = b64decode(json_return["image"])
    image = Image.open(BytesIO(image_bytes))
    image.save("./user.png")

if __name__ == "__main__":
    # url = "http://localhost:8080/image_server?"
    url = "http://192.168.10.1:8080/image_server?"

    file_path = "/home/yangwn/code/server_test/46.jpg"
    local_image(url, file_path)

代码使用
代码只要修改了图片path和url就能直接用
客户端
在主函数main中的url要适配你服务端的url,如果不知道输入什么地址,就用注释的那一行,也就是localhost,注意端口号是必要的
在进入local_image函数后,将图片数据转成字节流,并打包成dict字典的形式传到post函数
在post函数里面继续把字典转成字节流,随后就上传到服务端,并等待服务端返回
接着将返回值接收,我这里返回的是一张图片和一个状态(msg),把返回的图片字节流进行转换后用BytesIO打开即可
服务端
如果前面的客户端你想要用localhost,那就把address设置为None
图片数据的传输方式都一样
就是在post中写数据的时候还用了json_encode,所以把图片用代码先转成字符了

byte_array = BytesIO()
image.save(byte_array, format='PNG')
byte_array = byte_array1.getvalue()
byte_array = b64encode(byte_array).decode('utf-8')

改进
在客户端读图的时候

r_file = open(image_path, "rb")
content = pickle.dumps(r_file.read())
params = {"content": content}

用了pickle,发现多此一举,可以改为

with open(image_path, "rb") as r_file:
	content = b64encode(r_file.read())
params = {"content": content}

这时候服务端也要适配,将

content = pickle.loads(image_content)

改为

content = b64decode(image_content)

加入神经网络
当需要输入初始化参数的时候,比如输入一个网络,但是不需要每次post都初始化网络权重,于是可以传入参数。
先修改server,先初始化网络,然后再把网络传入到handle里面

class ImageServer(object):

	def __init__(self, port, server_address):
		...
		self.model = Model()
	def process(self):
		app = tornado.web.Application([(r"/image_server?", Handler, dict(model=self.model))], )

接着再Handler里重写initialize函数,接收model

class Handler(tornado.web.RequestHandler):
	def initialize(self, model):
		self.model = model
	def conten_process(self):
		...
		predict = self.model(image)
		...
		return ...
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值