zoukankan      html  css  js  c++  java
  • Python_Web_App_Day_1:编写Web App骨架

    用到知识点: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()
  • 相关阅读:
    USACO 3.3 A Game
    USACO 3.3 Camelot
    USACO 3.3 Shopping Offers
    USACO 3.3 TEXT Eulerian Tour中的Cows on Parade一点理解
    USACO 3.3 Riding the Fences
    USACO 3.2 Magic Squares
    USACO 3.2 Stringsobits
    USACO 3.2 Factorials
    USACO 3.2 Contact
    USACO 3.1 Humble Numbers
  • 原文地址:https://www.cnblogs.com/jp-mao/p/7056997.html
Copyright © 2011-2022 走看看