一.原因
近期在做短信與郵件通知系統。使用到了這一塊。例如,當訂單完成以后進行郵件短信的通知。雖然可以采用直接調用接口的方式實現,但有幾個原因讓我希望使用條件觸發的方式
1.由於系統中支持線上線下以及代充值等多種方式,所以在多個地方訂單改變狀態。這樣就讓觸發通知的代碼凌亂分布。
2.系統將來擴建,需要新增加接口。則需要新增加調用的代碼。
總而言之,直接調用將會增加維護難度。因此准備在訂單的狀態首次被置為支付成功時候進行短信與郵件的通知。
二.模塊需求
短信與郵件的通知不能影響內部系統的運行,但由於是遠程調用,時間是未知的。為了滿足需求,模塊必須滿足以下條件
1.短信,郵件的發送使用線程
2.觸發必須在數據庫事務完成的狀態下進行。
三.sqlalchemy的監聽類型
sqlalchemy是我們采取的數據庫后台python接口。它的事務觸發機制包括
1.orm中的字段改變觸發
2.orm對象狀態改變觸發
3.session改變觸發
4.連接池事件,連接事件
四.具體實現
1.方式一
一開始我采用了傾聽字段改變的事件
@event.listens_for(Order.status,"set",retval=True) def pay_success_reminder(target, value, oldvalue, initiator): ''' 已經充值成功,給用戶發送提醒 :return: ''' if value==oldvalue:return value if value=="pay_success":#支付成功 try:
...... pass except Exception as e: pass return value
這個事件在訂單狀態改變的時候就會觸發,不幸的事,他觸發在事務當中,當然,如果在其中另外線程去執行發送短信與郵件應該也無妨。但處於某些原因我希望在事務處理完成之后調用。
2.方式二
另外一種方式,監聽對象的更新事件,並在更新事件處理中監聽這個session commit事件。這樣就能夠完成commit之后對該對象的處理。
def pay_success_reminder2(order_no): def func(session): try: ...... pass except Exception as e: pass return True return func @event.listens_for(Order, 'after_insert',raw=True) @event.listens_for(Order, 'after_update',raw=True) def pay_success_reminder1(mapper, connection, target):# ''' 已經充值成功,給用戶發送提醒 :return: ''' if target.dict["status"]!=target.committed_state["status"] and target.dict["status"]=="pay_success": event.listen(target.session,"after_commit",pay_success_reminder2(target.dict["order_no"]) )
如果需要將某些信息傳遞給session狀態監聽處理函數,則在需要使用函數包裝的方式傳遞變量。