Linux で TCP 接続の最大数をテストする方法

Linux で TCP 接続の最大数をテストする方法

序文

TCP サーバの最大同時接続数に関して、「ポート番号の上限が 65535 であるため、TCP サーバが理論上処理できる最大同時接続数も 65535 である」という誤解があります。

まず結論を述べます。TCP サーバー プロセスの場合、同時に接続できるクライアントの数は、使用可能なポート番号によって制限されません。同時接続数は、Linux で開くことができるファイルの数によって制限されます。この数は設定可能で、非常に大きくなる可能性があるため、実際にはシステム パフォーマンスによって制限されます。

昨今、高並列性でないとサーバ開発をすると外に出るのが恥ずかしくなります。そこで、将来「日々並列性を向上させていますが、これまで達成した最高の並列性はどれくらいですか?」と聞かれたときに、きちんと答えられるように、お正月に家で何もせずにいる間に自分でデモを書くことにしました。

このテストの主な目的は、Linux のどのパラメータ設定が最大接続数を制限し、その上限はいくらであるかを調べることです。

1.まずデモのアイデアについてお話しします。

サーバーは、接続を受信するだけの epoll で実装されており、クライアントは go の goroutine を使用します。各 goroutine は、接続を確立するだけで、その後は何も行いません。

上記のコード:

サーバ:

/*
 * g++ -o test_epoll ./test_epoll.c
 */
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <文字列.h>
#include <errno.h>

int SetReuseAddr(int fd)
{
 整数 最適値 = 1;
 socklen_t optlen = sizeof(optval);
 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen) を返します。
}

int メイン()
{
 int fd = socket(AF_INET, SOCK_STREAM, 0);
 int iRet = SetReuseAddr(fd);
 (iRet != 0) の場合
 {
 printf("SO_REUSEADDR の setsockopt が失敗しました。エラー:%s\n", strerror(iRet));
 iRet を返します。
 }

 構造体 sockaddr_in アドレス;
 memset(&addr, 0, sizeof(addr));
 アドレス.sin_family = AF_INET;
 addr.sin_port = htons(8080);
 addr.sin_addr.s_addr = INADDR_ANY;
 (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1 の場合)
 {
 printf("バインドに失敗しました。エラー:%s\n", strerror(errno));
 errno を返します。
 }

 (listen(fd, 5) == -1)の場合
 {
 printf("listen に失敗しました。エラー:%s\n", strerror(errno));
 errno を返します。
 }
 printf("8080 でリッスンしています...\n");

 epfd = epoll_create(102400);
 構造体 epoll_event イベント;
 イベント.イベント = EPOLLIN;
 イベントデータfd = fd;
 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);

 構造体epoll_eventイベント[102400];
 int iOnline = 0;
 (1)
 {
 int num = epoll_wait(epfd, revents, 102400, 60 * 1000);
 printf("epoll_wait 戻り値 %d\n", num);
 (数値>0)の場合
 {
  (int i = 0; i < num; i++) の場合
  {
  (revents[i].data.fd == fd)の場合
  {
   整数クライアント;
   構造体 sockaddr_in cli_addr;
   socklen_t cli_addr_len = sizeof(cli_addr);
   クライアント = accept(fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
   (クライアント == -1)の場合
   {
   printf("受け入れに失敗しました、エラー:%s\n", strerror(errno));
   (errno == EMFILE)の場合
   {
    printf("プロセスごとの制限に達しました\n");
    終了(エラー番号);
   }
   (errno == ENFILE)の場合
   {
    printf("システム全体の制限に達しました\n");
    終了(エラー番号);
   }
   続く;
   }

   iOnline++;
   printf("%s:%d から新しい接続を受信しました\n", inet_ntoa(cli_addr.sin_addr), cli_addr.sin_port);
   イベント.イベント = EPOLLIN;
   イベントデータfd = クライアント;
   epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
  }
  }
 }
 printf("オンライン番号:%d\n", iOnline);
 }

 0を返します。
}

クライアント:

パッケージメイン

輸入 (
 "ネット"
 「fmt」
 "時間"
 「strconv」
 "ランタイム"
)

func Connect(ホスト文字列、ポート整数) {
 _, err := net.Dial("tcp", host+":"+strconv.Itoa(port))
 err != nil の場合 {
 fmt.Printf("%s:%d へのダイヤルに失敗しました\n", ホスト, ポート)
 戻る
 }

 のために {
 時間.スリープ(30 * 1000 * 時間.ミリ秒)
 }
}

関数main() {
 カウント:= 0
 のために {
 接続します("192.168.63.128", 8080)
 カウント++;
 fmt.Printf("Goroutie num:%d\n",runtime.NumGoroutine())
 時間.スリープ(100 * 時間.ミリ秒)
 }
}

2. テストを開始する

初め:

まずは結果からお話しします。接続数が 1031 に達した時点で accept が失敗しました。このとき errno は判定されていなかったため、accept 失敗のみが出力されました。

すると、最初に思い浮かぶのは ulimit -n 制限です。確認してみると、デフォルト値は 1024 であることがわかりました。次に、この値を変更し、/etc/security/limits.conf に次の内容を追加しました。

1 * ソフトノーファイル 102400
2 * ハードノーファイル 102400

