python中Sanic框架处理多个请求_tornado

简介:

Tornado是一种 Web 服务器软件的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。

得利于其非阻塞的方式和对epoll的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado 是实时 Web 服务的一个 理想框架。

反向代理服务器后端的Tornado实例:

python中Sanic框架处理多个请求_配置文件_02

python中Sanic框架处理多个请求_Server_03

1.单线程

tornado.web:基础web框架模块
tornado.ioloop:核心IO循环模块,高效的基础。封装了:
1.asyncio 协程,异步处理
2. epoll模型:水平触发(状态改变就询问,select(),poll()), 边缘触发(一直询问,epoll())
3.poll 模型:I/O多路复用技术
4.BSD(UNIX操作系统中的一个分支的总称)的kqueue(kueue是在UNIX上比较高效的IO复用技术)

epoll和kqueue的区别如下:

'epoll'仅适用于文件描述符,在Unix中,一切都是文件”。
这大多是正确的,但并非总是如此。例如,计时器不是文件。
信号不是文件。信号量不是文件。进程不是文件。
(在Linux中)网络设备不是文件。类UNIX操作系统中有许多不是文件的东西
。您不能使用select()/ poll()/ epoll来复制那些“事物”。
Linux支持许多补充系统调用,signalfd(),eventfd()和timerfd_create()
它们将非文件资源转换为文件描述符,以便您可以将它们与epoll()复用。
epoll甚至不支持所有类型的文件描述符;
 select()/ poll()/ epoll不适用于常规(磁盘)文件。
 因为epoll 对准备模型有很强的假设; 监视套接字的准备情况,
 以便套接字上的后续IO调用不会阻塞。但是,磁盘文件不适合此模型,因为它们总是准备就绪。
 
kqueue 中,通用的(struct kevent)系统结构支持各种非文件事件

 

import tornado.web  # tornado.web:基础web框架模块
import tornado.ioloop  # tornado.ioloop:核心IO循环模块,高效的基础。


class IndexHandler(tornado.web.RequestHandler):
    # 类比Django中的视图,相当于业务逻辑处理的类
    def get(self):  # 处理get请求的,并不能处理post请求
        self.write("good boy")  # 响应http的请求


if __name__ == "__main__":
    """
    实例化一个app对象
    Application:他是tornado.web框架的核心应用类,是与服务器对应的一个接口
    里面保存了路由映射表,还有一个listen方法,可以认为用来创建一个http的实例
    并绑定了端口
    """

    app = tornado.web.Application([
        (r"/", IndexHandler),
    ])

    app.listen(8888)  # 绑定监听端口,但此时的服务器并没有开启监听
    tornado.ioloop.IOLoop.current().start()

# Application([(r"/" ,IndexHandler),])传入的第一个参数是
# 路由路由映射列表,但是在此同时Application还能定义更多参数

# IOLoop.current():返回当前线程的IOLoop实例
# 例如实例化对象 ss=run() ,ss.start()
# start():启动了IOloop实例的循环,连接客服端后,同时开启了监听



原理如下:

1.linux-epoll进行管理多个客服端socket
2.tornado-IoLoop不断进行询问epoll:小老弟,有没有我需要做的事吗?    
3. 当epoll返回说:老哥,有活了
4.tornado-IoLoop将活上传给tornado.web.Application
5. tornado.web.Application中有 路由映射表,就会将socket进行一个路由匹配,,把socket匹配给相对应的Handler(处理者)
6. Handler(处理者)处理后将数据返回给对应的socket,(这里因为可能会延时响应,所以这里进行异步处理)
7. socket然后再传给了客服端浏览器

 



python中Sanic框架处理多个请求_tornado_04


图片将就看啊,手残党。。

2.多线程

新加模块如下:

tornado.httpserver  	#tornado的HTTP服务器实现


注意:
在windows中运行多进程会报错:
AttributeError: module ‘os’ has no attribute 'fork’
这是因为fork这个系统命令,只在linux中才有用,
如果你是在windows, win7、winxp中运行的话就会出错。
所以把上面的python文件,拷贝到linux的系统中再运行就没问题了.
这个错误我暂时没有解决,后续补上。

import tornado.web
import tornado.ioloop
import tornado.httpserver  	#tornado的HTTP服务器实现


class IndexHandler(tornado.web.RequestHandler):
    def get(self):      #处理get请求的,并不能处理post请求
        self.write("good boy")      #响应http的请求

if __name__=="__main__":

    app = tornado.web.Application([
            (r"/" ,IndexHandler),
        ])
    httpServer = tornado.httpserver.HTTPServer(app)

    #httpServer.listen(8000)  #多进程改动原来的这一句

    httpServer.bind(8888)      #绑定在指定端口
    httpServer.start(2)
        #默认(0)开启一个进程,否则对面开启数值(大于零)进程
  #值为None,或者小于0,则开启对应硬件机器的cpu核心数个子进程
    #例如 四核八核,就四个进程或者八个进程
    tornado.ioloop.IOLoop.current().start()


