1-單例設計模式?
單例—將只會初始化一次的操作,可以封裝到單列中—優化 單例—始終只需要初始化一個物件的方案,可以採用單例---資料庫連結用單例 ip + 埠 + 賬號密碼 == 資料庫物件 ip + 埠 + 賬號密碼 == 資料庫物件 ip + 埠 + 賬號密碼 == 資料庫物件 1,單例只保留一個物件,可以減少系統資源開銷。 2,提高建立速度,每次都獲取已經存在的物件因此提高建立速度--全域性共享物件。 3,單例在系統中只存在一個物件例項,因此任何地方使用此物件都是同一個物件避免多例項建立使用時產生的邏輯錯誤。
程式碼實現# 方案一:重寫__new__方法實現單列class Singleton(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) return cls._instance# 方法二:使用裝飾器實現單列from threading import Lockdef singleton(cls): instances = {} instances_lock = Lock() def wrapper(*args, **kwargs): if cls not in instances: # 使用鎖保證只建立一個物件 with instances_lock: # {"類名": 類的物件} instances[cls] = cls(*args, **kwargs) # 下一次直接返回物件 return instances[cls] return wrapper# 裝飾完畢後這個類就是一個單列@singletonclass Foo(object): passfoo1 = Foo()foo2 = Foo()
單列在專案中應用# -*- coding:utf-8 -*-from info.lib.yuntongxun.CCPRestSDK import RESTimport sslssl._create_default_https_context = ssl._create_unverified_context#-----------------------需要將以下程式碼修改為自己賬號裡面的值-------------------------------------# 說明:主賬號,登陸雲通訊網站後,可在"控制檯-應用"中看到開發者主賬號ACCOUNT SID_accountSid = '8a216da85f5c89b1015f9be6a9a41d68'# 說明:主賬號Token,登陸雲通訊網站後,可在控制檯-應用中看到開發者主賬號AUTH TOKEN_accountToken = 'a5561334640043198099b9edcdcc86d5'# 請使用管理控制檯首頁的APPID或自己建立應用的APPID_appId = '8a216da85f5c89b1015f9be6ab121d6f'#-----------------------------------------------------------------------------------------------# 說明:請求地址,生產環境配置成app.cloopen.com_serverIP = 'app.cloopen.com'# 說明:請求埠 ,生產環境為8883_serverPort = "8883"# 說明:REST API版本號保持不變_softVersion = '2013-12-26'# 雲通訊官方提供的傳送簡訊程式碼例項--(未使用單列模型進行優化,每次傳送簡訊驗證都需要進行許可權驗證操作耗時)# def sendTemplateSMS(to, datas, tempId):# # 初始化REST SDK# # 許可權校驗# # 需要和雲通訊後臺進行網路通訊---耗時# rest = REST(serverIP, serverPort, softVersion)# rest.setAccount(accountSid, accountToken)# rest.setAppId(appId)## # 發簡訊# result = rest.sendTemplateSMS(to, datas, tempId)# for k, v in result.iteritems():# # if k == 'templateSMS':# for k, s in v.iteritems():# print '%s:%s' % (k, s)# else:# print '%s:%s' % (k, v)# 使用單列模型進行優化,只需要在第一次簡訊驗證碼的時候進行許可權驗證class CCP(object): """傳送簡訊的輔助類""" def __new__(cls, *args, **kwargs): # 判斷是否存在類屬性_instance,_instance是類CCP的唯一物件,即單例 if not hasattr(CCP, "_instance"): # 將客戶端和雲通訊的許可權鑑定操作封裝到單列中提高效能 # 父類初始化給物件賦值 cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs) # 許可權認證封裝到單列【提高效能】,判斷你是否是雲通訊的開發者 cls._instance.rest = REST(_serverIP, _serverPort, _softVersion) cls._instance.rest.setAccount(_accountSid, _accountToken) cls._instance.rest.setAppId(_appId) # 當CCP類身上有_instance屬性,直接返回 return cls._instance # CCP().send_template_sms() def send_template_sms(self, to, datas, temp_id): """傳送模板簡訊""" # @param to 手機號碼 # @param datas 內容資料 格式為陣列 例如:{'6位簡訊驗證碼值:123456', '5'},如不需替換請填 '' # @param temp_id 模板Id result = self.rest.sendTemplateSMS(to, datas, temp_id) print(result) # 如果雲通訊傳送簡訊成功,返回的字典資料result中statuCode欄位的值為"000000" if result.get("statusCode") == "000000": # 返回0 表示傳送簡訊成功 return 0 else: # 返回-1 表示傳送失敗 return -1if __name__ == '__main__': ccp = CCP() # 注意: 測試的簡訊模板編號為1 ccp.send_template_sms('185xxxxxxxx', ['1234', 5], 1)
python web 處理企業級電商業務中的秒殺功能:1. [秒殺]搶訂單環節一般會帶來2個問題:1.高併發:大量使用者同一時間搶購,網站瞬時訪問量劇增,導致伺服器壓力大 2.超賣: 成功下訂單買到商品的人數,超過資料庫最大庫存數量
2. [秒殺]解決方案:前端 [擴容,靜態化,限流]: A擴容: 加機器,這是最簡單的方法,通過增加前端池的整體承載量來抗峰值。 B:靜態化 將頁面能夠靜態化的元素全部靜態化,並減少動態元素,通過CDN來抗峰值 ESI: 在web伺服器上做動態內容請求,並將資料插入靜態頁面中,使用者拿到就一個完整的頁面,這種方案對伺服器端效能有影響,但是使用者體驗好 CSI:在靜態頁面中單獨傳送非同步js請求,從伺服器動態獲取資料,這種伺服器效果很好,但是使用者體驗稍差 C:限流 ip限流:針對某一個ip地址,限制單位時間內訪問次數 D:其他 在活動入口的地方設定關卡遊戲或者問題環節,削弱峰值
後端出現高併發和超賣的原因:I:首先MySQL自身對於高併發的處理效能就會出現問題,一般來說,MySQL的處理效能會隨著併發thread上升而上升,但是到了一定的併發度之後會出現明顯的拐點,之後一路下降,最終甚至會比單thread的效能還要差。 II:其次,超賣的根結在於減庫存操作是一個實務操作,需要先select,然後insert,最後update -1。最後這個-1操作是不能出現負數的,但是很多使用者在有庫存的情況下併發操作,出現負數這是無法避免的。 III:最後,當減庫存和高併發碰到一起的時候,由於操作的庫存數目在同一行,就會出現爭搶InnoDB行鎖的問題,導致出現互相等待甚至死鎖,從而大大降低MySQL的處理效能,最終導致前端頁面出現超時異常。
解決方案1:將存庫從MySQL前移到Redis中,所有的寫操作放到記憶體中,由於Redis中不存在鎖故不會出現互相等待,並且由於Redis的寫效能和讀效能都遠高於MySQL,這就解決了高併發下的效能問題。然後通過佇列等非同步手段,將變化的資料非同步寫入到DB中。 優點:解決效能問題 缺點:沒有解決超賣問題,同時由於非同步寫入DB,存在某一時刻DB和Redis中資料不一致的風險。
解決方案2:引入佇列,然後將所有寫DB操作在單佇列中排隊,完全序列處理。當達到庫存閥值的時候就不在消費佇列,並關閉購買功能。這就解決了超賣問題。 優點:解決超賣問題,略微提升效能。 缺點:效能受限於佇列處理機處理效能和DB的寫入效能中最短的那個,另外多商品同時搶購的時候需要準備多條佇列。
解決方案3:將寫操作前移到Memcached中,同時利用Memcached的輕量級的鎖機制CAS來實現減庫存操作。 優點:讀寫在記憶體中,操作效能快,引入輕量級鎖之後可以保證同一時刻只有一個寫入成功,解決減庫存問題。 缺點:沒有實測,基於CAS的特性不知道高併發下是否會出現大量更新失敗?不過加鎖之後肯定對併發效能會有影響。
解決方案4:將提交操作變成兩段式,先申請後確認。然後利用Redis的原子自增操作(相比較MySQL的自增來說沒有空洞),同時利用Redis的事務特性來發號,保證拿到小於等於庫存閥值的號的人都可以成功提交訂單。然後資料非同步更新到DB中。 優點:解決超賣問題,庫存讀寫都在記憶體中,故同時解決效能問題。 缺點:由於非同步寫入DB,可能存在資料不一致。另可能存在少買,也就是如果拿到號的人不真正下訂單,可能庫存減為0,但是訂單數並沒有達到庫存閥值
伺服器解決效能瓶頸問題1.排隊: 可以使用訊息佇列,將同步請求轉化成非同步請求,中間通過一個訊息佇列在一端[佇列入口]承接瞬時的流量峰值,在另一端[隊列出口]平滑的將訊息推送出去 2.設定關卡: 在活動入口的地方設定關卡遊戲或者問題環節,削弱峰值 3.分層過濾: 秒殺請求先經過CDN ==> 前端系統 ==> 後端系統 過濾掉無效請求