次に、現在の xshell 接続を閉じて再接続し、有効にします。これで、ulimit -n は 102400 になります。

これら 2 行は、各プロセスが開くことができるファイル記述子の数に対するソフト制限とハード制限を 102400 に調整することを意味します。

注意: ulimit -n 102400 も有効になりますが、この変更は一時的です。

次に2回目のテストを実行します。

2回目:

面白いことに、実際には接続数は 2000 以上しかありません。Windows のデフォルトの接続数がなぜこんなに高いのか疑問に思っていました。一部の接続が切断されていることが判明しましたが、何もしていないため、まだ残っていると思っていました。別の仮想マシンをインストールする必要があるようです [Erha]

つづく。 。 。

仮想マシンをインストールします。

時間: 2017-12-31 00:09:00

仮想マシンがインストールされたら、続行します。

今回は本当に10Kを超えました。

まだまだ接続数が増え続けています。最終的には10万まで行けるかな。楽しみです。

時間: 2017-12-31 00:41:00、最終的な上限は 28232 で止まっています。golang はダイヤル失敗を報告し続けています。具体的なエラー メッセージを出力し忘れたため、ダイヤルが失敗した理由がわかりません。そのため、もう一度実行するしかありません T_T

時間: 2017-12-31 01:01:00、ダイヤル失敗のエラー メッセージを追加し、再度実行しましたが、28232 でダイヤル失敗が引き続き発生し、次のエラー メッセージが表示されました:

golangの標準ライブラリのドキュメントにはエラーメッセージの説明がありません。エラーメッセージからするとアドレスの割り当てに失敗しているようなので、ポートアドレスの範囲が制限されているのかなと思います。

ポートアドレス範囲を確認し、これが制限であることを確認しました。ポートアドレスは16ビットなので、ポートアドレス範囲を1024〜65535に変更しても、最大64521の接続しか開けません。クライアントとして仮想マシンを1台しか持っていないので、10万接続を実現することは不可能です。ただし、このテストを通じて、どのパラメータが接続の上限を制限するのかについても把握できました。これが私が望んでいることです。

最後に、epoll のような素晴らしいメカニズムを立ち上げてくれた Linux カーネル チームの素晴らしい人々に感謝したいと思います。epoll のおかげで、高い並行性を簡単に実現できるようになりました。いつか私も彼らと同じくらい素晴らしい人になれたらいいなと思います。

要約する

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • Linux での TCP 接続タイムアウト問題の解決方法
  • Linux で TCP 接続を確認する 2 つのコマンド
  • Linux で TCP フラッド攻撃を防ぐ方法

<<:  Windows プラットフォームでの MySQL のインストールと設定方法と注意事項

>>:  MySQLクエリ結果をCSVにエクスポートする方法

ブログ    

推薦する

幅の比率に応じて高さを変えるCSSを実装するいくつかの方法

[解決策1: パディングの実装]原理:要素の padding の値がパーセンテージの場合、このパーセ...

MySQL ALTERコマンドの知識ポイントのまとめ

テーブル名を変更したり、テーブル フィールドを変更したりする必要がある場合は、 MySQL ALTE...

Zookeeperスタンドアロン環境とクラスタ環境の構築

1. 単一マシン環境の構築# 1.1 ダウンロードZookeeper の対応するバージョンをダウンロ...

Centos7でのSambaサーバー構成(実戦)

サンバの概要Samba は、Linux および UNIX システム上で SMB プロトコルを実装する...

ウェブフロントエンドエンジニアにおすすめのヒント

まず、Webフロントエンドエンジニアの価値についてお話ししましょう。現在、Web製品のインタラクショ...

JavaScript ジグソーパズルゲーム

この記事の例では、ジグソーパズルゲームを実装するためのJavaScriptの具体的なコードを参考まで...

表には表示したい境界コードが表示されます

テーブルの共通プロパティ基本的な属性は、width (幅)、height (高さ)、border (...

jQueryは広告を上下にスクロールする効果を実現します

この記事では、広告を上下にスクロールする効果を実現するためのjQueryの具体的なコードを参考までに...

Dockerコンテナデータをコピーしてバックアップする方法の詳細な説明

ここでは、Jenkins コンテナを例に 3 つの方法を紹介します。方法1コンテナをイメージにパッケ...

nginx のロードバランシングとリバースプロキシの説明

目次負荷分散負荷分散分類1. DNS 負荷分散2. IP負荷分散3. リンク層の負荷分散4. ハイブ...

Bootstrap 3.0 学習ノート グリッドシステムの原則

前の 2 つの記事の簡単な紹介を通じて、Bootstrap についての基礎的な理解が得られました。 ...

Navicat が MySql サーバーにリモート接続できない問題の解決策

Navicat が MySql サーバーにリモート接続できない問題の解決策は、先頭に書かれています:...

MySQL 8.0.15 winx64 解凍版のインストールと設定方法のグラフィックチュートリアル

この記事では、MySQL 8.0.15 winx64解凍版のインストールと設定方法を紹介します。具体...

Vue フロントエンドで PDF を生成してダウンロードする方法

目次1. インストールと導入2. PDFファイルをパッケージ化してエクスポートする方法構成の詳細PD...