• 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()
  • 相关阅读:
    left join,right join,join的区别
    java反编译工具
    maven搭建Struts2+Hibernate+Spring
    JAVA验证是否是Email地址和验证是否是手机号码
    jquery的强大选择器
    JavaScript事件委托的技术原理
    Hybrid APP混合开发的一些经验和总结
    React实例入门教程(1)基础API,JSX语法--hello world
    Vue.js 和 MVVM 的小细节
    前端构建工具gulpjs的使用介绍及技巧
  • 原文地址:https://www.cnblogs.com/jp-mao/p/7056997.html
Copyright © 2020-2023  润新知