uwsgi+django+gmail+threading多執行緒發郵件

只愛一個人發表於2017-12-06

uwsgi+django+gmail+threading多執行緒發郵件

最近負責公司的金融平臺、其中在稽核人員通過提現稽核之後需要給使用者發郵件進行通知。

廢話不多說、直接上程式碼:

# coding: utf-8

import threading
import datetime
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email import encoders
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
import os
import logging

logger = logging.getLogger('django')

class EmailSend(threading.Thread):
    '''
    傳送郵件
    '''
    def __init__(self, msg, title, receivers):
        self.msg = msg
        self.title = title
        self.receivers = receivers
        self.img = os.path.join(os.path.abspath('.'), 'static/img/liveme.png')
        threading.Thread.__init__(self)

    def run(self):
        EMAIL_LIST = self.receivers
        EMAIL_HOST = 'smtp.gmail.com'
        EMAIL_HOST_USER = '****'
        EMAIL_HOST_PASSWORD = '****'


        message = MIMEMultipart('related')
        message['Subject'] = Header(self.title, 'utf-8')

        msg_text = MIMEText(self.msg, 'html', 'utf-8')
        message.attach(msg_text)

        with open(self.img, 'rb') as f:
            msg_image = MIMEImage(f.read())
            msg_image.add_header('Content-Disposition', 'attachment', filename='liveme.png')
            msg_image.add_header('Content-ID', '<0>')
            msg_image.add_header('X-Attachment-Id', '0')
            message.attach(msg_image)
        logger.info('begin to send email, params: %s, time: %s' % (locals(), str(datetime.datetime.now())))
        smtpObj = smtplib.SMTP()
        smtpObj.connect(EMAIL_HOST, 25)
        smtpObj.starttls()
        smtpObj.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
        smtpObj.sendmail(EMAIL_HOST_USER, EMAIL_LIST, message.as_string())
        smtpObj.quit()
        logger.info('end to send email, params: %s, time: %s' % (locals(), str(datetime.datetime.now())))

def send(msg, title, receivers):
    '''
    傳送郵件
    :param msg:
    :param title:
    :param receivers:
    :return:
    '''
    EmailSend(msg, title, receivers).start()

複製程式碼

本地是用pycharm起的開發環境、OK、程式碼在本地完全沒問題、可以很好的把郵件發出去。但是把程式碼部署到測試環境的時候發現不行了(測試環境為uwsgi)、TMD、郵件根本發不出去、也不報錯。此刻表示很桑心?。

於是乎開始了漫長的排查過程。。。

  1. 測試伺服器用的是aws,會不會是aws對伺服器做了什麼限制?

    開始找運維、經過運維同學的一番折騰、發現aws沒有做任何限制。不對啊、那為毛在本地就可以很好的發出去而到了aws就掛了、不科學啊。難道是網路原因?於是又開始折騰搞網路的同學、最後發現網路也沒有問題?

  2. 發現一個很怪異的問題:每次觸發郵件操作之後都好比石沉大海似的沒有了下文,但是一重啟服務立馬就能收到之前的郵件。

    這是什麼邏輯,難道每次發完郵件都需要重啟服務?顯然不可能啊、沒人這麼幹啊。算了,繼續排查。

  3. 排查中。。。

    發現程式碼裡面少了一個操作smtpObj.quit()、恍然大悟、難不成不執行smtpObj.quit()的話程式就會hang住?滿懷期待的加上了這句程式碼、結果除了失望還是失望?。

  4. 繼續排查。。。

    這環境和程式碼都明明是一模一樣的、為啥啊、難道是多執行緒的問題?可是程式碼和環境都是一樣的啊、想不通、開始走到崩潰的邊緣。靜下心來仔細想想,不對、還有一個地方不一樣。本地開發環境是用python manage.py runserver方式啟動的、而測試環境是用uwsgi方式啟動的。於是抱著試試看的心態去Google了一下。TMD、還真是uwsgi搞的鬼、之前的uwsgi.ini配置如下:

    [uwsgi]
    socket=127.0.0.1:8000
    #http= :8000
    master=true
    vhost=true
    gid=nobody
    uid=nobody
    module=uwsgi
    workers=24
    max-requests=1000
    limit-as=1024
    pidfile=/data/app/cms-finance/uwsgi.pid
    daemonize=/data/app/cms-finance/uwsgi.log
    log-x-forwarded-for
    harakiri=1800
    buffer-size=16384
    複製程式碼

    後來增加了倆配置:

    enable-threads=true
    lazy=true
    複製程式碼

    再次觸發郵件操作、萬幸、這次可算是收到了。

    官方文件是這麼說明的

    Python 執行緒小貼士
    
    如果你沒有使用執行緒啟動 uWSGI,Python 的 GIL
    將不會被開啟,所以你的應用產生的執行緒
    將永遠不會執行。你可能不會喜歡這個選擇,但是記住 uWSGI
    是一個語言無關的伺服器,所以它的大部分選擇都是儘可能維持它 “agnostic”。
    
    但是不用擔心,基本上不存在不能通過選項來改變的由 uWSGI 開發者決定的選項。
    
    如果你想維持 Python 的執行緒支援同時應用又不啟動多個執行緒,只需要加上
    --enable-threads 選項 (或者 enable-threads = true 在 ini 風格配置檔案中)。
    複製程式碼

最後

只想告誡自己

有些坑只有踩過之後才會記憶深刻?

相關文章