1.服務(wù)接口的冪等設(shè)計
冪等:每次請求重復(fù)調(diào)用的結(jié)果都是一樣的。
分布式服務(wù)中的提交接口一般都需要進行冪等校驗。例如做支付接口,如果一筆支付在網(wǎng)絡(luò)異常時進行兩次,或者更多次調(diào)用。沒有控制冪等的設(shè)計,那么可能會導(dǎo)致進行多次支付,而系統(tǒng)中只有一筆訂單,造成的影響不言而喻。
冪等性一般可以用一個請求號和請求來源來記錄,將請求號和請求來源存入redis里面,如果有重復(fù)的過來,就直接拒絕,也可以針對請求和請求來源做唯一索引,這樣帶有重復(fù)數(shù)據(jù)提交過來的時候就會落庫失敗。
當然也可能會有不同的請求號的重復(fù)提交,比如用戶第1次申請的時候,卡住了,然后用戶返回之后可以點擊第2次申請,這樣就會導(dǎo)致重復(fù)點擊了兩次支付。這樣一般的做法就是添加一個樂觀鎖機制,根據(jù)用戶這一筆錢的支付狀態(tài),或者是用戶的版本來進行比對,如果比對失敗,說明可能是重復(fù)的支付,那么就直接拒絕掉。
2.mq分布式系統(tǒng)的應(yīng)用,以及mq消息不丟失的設(shè)計
mq一般用來作為分布式系統(tǒng)的解耦合調(diào)用,系統(tǒng)的削峰填谷(入隊隊列滿之后,拒絕新的事件入隊,也可以適當避免系統(tǒng)低峰時間請求數(shù)量太少了而導(dǎo)致的系統(tǒng)資源浪費),系統(tǒng)的消息分發(fā),做延遲隊列來處理一些定時的任務(wù)等。
mq的類型也是多種多樣,如何選型也是比較關(guān)鍵的因素。例如最近很火的kafka,因為它的高吞吐量,零拷貝快速持久化消息著稱;rabbitmq,以它對多種消息傳輸協(xié)議,支持靈活的消息路由,負載均衡策略和保證消息不丟失的策略而廣受企業(yè)追捧;activemq,以它支持多種語言客戶端,能夠?qū)崿F(xiàn)多種高級應(yīng)用聞名;rocketmq,能夠支持大量的消息堆積,精準控制消息順序,消息過濾等多種功能......不同的mq應(yīng)用場景也不一樣,諸君需要根據(jù)系統(tǒng)屬性來合理選擇。
mq中如何確保消息不丟失?以rabbitmq為例。如果是生產(chǎn)者丟失消息,沒有正確的發(fā)送到mq中。為了確保消息不丟失,mq生產(chǎn)者發(fā)送的時候要設(shè)置持久化參數(shù),那么mq接收到生產(chǎn)者傳送過來的消息時,會首先把mq消息,持久化到磁盤,持久化完成之后才會返回,給生產(chǎn)者一個ack,生產(chǎn)者收到這個ack之后,說明消息已經(jīng)成功傳輸?shù)絤q上,否則生產(chǎn)者將會重復(fù)傳送這一條消息。消息傳到mq上以后,還要確保消息能夠成功的發(fā)送給消費者,且消費者能夠成功的處理完消息,因此可以設(shè)置消費者處理消息完成之后,再返回ack到mq,mq收到消費者回傳的ack之后才會將消息移出隊列,否則則會重復(fù)發(fā)送。
3.redis分布式系統(tǒng)應(yīng)用
數(shù)據(jù)緩存(通過redis緩存的數(shù)據(jù)可以更快的進行訪問,保證db不會因為壓力太大出現(xiàn)異常,同時可以增強系統(tǒng)吞吐量,但是需要準確的控制緩存數(shù)據(jù)與db數(shù)據(jù)之間的一致性,否則可能會造成臟讀。如何保證緩存命中率是個比較復(fù)雜的事情,需要根據(jù)系統(tǒng)屬性來對緩存屬性進行設(shè)計,同時也要兼顧到緩存穿透一類的問題)。
分布式鎖(使用redis的setnx函數(shù),利用此函數(shù)的原子特性,將需要鎖的對象唯一性的一些字段添入其中,設(shè)置過期時間。其他線程過來,發(fā)現(xiàn)對象被鎖之后,采用自旋的方式重復(fù)獲取對象一段時間,如果占用對象的線程釋放對象之后便可以獲取到對象鎖,否則直接拋出失?。?/p>
分布式冪等性接口(同樣使用setnx函數(shù)的特性,根據(jù)來源傳輸?shù)奈ㄒ恍哉埱筇杹磉M行加鎖)。
處理隊列(類似于用redis來實現(xiàn)一個mq隊列的功能,不過沒有mq那么完全的一個保證消息不丟失的機制,需要衡量利弊使用)。
bitmap(用來記錄一些大數(shù)據(jù)位的數(shù)據(jù)結(jié)構(gòu),可以用來做布隆過濾器,進行緩存條件的過濾,可以防止緩存穿透,緩存擊穿)。
4.線程及線程池的使用,種類,以及線程池的設(shè)計
放一張線程狀態(tài)轉(zhuǎn)換圖(清晰明了,相信不用我多做解釋):
線程池可以分為四大類(Executors工具類),單例線程池(顧名思義,是初始化核心線程和最大線程數(shù)只有一的線程池,一般用來對線程進入的先后順序進行依次執(zhí)行。),固定線程池(固定的核心線程數(shù)和最大線程數(shù),可以根據(jù)機器的核載量來決定線程池的大?。彺婢€程池(初始化核心線程數(shù)為0,阻塞隊列為同步的,且最大線程數(shù)不做限制的一個線程。這類線程時可以根據(jù)具體請求來決定線程池數(shù)量的大小,收放自如,但是不做最大線程數(shù)的限制,但是可能會引起機器壓力過大線程數(shù)過多等問題),定時任務(wù)線程池(創(chuàng)建一個定長的,可以按周期定時執(zhí)行的線程池)
線程池初始化的幾個參數(shù):如果選擇用自定義線程池ThreadPoolExecutor來初始化線程池(這種方式也是阿里推薦使用的,直接用Executors工具類可能會導(dǎo)致),需要注意的幾個參數(shù)(核心線程數(shù),最大線程數(shù),隊列選型,拒絕策略)。隊列類型:ArrayBlockingQueue,PriorityBlockingQueue:有界隊列,隊列滿時會創(chuàng)建新線程直到最大線程數(shù),隊列滿時按照拒絕策略返回;linkedBlockingQueue:無界隊列,隊列不限制大小,不過需要注意如果隊列一直增加會導(dǎo)致oom;SynchronousQueue:同步隊列,一般用于cache類型的線程池;拒絕策略:AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)。CallerRunsPolicy:由調(diào)用線程處理該任務(wù)。
以上就是長沙牛耳教育java培訓(xùn)機構(gòu)的小編針對“Java基礎(chǔ)學(xué)習(xí):Java并發(fā)程序設(shè)計教程”的內(nèi)容進行的回答,希望對大家有所幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。
Java基礎(chǔ)學(xué)習(xí)