• Python面试-websocket及web框架


    一、Websocket

        1. websocket概念

        在讲websocket之前,我们先来看看ajax轮询和long poll的实现机制。

        A.  ajax轮询

        ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

        场景再现:

            客户端:啦啦啦,有没有新信息(Request)

            服务端:没有(Response

            客户端:啦啦啦,有没有新信息(Request)

            服务端:没有。。(Response

            客户端:啦啦啦,有没有新信息(Request)

            服务端:你好烦啊,没有啊。。(Response

            客户端:啦啦啦,有没有新消息(Request

            服务端:好啦好啦,有啦给你。(Response

            客户端:啦啦啦,有没有新消息(Request

            服务端:。。。。。没。。。。没。。。没有(Response—- loop

     

       B.  long poll

       long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。

        场景再现:

       客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request

             服务端:额。。 等待到有消息的时候。。来 给你(Response

       客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request-loop

       

        从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。

        何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。简单地说就是,服务器是一个很懒的冰箱(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。

        说完这个,我们再来说一说上面的缺陷,从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。

       ajax轮询 需要服务器有很快的处理速度和资源。(速度)long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)

        所以 ajax轮询  long poll 都有可能发生这种情况。

      客户端:啦啦啦啦,有新信息么?

      服务端:正忙,请稍后再试(503 Server Unavailable

      客户端:。。。。好吧,啦啦啦,有新信息么?

      服务端:正忙,请稍后再试(503 Server Unavailable

       

       c. websocket方式

     通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。一种需要更快的速度,一种需要更多的电话。这两种都会导致电话的需求越来越高。

         哦对了,忘记说了HTTP还是一个无状态协议。通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。

        所以在这种情况下出现了,Websocket出现了。他解决了HTTP的这几个难题。首先,被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。所以上面的情景可以做如下修改。

      客户端:啦啦啦,我要建立Websocket协议,需要的服务:chatWebsocket协议版本:17HTTP Request

      服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched

      客户端:麻烦你有信息的时候推送给我噢。。

      服务端:ok,有的时候会告诉你的。

      服务端:balabalabalabala

      服务端:balabalabalabala

      服务端:哈哈哈哈哈啊哈哈哈哈

      服务端:笑死我了哈哈哈哈哈哈哈

        就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你 

        这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况

         

     

       2. websocket 使用

       服务端:

       客户端:

    二、IO多路复用

        1. 什么是多路复用

        设想一个场景:当我们要编写一个echo服务器程序的时候,需要对用户从标准输入键入的交互命令做出响应。在这种情况下,服务器必须响应两个相互独立的I/O事件:

        1)网络客户端发起网络连接请求;

        2)用户在键盘上键入命令行。我们先等待哪个事件呢?没有哪个选择是理想的。如果在acceptor中等待一个连接请求,我们就不能响应输入的命令。类似地,如果在read中等待一个输入命令,我们就不能响应任何连接请求。针对这种困境的一个解决办法就是I/O多路复用技术

       I/O多路复用,I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接。

       2. select

       I/O多路复用这个概念被提出来以后, select是第一个实现 (1983 左右在BSD里面实现的)

       select 被实现以后,很快就暴露出了很多问题。

         select 会修改传入的参数数组,这个对于一个需要调用很多次的函数,是非常不友好的。

         select 如果任何一个sock(I/O stream)出现了数据,select 仅仅会返回,但是并不会告诉你是那个sock上有数据,于是你只能自己一个一个的找,10几个sock可能还好,要是几万的sock每次都找一遍,这个无谓的开销就颇有海天盛筵的豪气了。

         select 只能监视1024个链接, 这个跟草榴没啥关系哦,linux 定义在头文件中的,参见FD_SETSIZE

         select 不是线程安全的,如果你把一个sock加入到select, 然后突然另外一个线程发现,尼玛,这个sock不用,要收回。对不起,这个select 不支持的,如果你丧心病狂的竟然关掉这个sock, select的标准行为是。。呃。。不可预测的, 这个可是写在文档中的哦.

        “If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
        霸不霸气了,不过然后你就有了多进程的各种问题。

     

        2. poll

        于是14年以后(1997年)一帮人又实现了poll, poll 修复了select的很多问题,比如

        poll 去掉了1024个链接的限制,于是要多少链接呢, 主人你开心就好。

        poll 从设计上来说,不再修改传入数组,不过这个要看你的平台了,所以行走江湖,还是小心为妙。

        其实拖14年那么久也不是效率问题, 而是那个时代的硬件实在太弱,一台服务器处理1千多个链接简直就是神一样的存在了,select很长段时间已经满足需求。

        但是poll仍然不是线程安全的, 这就意味着,不管服务器有多强悍,你也只能在一个线程里面处理一组I/O流。你当然可以那多进程来配合

     3. epoll

     5年以后, 2002, 大神 Davide Libenzi 实现了epoll.

       epoll 可以说是I/O 多路复用最新的一个实现,epoll 修复了poll select绝大部分问题, 比如:

       epoll 现在是线程安全的。

       epoll 现在不仅告诉你sock组里面数据,还会告诉你具体哪个sock有数据,你不用自己去找了。可是epoll 有个致命的缺点。。只有linux支持。比如BSD上面对应的实现是kqueue

        而ngnix 的设计原则里面, 它会使用目标平台上面最高效的I/O多路复用模型咯,所以才会有这个设置。一般情况下,如果可能的话,尽量都用epoll/kqueue吧。

     

    三、web服务器框架

        1. 同步异步、阻塞非阻塞

        同步和异步针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞更关注的是单个进程的执行状态。

        同步:执行一个操作之后,等待结果,然后才继续执行后续的操作。

        异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

        阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

        非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。

      老张爱喝茶,废话不说,煮开水。
      出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
      1). 老张把水壶放到火上,立等水开。(同步阻塞)
        老张觉得自己有点傻
      2). 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
        老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
      3). 老张把响水壶放到火上,立等水开。(异步阻塞)
        老张觉得这样傻等意义不大
      4). 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
        老张觉得自己聪明了。

         所谓同步异步,只是对于水壶而言。
        普通水壶,同步;响水壶,异步。
        虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
        同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

        所谓阻塞非阻塞,仅仅对于老张而言。
        傻等的老张,阻塞;看电视的老张,非阻塞。
       情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

     2. django, flask, tornado

         在Pythonweb开发框架中,目前使用量最高的有DjangoFlaskTornado, 经常会有人拿这几个对比,相信大家的初步印象应该是 Django大而全、Flask小而精、Tornado性能高。
        DjangoPython 中最全能的 web 开发框架,走大而全的方向。它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构、以及全功能的管理后台。不过Django提供的方便,也意味着Django内置的ORM跟框架内的其他模块耦合程度高,深度绑定了该框架,应用程序必须使用Django内置的ORM,否则就不能享受到框架内提供的种种基于其ORM的优秀特性。


        Tornado全称Tornado Web Server,是一个用Python语言写成的Web服务器兼Web应用框架。Tornado走的是少而精的方向,注重的是性能优越,它最出名的是异步非阻塞的服务器方式。(Tornado框架和服务器一起组成一个WSGI的全栈替代品。单独在WSGI容器中使用tornado web框架或者tornaod http服务器,有一定的局限性,为了最大化的利用tornado的性能,推荐同时使用tornaodweb框架和HTTP服务器。)


        Flask是一个使用 Python 编写的轻量级 Web 应用框架,也被称为 “microframework”,语法简单,部署很方便,整个框架自带了路径映射、模板引擎(Jinja2)、简单的数据库访问等web框架组件,支持WSGI协议(采用 Werkzeug)。Flask使用 BSD 授权。 Flask使用简单的核心,用 extension 增加其他功能,虽然没有默认使用的数据库、窗体验证工具,然而Flask保留了扩增的弹性,可以用Flask-extension加入ORM、窗体验证工具、文件上传、各种开放式身份验证技术这些功能。


        从性能上看Tornado DjangoFlask等主流 Web 服务器框架相比有着明显的区别:它是非阻塞式服务器,速度相当快。然而 Tornado 相比 Django Flask属于较为原始的框架,插件少,许多内容需要自己去处理。而Flask插件多,文档非常专业,有专门的公司团队维护,对于快速开发很有效率。由于WSGI协议的存在,可以结合 Tornado 的服务器异步特性、并发处理能力和Flask的文档和扩展能力为一体。虽然像DjangoFlask框架都有自己实现的简单的WSGI服务器,但一般用于服务器调试,生产环境下建议用其他WSGI服务器,比如Nginx+uwsgi+Django方式。

       3. Sanic

       sanic是基于PythonSanic 是一个和类Flask 的基于Python3.5+web框架,它编写的代码速度特别快。

    除了像Flask 以外,Sanic 还支持以异步请求的方式处理请求。这意味着你可以使用新的 async/await 语法,编写非阻塞的快速的代码。

    下图是一张各类web框架性能测试对比(仅供参考,实际情况不同任务及及硬件配置不同)

  • 相关阅读:
    排序算法系列之冒泡排序 (3)
    排序算法系列之选择排序 (2)
    排序算法系列之插入排序 (1)
    深挖 NGUI 基础 之UICamera (二)
    深挖 NGUI 基础 之UIRoot (一)
    JPS寻路和AStar寻路
    旋转矩阵
    [斜边的血条进度]
    UI框架:ui节点控件的绑定方式
    Shader播放序列帧
  • 原文地址:https://www.cnblogs.com/jamon/p/10830297.html
Copyright © 2020-2023  润新知