6

私は現在、EC2 スモール インスタンス サーバーに Django-Rest-Framework を使用して Django をデプロイし、いくつかの Android アプリに一連の API を提供しています。

問題は、プロファイルしなければならない深刻なパフォーマンスの問題に直面していることです。1 つのリクエストのほとんどの時間が DRF のコア内で費やされていることがわかりました。

これを長い投稿にして申し訳ありませんが、何が起こっているのかについて適切な答えを得ることができるように、すべてを表示する必要があると思います. 先に進みましょう:

私のセットアップはnginx/uwsgiです。upstartを使用して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 

次の API をリクエストするとします。

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

次のルールに一致します。

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

クラスベースのビューは次のとおりです。

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 []

シリアライザ クラス:

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')

そして最後にクラスメソッド「get_most_viewed」(マネージャーメソッドではなくクラスメソッドを使用するのが間違っていることは知っています)

@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 です。そして、ここに ab の結果があります:

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-profiling を使用して vmware CentOS マシンでこれをプロファイリングすることにしました。リクエストに ?prof を追加すると、コール スタックが表示されます。

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 ビューと DRF ビューに費やされています。

私がここで何か間違ったことをしている場合、誰かが指摘できますか? リクエストが遅いのはなぜですか?python / Djangoはスケーリングしますか? 私はそれを読みましたが、私が行っているような単純なDB読み取りおよびレンダリング操作で、1秒あたり何回のリクエストを期待する必要がありますか?

4

3 に答える 3

2

python / djangoはスケーリングしますか

Django は、Disqus や Instagram などのかなり大規模なサービスを支えているため、問題なくスケーリングできます。

次に、これをプロファイルすることにしました

ご覧のとおり、ほぼすべての時間が .get_most_viewed() メソッド内で費やされているため、問題が発生しているように見えます。(そこは間違っているかもしれませんが、あなたのプロフィールによると、2.037 時間のうち 2.028 がそこで費やされているため、時間の約 99.6% が費やされています)

私の ORM スキルでは、そのかなり複雑なクエリをどのように処理する必要があるかを正確に把握することはできません。いずれにせよ、モデル定義を確認する必要がありますが、そのクエリをデバッグして単純化することを検討する必要があります。ビューの他の部分を見ています。

manage.py シェルを使用して Django シェルにドロップすると、その特定のクエリを除外してビューの残りの部分を除外できます。

また、ここで、または Django IRC チャネルまたは django メーリング リストで、クエリを簡素化する方法についてのヘルプを取得してみてください (より一般的な Django/REST フレームワークではなく、そのクエリについて具体的に尋ねた方がうまくいく可能性があります)。ここで質問を言いますが、そのほとんどは実際にはあなたが見ている問題に関連していないようです.

これを解決するための正しい方向を示すのに役立つことを願っています.

于 2013-02-16T18:26:48.883 に答える
0

会話は DRF のメーリング リスト https://groups.google.com/forum/?fromgroups=#!topic/django-rest-framework/AkLKdfxCrcUで続けられました。

于 2013-02-17T15:17:01.537 に答える
0

初期評価を使用します(リスト(クエリセット)など)。多くの並行ワーカーがリクエストを処理すると、メモリが不足する可能性がありますか? したがって、結果を速くするか、メモリ使用量を増やすかを決定する必要があります(リクエストごとに65MB?これは数秒ですべてのメモリを消費する可能性があります)

プロファイラーは、あなたのコードが QuerySet.all() を使用していると言っています。

現在の QuerySet (または QuerySet サブクラス) のコピーを返します。

そして、これは非常に時間がかかる deepcopy を呼び出します。

したがって、 all() が呼び出される場所を見つけて、使用しないでください。

于 2013-02-16T18:42:05.203 に答える