#app.listen(),只能在单进程中使用

#多进程中,虽然torna给我们提供了一次性启动多个进程的方式,但是由于一些问题,不建议使用上面打的方式启动多进程,而是手动启动多个进程,并且我们还能绑定不同的端口

问题如下:

#1.每个子进程都会从父进程中复制一份IOLoop的实例,
如果在创建子进程前修改了IOLoop,会影响所有的子进程

#2.所有的进程都是由一个命令启动的,无法做到不停止服务的情况下修改代码。
修改单个进程是需要关掉所有进程,影响客服服务使用。

#3.所有进程共享一个端口,向要分别监控很困难。

 

所以:我们手动启动多个进程,并且我们还能绑定不同的端口,进行单个进程操作。

3.options模块

新运用模块如下:

tornado.options模块:提供了全局参数的定义,储存和转换

 

属性和方法
tornado.options.define()
原型tornado.options.define(name, default=None, type=None, help=None, metavar=None,multiple=False, group=None, callback=None)

常用参数:

属性:

# name(选项变量名),必须保证其唯一性,
否则报错"options 'xxx' already define in '....'"

#default(设置选项变量的默认值),默认为None

#type(设置选项变量的类型),str,int,float,datetime(时间日期),等,
从命令行或配置文件导入参数时,会根据类型转换输入的值。
如果不能转化则报错,如果没有设置type的值,
会根据default的值的类型进行转换,比如default的值 8000,
而传给type的为‘8080’字符串,
则会转换成int类型。如果default也没有设置,则不转化

#multipe(设置选项变量是否为多个值),默认False 比如传递为列表, 列表包括了多个值,就要改为Ture。

#help

 

方法:

#tornado.options.options #全局的options对象,所有定义的选项变量都会作为该对象的属性

#获取参数的方法(命令行传入)#1. tornado.options.parse_command_line() #作用:转化命令行参数,接收转换并保存

#获取参数的方法(从配置文件导入参数)#tornado.options.parse_config_file(path=)

 

1获取参数的方法(命令行传入)

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options  #提供了全局参数的定义,储存和转换
from tornado.options import options #全局的options对象,所有定义的选项变量都会作为该对象的属性

#定义两个参数
tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str,multiple=True) #multiple=True接收多个值

class IndexHandler(tornado.web.RequestHandler):
    def get(self):      #处理get请求的,并不能处理post请求
        self.write("good boy")      #响应http的请求

if __name__=="__main__":
    tornado.options.parse_command_line()
            #转化命令行参数,接收转换并保存在tornado.options.options里面
    print('list',options.list)
    app = tornado.web.Application([
            (r"/" ,IndexHandler),
        ])
    httpServer = tornado.httpserver.HTTPServer(app)

    #httpServer.listen(8000)  #多进程改动原来的这一句

    httpServer.bind(options.port)      #绑定在指定端口
                        #全局的options对象,所有定义的选项变量都会作为该对象的属性
                        #这里就可以使用tornado.options.parse_command_line()保存的变量的值
    httpServer.start(1)
        #默认(0)开启一个进程,否则对面开启数值(大于零)进程
     #值为None,或者小于0,则开启对应硬件机器的cpu核心数个子进程
    #例如 四核八核,就四个进程或者八个进程
    tornado.ioloop.IOLoop.current().start()

#注意这里不能直接启动,应为没有进行传值,那要在怎么启动,用命令行cmd进行
#cmd进行当前py文件目录 python server.py  --port=9000  --list=good,nice,cool

#启动
#> python server.py  --port=9000  --list=good,nice,cool
#启动的时候就指定了端口号,就会启动指定端口号的服务器,也就是说我们不用每次用都去修改我们的代码的端口
#(输出)list ['good', 'nice', 'cool']

2.获取参数的方法(从配置文件导入参数) 将上面的代码中的tornado.options.parse_command_line()进行更改为 tornado.options.parse_config_file(“config”)

#"config"为配置文件,这里因为是在同一级目录下,所以直接写文件名,如果不在则需要进行书写路径。 #需要创建名为config的普通文件,作为配置文件 在里面进行书写配置。 config配置文件 例如:

port=7000
list=["job","good","nice"]

 

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options  #提供了全局参数的定义,储存和转换
from tornado.options import options
#全局的options对象,所有定义的选项变量都会作为该对象的属性

#定义两个参数
# config文件要定义,这里也要定义
tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str,multiple=True)
#multiple=True接收多个值

class IndexHandler(tornado.web.RequestHandler):
    def get(self):      #处理get请求的,并不能处理post请求
        self.write("good boy")      #响应http的请求

