年初、開発タスクを強制的に受けることになり、自分は製品や開発をしたことがないので、頭を抱えながら進めるしかありませんでした。ググりながら AI も使って、最近少し進展がありました。学んだ知識をメモしておきます。細かい内容ですが、役に立つかもしれません。
プロジェクト#
割り当てられたのは、ウェブサイトの可用性監視エンジンの開発です。他の言語は得意ではないので、Python を選びました。自分が一番慣れているフレームワークは Flask なので、そこから始めました。
Python の POST データ#
Flask や他の Python のフレームワークには、「反人間的」(もしくは私に対して反している可能性もあります)な書き方があります。
例えば、ルートを書くときには次のように書かないと POST データを取得できません。通常のアクセスでは GET しかできません。
@app.post("/test/api/add")
def ch(request: Request):
pass
並行処理#
最初はモジュールだけをやると言われましたが、後でエンジンと言われるようになり、だんだんと大きくなり、結局一人で製品を開発することになりました。
このエンジンは最終的には会社の全プロジェクトの監視タスクを受けることになります。目標は保守的に数千個のプロジェクトです。大体の見積もりでは、並行処理は 300 程度になると予想されます。
そこで、Flask がこのような大きな並行処理に耐えられるかどうか調べてみました。
すると、Flask は開発エンジンであり、本番環境では使用しないことが推奨されていることがわかりました。おそらく高い並行処理に対応できないためだと思います。そして、FastAPI は高い並行処理のシナリオに適しており、API エンジンとしても適しているとのことでしたので、FastAPI に切り替えることにしました。
- 単純に FastAPI に切り替えるだけでは最適化効果は得られません。関数に async キーワードを追加し、非同期処理を使用する必要があります。
- データを受け取る際には await キーワードを追加する必要があります。そうしないとデータを受け取ることができません。なぜかはわかりませんが、これについては学習する必要があります。
- すべての関数に async を追加する必要があります。そうしないと最適化されず、逆にパフォーマンスが低下します。
- FastAPI を使用する場合、別途 Web サーバーをインストールする必要があります。公式では uvicorn を推奨していますが、インストール時の形式は uvicorn [xxxx] です。この形式は uvicorn とその依存関係をインストールすることを意味します。
- 非同期処理を使用する場合、requests は使用できません。requests は非同期リクエストをサポートしていないため、aiohttp に切り替えました。
- そして最も重要なポイントですが、実際にテストした結果、FastAPI は Flask と比べて並行処理の最適化効果がそれほど大きくないことがわかりました。
リクエストの監視#
上流のサーバーからタスクを受け取り、監視する必要があります。
監視とは、GET で送信されてくる URL です。ここにもいくつかのポイントがあります。
- リクエストにはできるだけ UA などのものを付ける方が良いです。ウェブスクレイピングのような戦略を使って、スクレイピング防止システムに干渉されないようにします。
- ウェブサイトを監視する場合、GET メソッドを使用することが最適です。一部のウェブサイトではメソッドが制限されています。HEAD を使用することも考えましたが、誤検知が発生しました。おそらくミドルウェアやセキュリティデバイスの設定によるものと推測されます。
- ウェブサイトに対してリクエストを送信しても 200 が返ってきたからといって、必ずしもウェブサイトにアクセスできるわけではありません。レスポンスのサイズを確認してさらに確認する必要があります。
- タイムアウト時間は少し長めに設定しましょう。ターゲットネットワークが実際に遅い場合もありますし、自分たちのネットワーク層が過負荷になることもあります。
- 奇妙なネットワークアーキテクチャもあります。例えば、政府機関のウェブサイトです。地方の組織の多くは、ウェブサイトを上位のホストに関連付けることを選択します。名前は異なるかもしれませんが、同じホストを指しています。この市区を監視すると、ホスト側からは私が高頻度のリクエスト、つまり DDoS のように見えるため、直接ブロックされます。唯一の方法は、プロキシプールを使用し、各リクエストに異なるプロキシを使用することです。
全体のアーキテクチャ#
最初はスクリプトを書くだけで済むと思っていましたが、徐々にアーキテクチャ全体を変更する必要があることに気づきました。
まず、会社からはプロキシプールは提供されず、高頻度のダイヤルアップサーバーと会社の出口サーバーのみが提供されました。なので、これらの 2 つのマシンを最大限活用しようと考え、ロードバランシングを使用することにしました。
実際、以前は聞いたことはあっても実際に使ったことはありませんでした。私の主な仕事はどうやって侵入するかであり、ロードバランシングは単に別の権限を取得するためのものだと思っていました。
ロードバランシング#
詳しく学んでみると、ロードバランシングは基本的にはリバースプロキシです。リバースプロキシサーバーを使用してリクエストを転送します。以前に書いた記事と似ています。
初探单域名单端口多服务 web
その記事では、サフィックスに基づいて異なるサーバーに転送する方法を説明していますが、ロードバランシングはある種のアルゴリズムに基づいて転送します。
以前は、専門のサービスコンポーネントがあるのかと思っていましたが、リバースプロキシが最も便利だということがわかりました。
設定ファイルは以下のようになります。
worker_processes 4; //ワーカープロセス数、通常はホストのコア数と同じです
events {
worker_connections 40960;//各プロセスが実行できるスレッド数、私の場合はphpstudyのパラメータをそのまま使っています
}
http {
upstream web { //ロードバランシングノード
server 192.168.1.2:8000;
server 192.168.1.3:8000;
}
server {
listen 80;//リバースプロキシサーバーのリッスンポート
location / {
proxy_pass http://web;//ノードリストを指定します。ここでのwebはupstream webのwebです。通常は正常なURLです
}
}
}
Docker の学習#
ここまで来たので、個別に nginx をサーバーにインストールするのは面倒です。そこで、Docker を思いつきました。以前は他の人のものを使用するときにだけ Docker を使用していましたが、今回は自分で開発者として使ってみることにしました。
サーバーシステムは CentOS 7 で、yum install docker
を実行しました。
そして、docker-compose もインストールする必要があります(この単語をようやく覚えました)。
不思議なことに、CentOS 7 では yum で直接インストールできず、docker-compose のインストールには pip を使用する必要があります。ただし、インストールが完了しても使用できないことが実証されています。おそらく他の問題があるかもしれません。そのため、公式ドキュメントに従って wget で実行可能ファイルを取得しました。
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose.yaml#
また、docker-compose.yaml のルールも学びました。やはりノードアーキテクチャでは SQLite は使用できません。MySQL をデプロイする必要があります。2 つのコンポーネントを組み合わせるために、docker-compose を使用して簡単に設定できます。
version: '3'
services:
nginx:
image: nginx //イメージ名
ports:
- "19100:80" //ホストのポート:コンテナ内のサービスのポート
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro //ファイルをコンテナ内に上書きします
db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: web
ports:
- "3306:3306"
volumes:
- ./web.sql:/docker-entrypoint-initdb.d/web.sql //docker-entrypoint-initdb.dディレクトリ内のSQLファイルは自動的にデータベースにインポートされます
以上です。思いついたことはこれくらいです。多くの問題に直面し、多くのことを学びました。やはり「あなたが耐える苦難は、将来の資本になる」という言葉は本当です。
ブラウザが重くなり始めたので、ここまでにします。質問やアドバイスがあれば、積極的にコメントしてください。進歩と学習を促してください。
最後に、監視には誤報があります。利用可能なターゲットをダウンと報告してしまいますが、なぜかわかりません。同じターゲットの Python スクリプトでは使用できませんが、curl や httpx ツールでは正常に動作します。理由がわかりません。大先輩方からのアドバイスをお待ちしています。