5

Python でクライアント サーバー アプリを作成しています。アイデアは、メイン サーバーとそれに接続する何千ものクライアントを持つことです。サーバーはランダムに小さなファイルをクライアントに送信して処理します。クライアントは作業を行い、そのステータスを毎分サーバーに更新する必要があります。これに関する私の問題は、今のところ、小さくて古いホームサーバーしか持っていないため、それほど多くの接続を処理できないと思うことです. 多分あなたはこれで私を助けることができます:

  • サーバーの接続数を増やすにはどうすればよいですか?
  • クライアント側から負荷を分散するにはどうすればよいですか?
  • どうすればコミュニケーションを改善できますか?つまり、サーバー上にクライアントのリストとそのステータス (おそらく DB 内?) が必要であり、この更新は時々受信されるため、永続的な接続は必要ありません。UDP を使用して更新を送信することをお勧めしますか? そうでない場合、更新を受け取るたびに新しいスレッドを作成する必要がありますか?

編集:問題を少し詳しく説明するために質問を更新しましたが、主に同じ問題を抱えている人にとって十分に明確になるようにしました。@TimMcNamaraの回答には実際に良い解決策があります。

4

2 に答える 2

14

成功への準備: アクセスパターンが重要

ネットワーキング ソリューションの実装方法に影響を与える可能性のある設計上の決定事項には、どのようなものがありますか? あなたはすぐにいくつかのリストを書き始めます:

  • プログラマビリティ
  • 使用可能なメモリ
  • 利用可能なプロセッサ
  • 利用可能な帯域幅

これは素晴らしいリストのようです。プログラミングが簡単で、かなりハイスペックなものが欲しい。しかし、このリストは失敗します。ここで行ったことは、サーバーのみを確認することです。Web アプリケーションで制御できるのはこれだけかもしれませんが、センサー ネットワークのように完全に制御できる分散システムについてはどうでしょうか。

1 分ごとに最新のセンサー測定値を更新する必要があるデバイスが 10,000 台あるとします。これで、すべてのデバイスとの同時接続を保持するハイエンド サーバーを使用できるようになりました。

ただし、非常にハイエンドなサーバーを使用している場合でも、パフォーマンスの問題が発生する可能性があります. すべてのデバイスが同じクロックを使用し、すべてのデバイスが毎分の最初にデータを送信しようとすると、サーバーは毎分 1 ~ 2 秒間多くの CPU 作業を行い、残りの時間は何もしません。非常に非効率的です。

センサーを制御できるので、センサー自体に負荷分散を依頼できます。1 つのアプローチは、各デバイスに ID を付与し、モジュラス演算子を使用して、1 分あたりの適切な時間にのみデータを送信することです。

import time        

def main(device_id):
    data = None
    second_to_send = device_id % 60
    while 1:
        time_now = time.localtime().tm_sec
        if time_now == 0:
            data = read_sensors()
        if time_now == second_to_send and data:
            send(data)
        time.sleep(1)

このタイプの負荷分散の結果の 1 つは、そのような強力なサーバーが不要になることです。全員との接続を維持するために必要だと考えていたメモリと CPU は必要ありません。

ここで私が言おうとしているのは、特定のソリューションが問題全体に焦点を合わせていることを確認する必要があるということです。あなたが提供した簡単な説明では、膨大な数の接続を常時維持する必要はないようです。ただし、100% の接続が必要であるとしましょう。どのようなオプションがありますか?

ノンブロッキング ネットワーキング

ノンブロッキング I/O の効果は、ファイル記述子にデータがない場合にファイル記述子を要求している関数が、すぐに戻ることを意味します。ネットワークの場合、ソケットから読み取ろうとする関数が呼び出し元にデータを返さないため、これは潜在的に悪い可能性があります。したがって、スレッドを生成してから を呼び出す方が簡単な場合がありますread。そうすれば、スレッド内でブロックしてもプログラムの残りの部分には影響しません。