if __name__=="__main__":
    tornado.options.parse_config_file("config")
            #需要创建名为config的普通文件
    print('list',options.list)
    app = tornado.web.Application([
            (r"/" ,IndexHandler),
        ])
    httpServer = tornado.httpserver.HTTPServer(app)

    #httpServer.listen(8000)  #多进程改动原来的这一句

    httpServer.bind(options.port)      #绑定在指定端口
    #全局的options对象,所有定义的选项变量都会作为该对象的属性
    #这里就可以使用tornado.options.parse_command_line()保存的变量的值
    httpServer.start(1)
        #默认(0)开启一个进程,否则对面开启数值(大于零)进程
     #值为None,或者小于0,则开启对应硬件机器的cpu核心数个子进程
    #例如 四核八核,就四个进程或者八个进程
    tornado.ioloop.IOLoop.current().start()

 

说明:这两种方法以后都不常用,常用的是文件配置,但是是.py文件进行配置。 因为书写格式人需要按照Python的语法要求,并且普通的文件不支持字典类型

 

4.最终的py配置文件

创建config.py文件编写配置文件 #定义字典 #新建文件config.py文件,配置模型: options = { ‘port’:8080, ‘list’:[‘good’,‘nicd’,‘job’] }

import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
import tornado1.config     #直接导入配置的py文件


class IndexHandler(tornado.web.RequestHandler):
    def get(self,*args,**kwargs):      #处理get请求的,并不能处理post请求
        self.write("good boy")      #响应http的请求

if __name__=="__main__":
    print('list=',tornado1.config.options["list"])
    app = tornado.web.Application([
            (r"/" ,IndexHandler),
        ])
    httpServer = tornado.httpserver.HTTPServer(app)

    httpServer.bind(tornado1.config.options["port"])      #绑定在指定端口
                        #全局的options对象,所有定义的选项变量都会作为该对象的属性
                        #这里就可以使用tornado.options.parse_command_line()保存的变量的值
    httpServer.start(1)
                                #默认(0)开启一个进程,否则对面开启数值(大于零)进程
                             #值为None,或者小于0,则开启对应硬件机器的cpu核心数个子进程
                            #例如 四核八核,就四个进程或者八个进程
    tornado.ioloop.IOLoop.current().start()

##其他的获取参数方式

http://127.0.0.1:9000/error?flag=0 向error页面传递flag=0这个值,其中?表示传值 页面中接收flag

def get(self, *args, **kwargs):
        flag = self.get_query_argument("flag")

 

get方法: get_query_argument()/get_query_arguments()

post方法: get_body_argument()/get_body_arguments() 上述四个获取值方法统一参数:(name, default=_ARG_DEFAULT, strip=True)

# name:获取name属性的值

default:设置默认值,如果没有参数传递过来,那么就是用默认值

strip: 去除左右空格

  # 使用get方式传递参数     def get(self):         # 获取单个键值         get_a = self.get_query_argument('a')  # 如果同时传递多个a的参数,那么会采用后面覆盖前面的原则         self.write(get_a)         # 其中获取多个使用:get_query_arguments()

# 使用post传递参数     def post(self):         # 接收单个参数         post_a = self.get_body_argument('a')         self.write(post_a)         # 其中获取多个参数传递使用get_body_arguments()  

使用get_argument()/get_arguments()可以不区分post/get传递方式

def post(self):         # 接收单个参数         post_a = self.get_argument('a')         self.write(post_a)         # 其中获取多个参数传递使用get_arguments()  

值得注意的是:使用上述方法获取参数只能够获取headers报头为"application/x-www-form-urlencoded"或者"multipart/form-data"的数据,如果想获取json或者xml方式传递的数据需要使用self.request方式获取

def post(self):         self.write(self.request.headers.get('Content-Type'))  # 获取“application/json”类型传递         # 如果想获取内容,可以使用self.request.body

 

 

5.关闭日志logging=none

当我们使用命令行传递参数parse_command_line()或者配置文件parse_config_file(path)传递参数是,tornado会默认开启日志,向终端打印信息。 比如在传递参数parse_command_line()类型中。 python server.py --port=9000 --list=good,job,nice 启动py文件后 在进行网页登录本地地址(ipconfig可查看)192.168.22.33:7000进入后 cmd终端就会打印[I 181215 19:13:39 web:2162] 304 GET / (192.168.22.33) 1.00ms #I为信息(information) 或者 [W 181215 19:18:10 web:2162] 404 GET /index (192.168.43.145) 0.50ms #W为错误(wrong)

如果不想让终端打印 在tornado.options.parse_command_line()之前加上tornado.options.options.logging=None 或者在cmd中python server.py --port=9000 --list=good,job,nice --logging=none