2013-02-16 40 views
6

मैं वर्तमान में अपने एंड्रॉइड ऐप्स के लिए एपीआई का एक सेट प्रदान करने के लिए अपने ईसी 2 छोटे इंस्टेंस सर्वर पर Django w/Django-Rest-Framework को तैनात कर रहा हूं।Django Rest Framework/Django प्रदर्शन समस्या

समस्या यह है कि मुझे एक गंभीर प्रदर्शन समस्या का सामना करना पड़ रहा है जिसे मुझे प्रोफाइल करना था। मुझे पता चला कि एक ही अनुरोध के लिए ज्यादातर समय डीआरएफ के कोर के अंदर बिताया जाता है।

इसे एक लंबी पोस्ट बनाने के लिए खेद है, लेकिन मुझे लगता है कि मुझे सब कुछ दिखाना है ताकि मैं क्या हो रहा है के लिए उचित उत्तर प्राप्त कर सकूं। मुझे आगे बढ़ने दें:

मेरा सेटअप nginx/uwsgi है।

description "pycms" 
start on [2345] 
stop on [06] 

respawn 

# start from virtualenv path 
chdir /www/python/apps/pycms/ 

exec uwsgi -b 25000 --chdir=/www/python/apps/pycms --module=wsgi:application --env DJANGO_SETTINGS_MODULE=settings --socket=127.0.0.1:8081 --processes=5 --harakiri=20 --max-requests=5000 --vacuum --master --pidfile=/tmp/pycms-master.pid 

मान लिया जाये कि मैं निम्नलिखित एपीआई का अनुरोध:

http://IP_ADDRESS/api/nodes/mostviewed/9/ 

निम्नलिखित में से कौन नियम से मेल खाता:

url(r'^nodes/mostviewed/(?P<category>\d+)/$', MostViewedNodesList.as_view(), name='mostviewed-nodes-list'), 

यहाँ वर्ग आधारित दृश्य है यहाँ मैं कैसे नवोदय का उपयोग कर uwsgi चला रहा हूँ है:

class MostViewedNodesList(generics.ListAPIView): 
    """ 
    API endpoint that lists featured nodes 
    """ 
    model = ObjectStats 
    serializer_class = NodeSerializer 
    permission_classes = (permissions.AllowAny,) 

    def get_queryset(self): 
     if(self.kwargs.has_key('category')): 
      category_id = self.kwargs.get('category') 
      return ObjectStats.get_most_viewed(category_id) 
     else: 
      return [] 

serializer वर्ग:

class NodeSerializer(serializers.ModelSerializer): 
    images = ImageSerializer() 
    favorite = ObjectField(source='is_favorite') 
    rating = ObjectField(source='get_rating') 
    meta = ObjectField(source='get_meta') 
    url = ObjectField(source='get_absolute_url') 
    channel_title = ObjectField(source='channel_title') 

    class Meta: 
     model = Node 
     fields = ('id', 'title', 'body', 'images', 'parent', 'type', 'rating', 'meta', 'favorite', 'url', 'channel_title') 

और अंत में classmethod 'get_most_viewed' (मैं जानता हूँ कि यह नहीं बल्कि प्रबंधक विधि की तुलना में classmethods उपयोग करने के लिए गलत है)

@classmethod 
    def get_most_viewed(cls, category_id): 
     return list(Node.objects.extra(
      where=['objects.id=content_api_objectviewsstats.node_id', 'content_api_objectviewsstats.term_id=%s'], 
      params=[category_id], 
      tables=['content_api_objectviewsstats'], 
      order_by=['-content_api_objectviewsstats.num_views'] 
     ).prefetch_related('images', 'objectmeta_set').select_related('parent__parent')) 

आप इस सब से देख सकते हैं, यह एक सामान्य बात है अनुरोध को नामित दृश्य पर रीडायरेक्ट किया गया है, MySQL से डेटा ला रहा है और फिर धारावाहिक परिणाम लौटा रहा है। साधारण या किसी भी जटिल प्रसंस्करण से कुछ भी नहीं।

निष्पादित:

ab -c 500 -n 5000 http://IP_HERE/api/nodes/mostviewed/9/ 

सूचना है कि इस कैशिंग के बिना है। निम्नलिखित अक्सर लॉग होता है:

