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