スレッドの問題には、メモリの非効率性、スレッドの作成に伴うレイテンシ、コンテキストの切り替えに伴う計算の複雑さが含まれます。

ノンブロッキング I/O を利用するには、関連するすべてのファイル記述子をwhile 1:ループで潜在的にポーリングできます。CPU が 100% で実行されるという事実を除けば、それは素晴らしいことです。

これを避けるために、イベントベースのライブラリが作成されました。実行する作業がない場合は CPU を 0% で実行し、データを読み取って送信する場合にのみアクティブにします。Python の世界では、TwistedTornado、またはgeventが大きな役割を果たしています。ただし、多くのオプションがあります。特にディーゼルは魅力的に見えます。

以下は、Tornado の Web ページからの関連する抜粋です。

ノンブロッキングで epoll または kqueue を使用するため、何千もの同時接続を処理できます。これは、リアルタイム Web サービスに最適であることを意味します。

これらのオプションはそれぞれ、わずかに異なるアプローチをとります。Twisted と Tornado は、ノンブロッキング操作に依存するという点で、アプローチがかなり似ています。Tornado は Web アプリケーションに焦点を当てていますが、Twisted コミュニティはより広範なネットワーキングに関心があります。その後、非 HTTP 通信用のツールがさらに追加されました。

ゲントは違います。ライブラリはソケット呼び出しを変更し、各接続が非常に軽量なスレッドのようなコンテキストで実行されるようにしますが、これは実際にはプログラマーには隠されています。データベース クエリやその他の I/O などのブロッキング コールが発生すると、gevent はコンテキストを非常に迅速に切り替えます。

これらの各オプションの結果は、単一の OS スレッド内で多くのクライアントにサービスを提供できることです。

サーバーの微調整

オペレーティング システムによって、許可される接続数が制限されています。話している数値に達すると、これらの制限に達する可能性があります。特に、Linux では、各ユーザーの制限が維持されます/etc/security/limits.confulimitシェルで呼び出すことにより、ユーザーの制限にアクセスできます。

$ ulimit -a
コア ファイル サイズ (ブロック、-c) 0
データ セグメント サイズ (キロバイト、-d) 無制限
スケジューリング優先度 (-e) 0
ファイルサイズ (ブロック、-f) 無制限
保留中のシグナル (-i) 63357
最大ロック メモリ (キロバイト、-l) 64
最大メモリ サイズ (キロバイト、-m) 無制限
ファイルを開く (-n) 1024
パイプサイズ (512 バイト、-p) 8
POSIX メッセージ キュー (バイト、-q) 819200
リアルタイム優先度 (-r) 0
スタックサイズ (キロバイト、-s) 8192
CPU 時間 (秒、-t) 無制限
最大ユーザー プロセス (-u) 63357
仮想メモリ (キロバイト、-v) 無制限
ファイルロック (-x) 無制限

ここで最も関連性の高い行を太字にしましたopen files。開いている外部接続は、開いているファイルと見なされます。その 1024 の制限に達すると、アプリケーションは別のファイルを開くことができなくなり、それ以上のクライアントはサーバーに接続できなくなります。httpdWeb サーバーとしてユーザーがいるとします。これにより、その制限を上げるために行うことができる変更のアイデアが得られるはずです。

httpd soft nofile 20480
httpd hard nofile 20480

非常に大量の場合、システム全体の制限に達する可能性があります。次の方法で表示できますcat /proc/sys/fs/file-max

$ cat /proc/sys/fs/file-max
801108

この制限を変更するには、 を使用しますsudo sysctl -w fs.file-max=n。ここで、n は許可するオープン ファイルの数です。/etc/sysctl.confこれが再起動後も存続するように変更します。

于 2012-10-13T23:32:46.580 に答える
1

一般的に言えば、非常に控えめなホーム サーバーでさえ、一度に数万のソケットを使用しても問題はありません。

接続ごとに新しいスレッドやプロセスを作成しないようにしてください。

于 2012-10-13T22:38:16.853 に答える