用到知识点:logging、asyncio、aiohttp模块(后续补充知识点)
1、logging(日志)模块
功能:
提供了标准的日志接口,记录程序日志,包含正常的程序访问日志、错误、警告等信息输出
可以存为各种格式的日志数据
日志分为:debug()、info()、warning()、error()、critical()5个级别(级别:DEBUG < INFO < WARNING < ERROR < CRITICAL)
import logging print(logging.DEBUG) # 10 print(logging.INFO) # 20 print(logging.WARNING) # 30 print(logging.ERROR) # 40 print(logging.CRITICAL) # 50
默认输出只显示大于等于WARNING的日志
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') """ 输出: WARNING:root:warning message ERROR:root:error message CRITICAL:root:critical message """
通过logging.basicConfig()函数灵活配置日志级别,日志格式,输出位置
在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='test.log', filemode='w') """ level=logging.DEBUG:修改默认输出级别为Debug format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s':指定handler使用的日志显示格式。 %(asctime)s:字符串形式的当前时间 %(filename)s:调用日志输出函数的模块的文件名 [line:%(lineno)d]:调用日志输出函数的语句所在的代码行 %(levelname)s:文本形式的日志级别 %(message)s:用户输出的消息 datefmt='%a, %d %b %Y %H:%M:%S':设置日期格式 %a:星期 %d:日期 %b:月份 %Y:年 %H:%M:%S:时:分:秒 filename='test.log':设置日志输出文件 filemode='w':设置日志输出文件打开方式 """ logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') """ test.log文件内容: Wed, 21 Jun 2017 16:54:30 test.py[line:12] DEBUG debug message Wed, 21 Jun 2017 16:54:30 test.py[line:13] INFO info message Wed, 21 Jun 2017 16:54:30 test.py[line:14] WARNING warning message Wed, 21 Jun 2017 16:54:30 test.py[line:15] ERROR error message Wed, 21 Jun 2017 16:54:30 test.py[line:16] CRITICAL critical message """
2、asyncio(异步IO)模块
用协程实现生产消息者模型
其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()只能传递None进去。因此c.next() 和 c.send(None) 作用是一样的。
第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。
def consumer(): r = '' while True: n = yield r # 跳出生成器,n没有定义 if not n: return print('[CONSUMER] Consuming %s...' %n) r = '200 OK' def produce(c): c.send(None) # 启动生成器,从生成器函数的第一行代码开始执行 n = 0 while n < 5: n = n + 1 print('[PRODUCE] Producing %s....' %n) r = c.send(n) #获取yield返回值 print('[PRODUCE] Consumer return %s....' %r) c.close() # 关闭consumer,整个过程结束 c = consumer() # 创建生成器对象 produce(c) """ 输出: [PRODUCE] Producing 1.... [CONSUMER] Consuming 1... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 2.... [CONSUMER] Consuming 2... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 3.... [CONSUMER] Consuming 3... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 4.... [CONSUMER] Consuming 4... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 5.... [CONSUMER] Consuming 5... [PRODUCE] Consumer return 200 OK.... """
asyncio
的编程模型就是一个消息循环,通过async关键字定义一个协程(coroutine),协程也是一种对象。
协程不能直接运行,需要把协程加入到事件循环(loop),由后者在适当的时候调用协程。
asyncio.get_event_loop
方法可以创建一个事件循环,然后使用run_until_complete
将协程注册到事件循环,并启动事件循环。
import asyncio async def hello(): # async定义一个协程 print('hello world!') # await用于挂起阻塞的异步调用接口。 # 异步调用,把asyncio.sleep(1)看成是一个耗时1秒的IO操作 r = await asyncio.sleep(1) print('hello again!') # 创建一个事件循环 loop = asyncio.get_event_loop() # 将协程注册到事件循环,并启动事件循环 loop.run_until_complete(hello()) # 关闭事件循环 loop.close()
用task封装两个coroutine
import asyncio import threading async def hello(): # async定义一个协程 print('hello world! (%s)' %threading.current_thread()) # await用于挂起阻塞的异步调用接口。 # 异步调用,把asyncio.sleep(1)看成是一个耗时1秒的IO操作 r = await asyncio.sleep(1) print('hello again! (%s)' %threading.current_thread()) # 创建一个事件循环 loop = asyncio.get_event_loop() tasks = [hello(), hello()] # 将协程注册到事件循环,并启动事件循环 loop.run_until_complete(asyncio.wait(tasks)) # 关闭事件循环 loop.close() """ 输出: hello world! (<_MainThread(MainThread, started 13160)>) hello world! (<_MainThread(MainThread, started 13160)>) (间隔约1秒后再输出下面) hello again! (<_MainThread(MainThread, started 13160)>) hello again! (<_MainThread(MainThread, started 13160)>) """
我们用asyncio
的异步网络连接来获取sina、sohu和163的网站首页
import asyncio async def wget(host): # async定义一个协程 print('wget %s...' %host) connect = asyncio.open_connection(host, 80) reader, writer = await connect header = 'GET / HTTP/1.0 Host: %s ' % host writer.write(header.encode('utf-8')) # 刷新底层传输的写缓冲区。也就是把需要发送出去的数据,从缓冲区发送出去 await writer.drain() while True: line = await reader.readline() if line == b' ': break print('%s header > %s' % (host, line.decode('utf-8').rstrip())) writer.close() # 创建一个事件循环 loop = asyncio.get_event_loop() tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']] # 将协程注册到事件循环,并启动事件循环 loop.run_until_complete(asyncio.wait(tasks)) # 关闭事件循环 loop.close() """ 输出: wget www.sohu.com... wget www.sina.com.cn... wget www.163.com... www.163.com header > HTTP/1.0 302 Moved Temporarily www.163.com header > Server: Cdn Cache Server V2.0 www.163.com header > Date: Wed, 21 Jun 2017 11:13:32 GMT www.163.com header > Content-Length: 0 www.163.com header > Location: http://www.163.com/special/0077jt/error_isp.html www.163.com header > Connection: close www.sina.com.cn header > HTTP/1.1 200 OK www.sina.com.cn header > Server: nginx www.sina.com.cn header > Date: Wed, 21 Jun 2017 11:12:45 GMT www.sina.com.cn header > Content-Type: text/html www.sina.com.cn header > Last-Modified: Wed, 21 Jun 2017 11:11:09 GMT www.sina.com.cn header > Vary: Accept-Encoding www.sina.com.cn header > Expires: Wed, 21 Jun 2017 11:13:45 GMT www.sina.com.cn header > Cache-Control: max-age=60 www.sina.com.cn header > X-Powered-By: shci_v1.03 www.sina.com.cn header > Age: 47 www.sina.com.cn header > Content-Length: 597663 www.sina.com.cn header > X-Cache: HIT from cnc.tianjin.sinacache.91.nb.sinaedge.com www.sina.com.cn header > Connection: close www.sohu.com header > HTTP/1.1 200 OK www.sohu.com header > Content-Type: text/html;charset=UTF-8 www.sohu.com header > Connection: close www.sohu.com header > Server: nginx www.sohu.com header > Date: Wed, 21 Jun 2017 11:13:27 GMT www.sohu.com header > Cache-Control: max-age=60 www.sohu.com header > X-From-Sohu: X-SRC-Cached www.sohu.com header > Content-Encoding: gzip www.sohu.com header > FSS-Cache: HIT from 8212655.14766265.9212435 www.sohu.com header > FSS-Proxy: Powered by 2969695.4280425.3969395 """
3、asyncio
可以实现单线程并发IO操作。
asyncio
用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine
实现多用户的高并发支持。
asyncio
实现了TCP、UDP、SSL等协议,aiohttp
则是基于asyncio
实现的HTTP框架。
编写一个HTTP服务器,分别处理以下URL:
-
/
- 首页返回b'<h1>Index</h1>'
; -
/hello/{name}
- 根据URL参数返回文本hello, %s!
import asyncio from aiohttp import web async def index(request): await asyncio.sleep(0.5) return web.Response(body=b'<h1>Index</h1>', content_type='text/html') async def hello(request): await asyncio.sleep(0.5) text = '<h1>hello, %s!</h1>' %request.match_info['name'] return web.Response(body=text.encode('utf-8'), content_type='text/html') async def init(loop): app = web.Application(loop=loop) app.router.add_route('GET', '/', index) app.router.add_route('GET', '/hello/{name}', hello) srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8500) print('Server started at http://127.0.0.1:8500...') return srv loop = asyncio.get_event_loop() loop.run_until_complete(init(loop)) loop.run_forever()
1、输入浏览器:
http://127.0.0.1:8500/hello/char
浏览器输出:
hello, char!
2、输入浏览器:
http://127.0.0.1:8500
浏览器输出:
Index!
""" app.py编写Web App骨架 """ import logging; logging.basicConfig(level=logging.INFO) import asyncio from aiohttp import web async def index(request): """响应函数""" return web.Response(body=b'<h1>Awesome</h1>', content_type='text/html') async def init(loop): """Web App服务器初始化""" # 制作响应合集 app = web.Application(loop=loop) # 把响应函数添加到响应函数集合 app.router.add_route(method='GET', path='/', handler=index) # 创建服务器(连接网址、端口,绑定handler) srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000) logging.info('server start at http://127.0.0.1:9000') return srv # 创建事件 loop = asyncio.get_event_loop() # 运行 loop.run_until_complete(init(loop)) # 服务器不关闭 loop.run_forever()