会社でメールマガジンを送る際、送信対象人数が多いとサーバーが止まってしまうという不具合があったので負荷分散するために送信処理とサーバーを並列化しました。
その際に、並列化のコードやサーバー側の設定で勉強になったことやつまづいたことを備忘録として書きます。
メール送信の並列化について
送信処理の並列化をスレッドプールかプロセスプールで並列化予定だったので、まずはスレッドプールとプロセスプールについて調べました。
スレッドプール
スレッドプールは、複数のスレッドをプールで管理し、必要に応じてスレッドを再利用する仕組みです。これにより、スレッドの生成と破棄にかかるオーバーヘッドを削減し、効率的に処理を行うことが期待できます。
プロセスプール
プロセスプールは、複数のプロセスを生成し、それぞれが独立して処理を行います。スレッドプールと比べてリソースをより分離することができるため、安定して大規模な処理を行うことが可能です。
並列化ではメールの送信は主にI/O操作が中心なためスレッドプールで実装しました。
送信処理について
送信処理を作成する際、pythonのconcurrent.futuresモジュールを使用して並列化しました。
with ThreadPoolExecutor(max_workers=3) as executor:
executor.map(送信処理)
というようなコードで3並列で送信をしてくコードを作成しました。
送信処理は実装コードによって変わりますが、djangoの場合は下記のように送信用のコードがあるので、コードに沿って送信処理を作成します。
djangoのメール送信の詳細
def send_mail(subject, message, from_email, recipient_list,
fail_silently=False, auth_user=None, auth_password=None,
connection=None, html_message=None):
"""
Easy wrapper for sending a single message to a recipient list. All members
of the recipient list will see the other recipients in the 'To' field.
If from_email is None, use the DEFAULT_FROM_EMAIL setting.
If auth_user is None, use the EMAIL_HOST_USER setting.
If auth_password is None, use the EMAIL_HOST_PASSWORD setting.
Note: The API for this method is frozen. New code wanting to extend the
functionality should use the EmailMessage class directly.
"""
connection = connection or get_connection(
username=auth_user,
password=auth_password,
fail_silently=fail_silently,
)
mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, connection=connection)
if html_message:
mail.attach_alternative(html_message, 'text/html')
return mail.send()
awsサーバーの並列化について
awsの場合はec2で送信サーバーを作った後、サーバー接続用のコードを作成します。 サーバー接続のコードはdjangoの場合は send_mail() と同じでサーバーの接続用のコードがあります それを参考にしてください
サーバーの並列化については送信処理の部分でsend_mail()に渡す値としてconnectionがあり、その部分に処理ごとに接続先サーバーを渡せばサーバーごとに処理します。
私は、接続先サーバーのリストを作り、itertoolsのcycleを使い処理ごとに順次割り当てしています。送信確認
ec2などで作成したメールサーバーに接続ができるか、送信は可能かを調べる場合は netcat などを使用して、ターミナル(Mac)やコマンドライン(Windows)から直接確認することができます。
nc [オプション] [ホスト名またはIPアドレス] [ポート番号]
nc example.com 587
上記の場合はexample.comに587ポートで接続します
ポート番号についてはwikipediaなどにも載っているので一度見ておくとわかりやすいかもです。
メール送信関連だけなら25、587、465、2525などを見れば問題ないです。
220 example.com ESMTP Postfix (Ubuntu)
上記のような文字が表示されれば接続できています。
メール送信のpostfixについて
私が一番苦戦した部分がpostfixの設定です。postfixの修正をする場合はsshなどを利用し、送信サーバに接続して、/etc/postfix/のファイルを修正します。
主な修正ファイルはmain.cfとmaster.cfです。
詳細な設定はサーバーによって変わったり、すると思うので修正の際に出たエラーの対処方法を記載します。
対処する際はPostfixの設定パラメータを参考にしてください