ملتوية: لماذا يمرر رد اتصال مؤجل إلى موضوع مؤجل يجعل الخيط يحجب فجأة؟

StackOverflow https://stackoverflow.com/questions/2466000

سؤال

لقد حاولت دون جدوى استخدام TXREDIS (واجهة برمجة تطبيقات غير ملتوية من أجل Redis) لقائمة انتظار رسائل مستمرة أحاول إعدادها مع مشروع SCRAPY الذي أعمل عليه. لقد وجدت أنه على الرغم من أن العميل لم يكن يحظر ، إلا أنه أصبح أبطأ بكثير مما كان يمكن أن يكون لأن ما كان ينبغي أن يكون حدثًا واحدًا في حلقة المفاعل تم تقسيمه إلى آلاف الخطوات.

لذا بدلاً من ذلك ، حاولت استخدام redis-py (API الملتوية العادية) ولف المكالمة في موضوع مؤجل. إنه يعمل بشكل رائع ، ومع ذلك أريد أن أقوم بإجراء مؤجل داخلي عندما أقوم بإجراء مكالمة لإعادة Redis حيث أرغب في إعداد تجميع الاتصال في محاولات لتسريع الأمور أكثر.

فيما يلي تفسيري لبعض رمز العينة المأخوذة من مستندات ملتوية لخيط مؤجل لتوضيح حالة الاستخدام الخاصة بي:

#!/usr/bin/env python
from twisted.internet import reactor,threads
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'


def aBlockingRedisCall():
    print 'doing lookup... this may take a while'
    time.sleep(10)
    return 'results from redis'

def result(res):
    print res

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)
    d = threads.deferToThread(aBlockingRedisCall)
    d.addCallback(result)
    reactor.run()

if __name__=='__main__':
    main()

وهنا التغيير الخاص بي لتجميع الاتصال الذي يجعل الرمز في حظر مؤشر الترابط المؤجل:

#!/usr/bin/env python
from twisted.internet import reactor,defer
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'

def aBlockingRedisCall(x):
    if x<5: #all connections are busy, try later
        print '%s is less than 5, get a redis client later' % x
        x+=1
        d = defer.Deferred()
        d.addCallback(aBlockingRedisCall)
        reactor.callLater(1.0,d.callback,x)
        return d

    else: 
        print 'got a redis client; doing lookup.. this may take a while'
        time.sleep(10) # this is now blocking.. any ideas?
        d = defer.Deferred()
        d.addCallback(gotFinalResult)
        d.callback(x)
        return d

def gotFinalResult(x):
    return 'final result is %s' % x

def result(res):
    print res

def aBlockingMethod():
    print 'going to sleep...'
    time.sleep(10)
    print 'woke up'

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)


    d = defer.Deferred()
    d.addCallback(aBlockingRedisCall)
    d.addCallback(result)
    reactor.callInThread(d.callback, 1)
    reactor.run()

if __name__=='__main__':
    main()

لذا فإن سؤالي هو ، هل يعرف أي شخص سبب حظر الخيط المؤجل و/أو هل يمكن لأي شخص اقتراح حل أفضل؟

هل كانت مفيدة؟

المحلول

حسنا ، مثل مستندات ملتوية قل:

المؤجلات لا تجعل الكود غير سحري لا يحظر

كلما كنت تستخدم رمز الحظر ، مثل sleep, ، عليك تأجيله إلى موضوع جديد.

#!/usr/bin/env python
from twisted.internet import reactor,defer, threads
from twisted.internet.task import LoopingCall
import time

def main_loop():
    print 'doing stuff in main loop.. do not block me!'

def aBlockingRedisCall(x):
    if x<5: #all connections are busy, try later
        print '%s is less than 5, get a redis client later' % x
        x+=1
        d = defer.Deferred()
        d.addCallback(aBlockingRedisCall)
        reactor.callLater(1.0,d.callback,x)
        return d

    else: 
        print 'got a redis client; doing lookup.. this may take a while'
        def getstuff( x ):
            time.sleep(3)
            return "stuff is %s" % x

        # getstuff is blocking, so you need to push it to a new thread
        d = threads.deferToThread(getstuff, x)
        d.addCallback(gotFinalResult)
        return d

def gotFinalResult(x):
    return 'final result is %s' % x

def result(res):
    print res

def aBlockingMethod():
    print 'going to sleep...'
    time.sleep(10)
    print 'woke up'

def main():
    lc = LoopingCall(main_loop)
    lc.start(2)


    d = defer.Deferred()
    d.addCallback(aBlockingRedisCall)
    d.addCallback(result)
    reactor.callInThread(d.callback, 1)
    reactor.run()

if __name__=='__main__':
    main()

في حالة عدم معقد API Redis ، فقد يكون من الطبيعي إعادة كتابته باستخدام Twisted.web ، بدلاً من مجرد استدعاء واجهة برمجة تطبيقات الحظر في الكثير من الخيوط.

نصائح أخرى

هناك أيضًا عميل Redis محدث لـ Twisted والذي يدعم بالفعل البروتوكول الجديد وميزات Redis 2.x. يجب أن تجربها بالتأكيد. يطلق عليه Txredisapi.

لقائمة انتظار الرسائل المستمرة ، أوصي RestMQ. نظام قائمة انتظار الرسائل المستند إلى redis مبني على أعلى الإعصار و TxRedisapi.

http://github.com/gleicon/restmq

هتافات

في ملاحظة ذات صلة ، من المحتمل أن تكتسب الكثير باستخدام عميل redis الذي تم إنشاؤه خصيصًا لـ Twisted ، مثل هذا: http://github.com/deldotdr/txredis

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top