परीक्षण के दौरान
HARAKIRI: --- uWSGI worker 5 (pid: 31015) WAS managing request /api/nodes/mostviewed/9/ since Sat Feb 16 13:07:21 2013 --- 
DAMN ! worker 2 (pid: 31006) died, killed by signal 9 :(trying respawn ... 
Respawned uWSGI worker 2 (new pid: 31040) 

लोड औसत ~ 5. करने के लिए चला जाता है और यहाँ अब परिणाम है:

सभी की
Concurrency Level:  500 
Time taken for tests: 251.810 seconds 
Complete requests:  1969 
Failed requests:  1771 
    (Connect: 0, Receive: 0, Length: 1771, Exceptions: 0) 
Write errors:   0 
Non-2xx responses:  1967 
Total transferred:  702612 bytes 
HTML transferred:  396412 bytes 
Requests per second: 7.82 [#/sec] (mean) 
Time per request:  63943.511 [ms] (mean) 
Time per request:  127.887 [ms] (mean, across all concurrent requests) 
Transfer rate:   2.72 [Kbytes/sec] received 

पहले, प्रति सेकंड 7 अनुरोध बहुत ही निराशाजनक है। टाइमआउट त्रुटियों के कारण ~ 1700 विफल अनुरोध भी प्रदर्शन अंतराल के कारण है जिसका मैं सामना कर रहा हूं।

पूरी तरह ईमानदार होने के लिए। मैं कैशिंग के बिना ~ 60 - 70 प्रति सेकंड अनुरोध की उम्मीद कर रहा हूं। मुझे पता है कि कैशिंग प्रक्रिया को गति देती है लेकिन यह मेरे पास प्रदर्शन के मुद्दों को भी छिपाती है, इसलिए मैं सामान को कैश करने से पहले इसे हल करने का प्रयास कर रहा हूं। ?

मैं जो जोड़कर Django-रूपरेखा का उपयोग कर एक VMware CentOS मशीन पर इस प्रोफ़ाइल में तो निर्णय लिया अनुरोध करने के लिए प्रोफेसर कॉल स्टैक पता चलता है:

Instance wide RAM usage 

Partition of a set of 373497 objects. Total size = 65340232 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 2270 1 7609040 12 7609040 12 dict of django.db.models.sql.query.Query 
    1 19503 5 6263400 10 13872440 21 dict (no owner) 
    2 63952 17 5739672 9 19612112 30 str 
    3 51413 14 5090344 8 24702456 38 list 
    4 58435 16 4991160 8 29693616 45 tuple 
    5 24518 7 4434112 7 34127728 52 unicode 
    6 8517 2 2384760 4 36512488 56 dict of django.db.models.base.ModelState 
    7 2270 1 2378960 4 38891448 60 dict of django.db.models.query.QuerySet 
    8 2268 1 2376864 4 41268312 63 dict of 0x14d6920 
    9 6998 2 2088304 3 43356616 66 django.utils.datastructures.SortedDict 
<619 more rows. Type e.g. '_.more' to view.> 



CPU Time for this request 

     663425 function calls (618735 primitive calls) in 2.037 CPU seconds 

    Ordered by: cumulative time 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 2.037 2.037 /usr/lib/python2.6/site-packages/django/views/generic/base.py:44(view) 
     1 0.000 0.000 2.037 2.037 /usr/lib/python2.6/site-packages/django/views/decorators/csrf.py:76(wrapped_view) 
     1 0.000 0.000 2.037 2.037 /usr/lib/python2.6/site-packages/rest_framework/views.py:359(dispatch) 
     1 0.000 0.000 2.036 2.036 /usr/lib/python2.6/site-packages/rest_framework/generics.py:144(get) 
     1 0.000 0.000 2.036 2.036 /usr/lib/python2.6/site-packages/rest_framework/mixins.py:46(list) 
     1 0.000 0.000 2.029 2.029 apps/content_api/views.py:504(get_queryset) 
     1 0.000 0.000 2.029 2.029 apps/objects_stats/models.py:11(get_most_viewed) 
    23/21 0.000 0.000 2.028 0.097 /usr/lib/python2.6/site-packages/django/db/models/query.py:92(__iter__) 
     4/2 0.003 0.001 2.028 1.014 /usr/lib/python2.6/site-packages/django/db/models/query.py:77(__len__) 
     1 0.000 0.000 1.645 1.645 /usr/lib/python2.6/site-packages/django/db/models/query.py:568(_prefetch_related_objects) 
     1 0.002 0.002 1.645 1.645 /usr/lib/python2.6/site-packages/django/db/models/query.py:1596(prefetch_related_objects) 
     2 0.024 0.012 1.643 0.822 /usr/lib/python2.6/site-packages/django/db/models/query.py:1748(prefetch_one_level) 
    2288 0.007 0.000 1.156 0.001 /usr/lib/python2.6/site-packages/django/db/models/manager.py:115(all) 
    6252 0.019 0.000 0.762 0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:231(iterator) 
    4544 0.025 0.000 0.727 0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:870(_clone) 
    4544 0.109 0.000 0.694 0.000 /usr/lib/python2.6/site-packages/django/db/models/sql/query.py:235(clone) 
    2270 0.004 0.000 0.619 0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:619(filter) 
    2270 0.013 0.000 0.615 0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:633(_filter_or_exclude) 
    1144 0.019 0.000 0.581 0.001 /usr/lib/python2.6/site-packages/django/db/models/fields/related.py:456(get_query_set) 
    1144 0.019 0.000 0.568 0.000 /usr/lib/python2.6/site-packages/django/db/models/fields/related.py:560(get_query_set) 
55917/18180 0.192 0.000 0.500 0.000 /usr/lib64/python2.6/copy.py:144(deepcopy) 
    2270 0.003 0.000 0.401 0.000 /usr/lib/python2.6/site-packages/django/db/models/query.py:820(using) 

और जैसा कि आप देख सकते हैं, समय के सबसे अधिक खर्च किया जाता है django विचारों पर & डीआरएफ विचार।

क्या कोई बता सकता है कि मैं यहां कुछ गलत कर रहा हूं? अनुरोध इतने धीमे क्यों हैं?पाइथन/Django पैमाने क्या है? मैंने पढ़ा है कि यह करता है लेकिन एक साधारण डीबी पर मुझे कितने अनुरोध/सेकंड की उम्मीद करनी चाहिए & प्रतिपादन ऑपरेशन जैसे कि मैं कर रहा हूं?

उत्तर

2

अजगर/Django पैमाने

Django शक्तियां ऐसी Disqus और Instagram के रूप में कुछ काफी बड़े पैमाने पर सेवाओं, करता है, तो हाँ, यह ठीक मापता है।

मैं इस

प्रोफ़ाइल आप देख सकते हैं लगभग पूरे समय विधि .get_most_viewed() के अंदर खर्च किया जाता है तो फैसला किया, ताकि इस मुद्दे को आप मिल गया है की तरह लग रहा है। (वहां गलत हो सकता है, लेकिन आपकी प्रोफ़ाइल से पता चलता है कि उस समय 2.037 में से 2.028 खर्च किया गया है, इसलिए 99.6% समय)

मेरे ओआरएम कौशल वास्तव में यह पता लगाने के लिए वास्तव में नहीं हैं कि आपको कैसे करना चाहिए उस काफी जटिल क्वेरी से निपटने के लिए, और किसी भी मामले में मॉडल परिभाषाओं को देखने की आवश्यकता होगी, लेकिन आपको दृश्य के अन्य हिस्सों को देखने के बजाय उस क्वेरी को डिबगिंग और सरलीकृत करने की आवश्यकता है।

आप प्रबंधन.py खोल का उपयोग कर Django खोल में ड्रॉप करना चाहते हैं, ताकि आप शेष दृश्य को बहिष्कृत करने में उस विशेष क्वेरी को प्रोफ़ाइल कर सकें।

भी हो सकता है कि कैसे आप क्वेरी यहां सरल बना सकते हैं, या Django आईआरसी चैनल या Django मेलिंग सूची पर (आप की संभावना अधिक भाग्य होगा अगर आप से है कि क्वेरी के बारे में विशेष रूप से पूछना, बल्कि पर कुछ मदद की हो रही कोशिश अधिक सामान्य Django/बाकी ढांचा यहाँ सवाल phrased, जिनमें से अधिकांश वास्तव में अपने दर्शन मुद्दे के लिए प्रासंगिक हो प्रतीत नहीं होता है।

आशा इस का समाधान प्राप्त करने के लिए सही दिशा में इंगित मदद करता है।

+0

धन्यवाद पर जारी किया गया था। असल में यह संबंधित है क्योंकि ऐसा प्रतीत होता है कि prefetch_related mysql से लगभग 7 एमबी डेटा लोड कर रहा है। समस्या का वर्णन यहां दिया गया है: https://github.com/tomchristie/django-rest-framework/issues/656 और आप इस सटीक मुद्दे पर जेवियर की टिप्पणी के साथ रेस्टफ्रेमवर्क मेलिंग सूची पर बातचीत भी देख सकते हैं। – Maverick

0

आप प्रारंभिक इवेल्यूएशन (जैसे सूची (क्वेरीसेट)) का उपयोग करें। हो सकता है कि आप कई यादगार श्रमिकों को आपके अनुरोधों को सर्वर करते समय शायद स्मृति से बाहर हो जाएं? तो आपको यह तय करना होगा: तेज़ एर परिणाम या अधिक मेमोरी उपयोग (प्रति अनुरोध 65 एमबी? इस सेकंड में सभी स्मृति) खा सकते हैं)

और प्रोफाइलर है कि आपके कोड QuerySet.all (का उपयोग करता है का कहना है कि जो:

वर्तमान क्वेरीसमूह (या क्वेरीसमूह उपवर्ग) की एक प्रति देता है।

और यह गहरी प्रतिभा को कॉल करता है जिसमें बहुत समय लगता है।

तो ऐसी जगह खोजें जहां सभी() को कॉल किया जाता है और इसका उपयोग नहीं किया जाता है।