• 继续谈谈Twisted


    那我就来继续随便谈谈Twisted

    首先讨论一下, 为什么需要twisted, 需要异步

    为了更高效的利用CPU和资源, 提高用户的相应速度

    任务需要较长时间才能完成分成两种情况,

    1) 计算量较大, 需要CPU算好久才能算出来, 自然算出来才能给结果, 称为CPU等待.

    2) 需要等待其他的数据, 比如需要从服务器等待获取信息, 需要从数据库等待查询结果, 这种虽然自己很闲, 无事可做, 但不得不干等, 称为I/O等待.

    CPU等待是没办法的, 就是要算那么长时间, 唯一能做的是为了让用户体验好些, 大家轮流占用CPU, 这种典型的方法就是多线程...

    I/O等待是应该需要优化的, 这是干等白白浪费并占住了资源, 使得其他用户也无法使用. 这儿就需要异步, 需要twisted, 需要callback.

    本来我要等待数据, 然后程序才能继续, 异步的做法是, 把后续的处理程序封装成callback, errback, 把等待数据ready封装成event (让系统调用select去侦听数据I/O). 这样主程序不需要去专门等待某一个event, 有event触发就处理, 这就达到异步的效果.

    所以在判断是否需要使用异步, 该不该使用twisted时, 只需要考虑是否存在I/O等待, 只要有I/O等待就应该考虑使用异步.

    而Twisted的代码无论多么复杂, 其实都是在做如下这样简单的事情,

    通过定义Protocal和Factory来解析请求

    通过定义Callback来处理请求

    定义I/O connection (将Factory作为参数, 包含Protocal和Callback), 并加到event loop(Reactor)中去

    Run Reactor

    再来, Twisted主要应用于什么场景, 使用Twisted真的比直接使用socket方便合理吗

    Twisted主要用于开发server, 它就是为此而生的

    下面我们通过简单的例子来和socket对比一下,

    下面我们先来看一下客户端, 建立一个链接, 发送多条message

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((ip, port))
    
        sock.send('Hello World1')
        time.sleep(3)
        sock.send('Hello World2')
        time.sleep(3)
        sock.send('Hello World3')
        
        sock.close()

    先来看看socket的server, 显然这段代码只能接收到第一条message

    如果需要接受所有的message, 必须在conn.recv前加上while, 这样保证接收到所有message后才去accept新的connection.

    如果同时打开多个client, 那么这儿必须先接收完第一个client的所有message, 才能开始接收第二个client的...

    这样就block了, 用户就不爽了...

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('localhost', 3000))
        sock.listen(5)
        while True:
            conn, addr = sock.accept()
            buf = conn.recv(1024)
            print 'Recv data:' + buf + '\n'

    再来看看用Twisted实现的server, 可以试试同时打开多个上面的client, 该server是可以同时并发接收每个client的数据的, 而不是需要处理完一个client, 才开始处理下一个. 所以确实用Twisted开发server比直接使用socket方便合理许多.

    from twisted.internet import protocol, reactor
    from twisted.internet.protocol import Factory
    
    class Echo(protocol.Protocol):
    
        def connectionMade(self):
            self.factory.numProtocols = self.factory.numProtocols+1 
            print 'connectionMade'
            print 'numProtocols:'+ str(self.factory.numProtocols)
    
        def connectionLost(self, reason):
            self.factory.numProtocols = self.factory.numProtocols-1
            print 'connectionLost'
            print 'numProtocols:'+ str(self.factory.numProtocols)
    
        def dataReceived(self, data):
            #self.transport.write(data)
            print 'dataReceived:' + str(data)
    
    myFactory = Factory()
    myFactory.protocol = Echo
    myFactory.numProtocols = 0
    
    reactor.listenTCP(3000, myFactory)
    reactor.run()

    想想Twisted是怎么样实现这种异步的, 是怎么样保存各个connection并在之间自由切换的?

    从头开始, 大家先简单把I/O想象成文件, 对于操作系统而言, I/O操作就等同于对文件的读写操作, 其他对系统是透明的.

    对于一个Twisted server, 刚开始监听一个I/O端口(这儿可以想象对于Twisted有个侦听队列, 刚开始list中只有一个port), 等待请求...

    请求到达(相当于conn, addr = sock.accept() ), 触发connectionMade Event, 并把该connection加到侦听队列.

    某connection收到数据, 触发dataReceived Event

    某connection closed, 触发connectionLost Event, 并把该connection从侦听队列中删除.

    如果在某个callback, 如dataReceived中出现一个很耗时的任务时该怎么办, 比如需要从另外一个服务器获取数据.

    异步中, callback使用的第一原则是, 不能block, 因为主线程一旦block, 啥事就都做不了

    所以这儿必须要再次使用异步, 在callback中再次设置event和callback, 并将和远程服务器的connection加到侦听队列中.

  • 相关阅读:
    接口
    java基础
    java的反射
    按照字典序打印所有的字符串
    求幂的问题
    时间复杂度与空间复杂度
    孩子们的游戏(圆圈中最后剩下的数)
    约瑟夫环问题
    翻转单词顺序列
    复杂链表的复制
  • 原文地址:https://www.cnblogs.com/fxjwind/p/2246501.html
Copyright © 2020-2023  润新知