環境
- マシンA(自宅)
- AMD Ryzen7 4800H(8core/16thread) 16GBメモリ
- Windows 11 Home(ホストOS:VMware Workstation 16 Player): Thunderbird(POP3)
- Red Hat Enterprise Linux release 8.4 (Ootpa)(ゲストOS):postfix/dovecot
- ホスト4core/ゲスト4coreの割り当て。
- マシンB(AWS)
- t3.xlarge(4vCPU/16GBメモリ)
- Red Hat Enterprise Linux release 8.6 (Ootpa):postfix/dovecot
検証手順
postfixの設定は本番を意識したものではなくなる早で検証開始する目的
#vi /etc/postfix/main.cf
#変更内容ポイント
myhostname = 環境の値を設定
inet_interfaces = all
home_mailbox = Maildir/
#最後に追加
message_size_limit = 0
mailbox_size_limit = 0
変更後再起動
# systemctl restart postfix
dovecotの設定は本番を意識したものではなくなる早で検証開始する目的
vi /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
vi /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = no
vi /etc/dovecot/conf.d/10-ssl.conf
ssl = no
変更後再起動
# systemctl restart dovecot
メールデータの作成(添付ファイルでサイズ膨らませる)
※これをベースにサイズと件数のバリエーションをshell scriptで簡単に組める。
dd if=/dev/urandom of=/tmp/500m bs=1M count=500
echo "mail body" | mailx -a /tmp/500M -s 'test' test@localhost.localdomain
※注意:500MBのメールデータを意図しても実際のPOPサーバー内のファイルは708760764まで膨らむ。
参考shell script
#!/bin/bash
a=0
while [ "$a" -lt $1 ]
do
a=$(expr $a + 1)
echo "mail body" | mailx -a /tmp/1M -s 'test' test@xxx.yyy.zzz
done
※性能差(上記ddの結果)
- 自宅マシンの性能
- 524288000 bytes (524 MB, 500 MiB) copied, 9.20449 s, 57.0 MB/s
- AWSの性能
- 524288000 bytes (524 MB, 500 MiB) copied, 6.27526 s, 83.5 MB/s
※ネットワークレイテンシーの違い
-
自宅VM内の論理通信
- pingのavgが0.75msec
-
自宅とパブリックAWS通信
- pingのavgが7msec
- 環境によっては二桁msecとなる場合もある。これが様々なクライアント、サーバーアプリに悪影響を与える。
-
ケース1
thunderbirdで500MBデータをPOP3取得
結果:8MB/s で1分数十秒程度
CPU使用率はクライアント、サーバーともに使用率低。(1POPで1コア程度なのでCPU使い切るにはクライアント多重度をあげる必要あり。)
結果は自宅内と自宅とAWS間で大差出ず。
※時間計測はWindowsのリソースモニタを目視し、Thunderbird固有処理(クライアント固有処理)ではなくPOP3通信が連続していると思われる期間を計測。 -
ケース2
WinScpで500MBファイルの転送
60MB/s 10sec 自宅内
30MB/s 20sec 自宅とAWS間
※上記のPOPでの8MB/sがネットワークネックでないと判断できる。
※POPのほうがSCPよりもアプリケーションの処理コストが高い。
※レイテンシーに影響されやすいアプリケーション実装。(POP比較) -
ケース3
※自宅とAWS間のみ
thunderbird(500MB=1M*500 POP3)
5MB/s で約2分ちょい。
1MBファイルを500通のメールを取得 -
ケース4
※自宅とAWS間のみ
thunderbird(500MB=2621448B*2000 POP3)
3.x MB/s で約3分半。 -
ケース5
POP3アプリケーション観点。アプリのオーバーヘッドをみるため(ノイズを省く)自宅内のみで比較
50Kのメールを1000件受信するケース
①Thunerbird:3.1MB/sで23秒
②Javaフェッチのみ:20MB/sで4秒
③Java(シングルスレッドでシーケンシャルに処理し、メール情報をファイル出力):0.6MB/sで118秒
※Javaのサンプルソースは本ページの最後に記載。
※メールデータ分析処理のようなメールをバッチ処理するアプリケーションではアプリの実装によって性能が大きく異なる。
検証サマリ
環境 | アプリケーション | 処理 | 結果 | 備考 |
---|---|---|---|---|
自宅内 | WinSCP | 500MBファイル転送 | ネットワークスループット:60MB/s 実行時間:10sec | |
自宅-AWS | WinSCP | 500MBファイル転送 | ネットワークスループット:30MB/s 実行時間:20sec | WinSCPはネットワークレイテンシーに影響されやすい。 |
自宅内 | Thunderbird | 500MBファイルを添付したメールを1通POP3取得 | 8MB/s で1分数十秒程度 | |
自宅-AWS | Thunderbird | 500MBファイルを添付したメールを1通POP3取得 | 8MB/s で1分数十秒程度 | SCPのアプリコスト(プロトコルオーバーヘッド)が高い。 |
自宅-AWS | Thunderbird | 1MBファイルを添付したメールを500通POP3取得 | 5MB/s で2分超 | メール数に応じてPOPプロトコルオーバーヘッドが大きくなる。 |
自宅-AWS | Thunderbird | 2621448Bファイルを添付したメールを2000通POP3取得 | 3MB/s で約3分半超。 | メール数に応じてPOPプロトコルオーバーヘッドが大きくなる。 |
自宅-AWS | Thunderbird | 50KBファイルを添付したメールを1000通POP3取得 | 3.1MB/s で23秒。 | |
自宅-AWS | Java(フェッチのみ) | 50KBファイルを添付したメールを1000通POP3取得 | 20MB/s で4秒。 | ネットワークスループットMAXに近い処理 |
自宅-AWS | Java(フェッチ+永続化) | 50KBファイルを添付したメールを1000通POP3取得 | 0.6MB/s で118秒。 | シングルスレッド、シーケンシャルな最も非効率な処理 |
総論
ネットワーク環境、アプリの実装、プロトコルの複雑度、対象データの特性によって処理性能やネットワーク負荷が大きく異なる。
今後
【チューニング余地ありそうなもの】
/etc/dovecot/conf.d/10-mail.conf
#Max number of mails to keep open and prefetch to memory. This only works with
#some mailbox formats and/or operating systems.
#mail_prefetch_count = 0
※大量のメールデータを取り扱うアプリケーションで効果ありそう。
/etc/dovecot/conf.d/20-pop3.conf
#Allow only one POP3 session to run simultaneously for the same user.
#pop3_lock_session = no
※いわゆるセッションの使いまわし。ネットワークレイテンシー高い環境下で効果ありそう。
参考
Java POP3アプリ(ググってコピペしてきたもの)。フェッチオンリーとしているのは実際のメールデータ処理をすべてコメントアウトしている。これがケース5の②に該当。コメントアウトを外すとケース5の③になる。
JavaMailがデータをネットワーク越しにもってきているのは確実だが、一時領域としてどう内部的に格納しているのかは未調査。
package sample;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Properties;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeMultipart;
public class FetchOnly {
public static void main(String[] args){
long start = System.currentTimeMillis();
Folder folder = null;
try {
Properties props = new Properties();
props.put("mail.pop3.host", "192.168.47.128");
props.put("mail.pop3.port", "110");
Session session = Session.getDefaultInstance(props);
Store store = session.getStore("pop3");
store.connect("test", "test");
folder = store.getFolder("INBOX");
//folder.open(Folder.READ_ONLY);
folder.open(Folder.READ_WRITE);
// メッセージ一覧を取得
Message[] arrayMessages = folder.getMessages();
for (int lc = 0; lc < arrayMessages.length; lc++) {
// メッセージの取得
Message message = arrayMessages[lc];
// 件名の取得と表示
String subject = message.getSubject();
//System.out.print("件名:" + subject + "\r\n");
// 受信日時を表示
String sentDate = message.getSentDate().toString();
//System.out.print("受信日時:" + sentDate + "\r\n");
// 本文の取得と表示
MimeMultipart mltp = (MimeMultipart)message.getContent();
BodyPart body1 = mltp.getBodyPart(0);
//System.out.print("本文:" + body1.getContent().toString() + "\r\n");
// 1:添付ファイルボディを取得
BodyPart body2 = mltp.getBodyPart(1);
String fileName = body2.getFileName();
//System.out.print("添付ファイル名:" + fileName + "\r\n");
// 2:添付ファイルのストリームを取得
InputStream ins = body2.getInputStream();
// 3:出力ストリームに出力しながらファイルを保存する
OutputStream outs = new FileOutputStream("C:\\Users\\m2tor\\Desktop\\save.txt");
// int out;
// while ((out = ins.read()) != -1) {
// outs.write(out);
// }
ins.close();
outs.close();
message.setFlags(new Flags(Flags.Flag.DELETED), true); // 対象メールをメールサーバから削除する.
}
folder.close(true);
System.out.println("メッセージ処理件数:" + arrayMessages.length);
long end = System.currentTimeMillis();
System.out.println("処理時間(秒):" + (end - start)/1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(folder.isOpen()) {
folder.close(false);
}
} catch (MessagingException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}
}