id全局唯一且自增,如何實現(xiàn)?
Redis的 incr 和 increby 自增原子命令
統(tǒng)一數(shù)據(jù)庫的id發(fā)放
美團(tuán)Leaf Leaf——美團(tuán)點評分布式ID生成系統(tǒng)(批發(fā)號段)
Twitter的snowflake算法
UUID
★如何設(shè)計算法壓縮一段URL?
通過發(fā)號策略,給每一個過來的長地址,發(fā)一個號即可,小型系統(tǒng)直接用mysql的自增索引就搞定了。如果是大型應(yīng)用,可以考慮各種分布式key-value系統(tǒng)做發(fā)號器。不停的自增就行了。第一個使用這個服務(wù)的人得到的短地址是http://xx.xx/0?第二個是 http://xx.xx/1 第11個是 http://xx.xx/a 第依次往后,相當(dāng)于實現(xiàn)了一個62進(jìn)制的自增字段即可。
常用的url壓縮算法是短地址映射法。具體步驟是:
將長網(wǎng)址用md5算法生成32位簽名串,分為4段,,每段8個字符;
對這4段循環(huán)處理,取每段的8個字符, 將他看成16進(jìn)制字符串與0x3fffffff(30位1)的位與操作,超過30位的忽略處理;
將每段得到的這30位又分成6段,每5位的數(shù)字作為字母表的索引取得特定字符,依次進(jìn)行獲得6位字符串;
這樣一個md5字符串可以獲得4個6位串,取里面的任意一個就可作為這個長url的短url地址。
★Dubbo負(fù)載均衡策略?
隨機、輪詢、最少使用、一致性哈希(除了一致性哈希外,都有加權(quán))
負(fù)載均衡算法?
常見6種負(fù)載均衡算法:輪詢,隨機,源地址哈希,加權(quán)輪詢,加權(quán)隨機,最小連接數(shù)。
nginx5種負(fù)載均衡算法:輪詢,weight輪詢,ip_hash,fair(響應(yīng)時間),url_hash
dubbo負(fù)載均衡算法:隨機,輪詢,最少活躍調(diào)用數(shù),一致性Hash
Dubbo中Zookeeper做注冊中心,如果注冊中心集群都掛掉,發(fā)布者和訂閱者之間還能通信么?
可以,因為dubbo在注冊中心掛掉之后,會從原先的緩存中讀取連接地址。
★Dubbo完整的一次調(diào)用鏈路介紹?
調(diào)用方:
將方法名方法參數(shù)傳入InvokerInvocationHandler的invoke方法中,對于Object中的方法toString, hashCode, equals直接調(diào)用invoker的對應(yīng)方法。
然后進(jìn)入(故障轉(zhuǎn)移集群)MockClusterInvoker.invoke()方法中。三種調(diào)用策略:①不需要mock, 直接調(diào)用FailoverClusterInvoker。②強制mock,調(diào)用mock。③先調(diào)FailoverClusterInvoker,調(diào)用失敗在mock.
FailoverClusterInvoker默認(rèn)調(diào)用策略。①通過目錄服務(wù)查找到所有訂閱的服務(wù)提供者的Invoker對象。②路由服務(wù)根據(jù)策略(比如:容錯策略)來過濾選擇調(diào)用的Invokers。③通過負(fù)載均衡策略LoadBalance來選擇一個Invoker
執(zhí)行選擇的Invoker.invoker(invocation),經(jīng)過監(jiān)聽器鏈,經(jīng)過過濾器鏈,執(zhí)行到遠(yuǎn)程調(diào)用的DubboInvoker。
DubboInvoker根據(jù)url 也就是根據(jù)服務(wù)提供者的長連接,這里封裝成交互層對象ExchangeClient供這里調(diào)用,判斷遠(yuǎn)程調(diào)用類型同步,異步還是oneway模式。ExchangeClient發(fā)起遠(yuǎn)程調(diào)用。
6.獲取調(diào)用結(jié)果:①Oneway返回空RpcResult②異步,直接返回空RpcResult, ResponseFuture回調(diào)③同步, ResponseFuture模式同步轉(zhuǎn)異步,等待響應(yīng)返回
消費方:
通過Invocation獲取服務(wù)名和端口組成serviceKey=com.alibaba.dubbo.demo.DemoService:20880, 從DubboProtocol的exproterMap中獲取暴露服務(wù)的DubboExporter, 在從dubboExporter 獲取invoker返回
經(jīng)過過濾器鏈。
經(jīng)過監(jiān)聽器鏈。
到達(dá)執(zhí)行真正調(diào)用的invoker, 這個invoker由代理工廠ProxyFactory.getInvoker(demoService, DemoService.class, registryUrl)創(chuàng)建,具體請看代理那部分介紹。
調(diào)用demoService實例方法,將結(jié)果封裝成RpcResult返回。
★SpringCloud和Dubbo有什么不一樣?
1.dubbo采用RPC的方式交互,SpringCloud采用Http,restful協(xié)議進(jìn)行交互。
2.dubbo依賴zookeeper進(jìn)行服務(wù)注冊,Springloud自己擁有自己的服務(wù)注冊中心。
3.dubbo需要強依賴,需要持有相同的類或者jar包,springcloud弱依賴,但需要通過接口文檔進(jìn)行約束。
4.C數(shù)據(jù)一致性,A服務(wù)可用性,P服務(wù)對網(wǎng)絡(luò)分區(qū)故障的容錯性,Zookeeper 保證的是CP,euraka保證的是AP。
使用Redis如何實現(xiàn)分布式鎖?
setnx指令,設(shè)置鎖的有效時間防止死鎖。設(shè)置一個隨機值來標(biāo)識鎖的持有人,利用這個隨機值來釋放鎖。
Tomcat如何優(yōu)化?
虛擬機參數(shù):
server模式。
最大堆最小堆大小。
年輕代和老年代的比例。
開啟優(yōu)化。
使用偏向鎖。
gc年齡。
合適的gc
tomcat參數(shù):
maxThread。
minThread。
acceptCount。
connectionTimeout。
maxProcessors與minProcessors。
★冪等的處理方式?
查詢與刪除操作是天然冪等
唯一索引,防止新增臟數(shù)據(jù)
token機制,防止頁面重復(fù)提交
悲觀鎖 for update
樂觀鎖(通過版本號/時間戳實現(xiàn), 通過條件限制where avai_amount-#subAmount# >= 0)
分布式鎖
狀態(tài)機冪等(如果狀態(tài)機已經(jīng)處于下一個狀態(tài),這時候來了一個上一個狀態(tài)的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態(tài)機的冪等。)
select + insert(并發(fā)不高的后臺系統(tǒng),或者一些任務(wù)JOB,為了支持冪等,支持重復(fù)執(zhí)行)
redis存儲訂單號,如果已存在的話說明已經(jīng)處理過
后臺系統(tǒng)怎么防止用戶惡意頻繁訪問?
設(shè)計一個數(shù)據(jù)結(jié)構(gòu),有用戶id,當(dāng)前秒數(shù),調(diào)用次數(shù)。每次請求時對比當(dāng)前秒數(shù)和該對象內(nèi)的是否一致,一致的話累加調(diào)用次數(shù)。不一致的話,將當(dāng)前秒數(shù)替換成新的,調(diào)用次數(shù)清0。
★請談?wù)剢吸c登錄原理?
同域下的單點登錄,只需共享session即可。
登錄業(yè)務(wù)系統(tǒng),跳轉(zhuǎn)至SSO服務(wù)器,判斷用戶名密碼正確,在sso域下種下cookie,在session中標(biāo)記為登錄,返回一個ticket,跳轉(zhuǎn)到業(yè)務(wù)系統(tǒng),業(yè)務(wù)系統(tǒng)再拿這個ticket跑去SSO服務(wù)器驗證ticket是否有效,有效的話,在業(yè)務(wù)系統(tǒng)session中設(shè)置為已登錄即可。
相比于單系統(tǒng)登錄,sso需要一個獨立的認(rèn)證中心,只有認(rèn)證中心能接受用戶的用戶名密碼等安全信息,其他系統(tǒng)不提供登錄入口,只接受認(rèn)證中心的間接授權(quán)。間接授權(quán)通過令牌實現(xiàn),sso認(rèn)證中心驗證用戶的用戶名密碼沒問題,創(chuàng)建授權(quán)令牌,在接下來的跳轉(zhuǎn)過程中,授權(quán)令牌作為參數(shù)發(fā)送給各個子系統(tǒng),子系統(tǒng)拿到令牌,即得到了授權(quán),可以借此創(chuàng)建局部會話,局部會話登錄方式與單系統(tǒng)的登錄方式相同。
單點登錄自然也要單點注銷,在一個子系統(tǒng)中注銷,所有子系統(tǒng)的會話都將被銷毀。
★談?wù)勴椖恐蟹植际绞聞?wù)應(yīng)用場景?
學(xué)勤系統(tǒng)與賬戶余額系統(tǒng)
招生系統(tǒng)與賬戶余額系統(tǒng)
★MQ和數(shù)據(jù)庫的一致性問題,MQ消息最終一致性。
事務(wù)消息與普通消息的區(qū)別就在于消息生產(chǎn)環(huán)節(jié),生產(chǎn)者首先預(yù)發(fā)送一條消息到MQ(這也被稱為發(fā)送half消息)
MQ接受到消息后,先進(jìn)行持久化,則存儲中會新增一條狀態(tài)為待發(fā)送的消息
然后返回ACK給消息生產(chǎn)者,此時MQ不會觸發(fā)消息推送事件
生產(chǎn)者預(yù)發(fā)送消息成功后,執(zhí)行本地事務(wù)
執(zhí)行本地事務(wù),執(zhí)行完成后,發(fā)送執(zhí)行結(jié)果給MQ
MQ會根據(jù)結(jié)果刪除或者更新消息狀態(tài)為可發(fā)送
如果消息狀態(tài)更新為可發(fā)送,則MQ會push消息給消費者,后面消息的消費和普通消息是一樣的
★正在處理的隊列突然斷電怎么辦?
正在處理的實現(xiàn)事務(wù)功能,下次自動回滾。
隊列實現(xiàn)持久化儲存,下次啟動自動載入。
添加標(biāo)志位,未處理 0,處理中 1,已處理 2。每次啟動的時候,把所有狀態(tài)為 1 的,置為 0。
關(guān)鍵性的應(yīng)用就給電腦配個 UPS。
★服務(wù)限流的方式
漏桶:水(請求)先進(jìn)入到漏桶里,漏桶以一定的速度出水(接口有響應(yīng)速率),當(dāng)水流入速度過大會直接溢出(訪問頻率超過接口響應(yīng)速率),然后就拒絕請求。
令牌桶算法:系統(tǒng)會按恒定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶里加入Token,如果桶已經(jīng)滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token就拒絕服務(wù)。
基于redis實現(xiàn)的限流:假設(shè)每分鐘訪問次數(shù)不能超過10次,在Redis中創(chuàng)建一個鍵,過期60秒,對此服務(wù)接口的訪問就把鍵值加1,在60秒內(nèi)增加到10的時候,禁止訪問服務(wù)接口。
計數(shù)器,滑動窗口(假設(shè)窗口為10s,則建立一個大小為10的數(shù)組,然后每次讓當(dāng)前秒數(shù)除10,落到哪個格子就累加,每一時刻數(shù)組的和就是窗口的數(shù)值)
令牌桶可以應(yīng)對突發(fā)的大流量
漏斗算法用于請求恒定速率通過
RabbitMQ消息堆積怎么處理?
新建一個topic,partition是原來的10倍;然后寫一個臨時的分發(fā)數(shù)據(jù)的consumer程序,這個程序部署上去消費積壓的數(shù)據(jù),消費之后不做耗時的處理,直接均勻輪詢寫入臨時建立好的10倍數(shù)量的queue;接著臨時征用10倍的機器來部署consumer,每一批consumer消費一個臨時queue的數(shù)據(jù);等快速消費完積壓數(shù)據(jù)之后,得恢復(fù)原先部署架構(gòu),重新用原先的consumer機器來消費消息。
★kafka消息會不會丟失?
Kafka消息發(fā)送分同步(sync)、異步(async)兩種方式。默認(rèn)是使用同步方式,可通過producer.type屬性進(jìn)行配置;Kafka保證消息被安全生產(chǎn),有三個選項分別是0,1,-1。
通過request.required.acks屬性進(jìn)行配置:
0代表:不進(jìn)行消息接收是否成功的確認(rèn)(默認(rèn)值);
1代表:當(dāng)Leader副本接收成功后,返回接收成功確認(rèn)信息;
-1代表:當(dāng)Leader和Follower副本都接收成功后,返回接收成功確認(rèn)信息;
網(wǎng)絡(luò)異常:
acks設(shè)置為0時,不和Kafka集群進(jìn)行消息接受確認(rèn),當(dāng)網(wǎng)絡(luò)發(fā)生異常等情況時,存在消息丟失的可能;
客戶端異常:
異步發(fā)送時,消息并沒有直接發(fā)送至Kafka集群,而是在Client端按一定規(guī)則緩存并批量發(fā)送。在這期間,如果客戶端發(fā)生死機等情況,都會導(dǎo)致消息的丟失;
緩沖區(qū)滿了:
異步發(fā)送時,Client端緩存的消息超出了緩沖池的大小,也存在消息丟失的可能;
Leader副本異常:
acks設(shè)置為1時,Leader副本接收成功,Kafka集群就返回成功確認(rèn)信息,而Follower副本可能還在同步。這時Leader副本突然出現(xiàn)異常,新Leader副本(原Follower副本)未能和其保持一致,就會出現(xiàn)消息丟失的情況;
以上就是消息丟失的幾種情況,在日常應(yīng)用中,我們需要結(jié)合自身的應(yīng)用場景來選擇不同的配置。
想要更高的吞吐量就設(shè)置:異步、ack=0;想要不丟失消息數(shù)據(jù)就選:同步、ack=-1策略。
★RabbitMQ的消息丟失解決方案?
消息持久化:Exchange 設(shè)置持久化:durable:true;
Queue 設(shè)置持久化;
Message持久化發(fā)送。
ACK確認(rèn)機制:消息發(fā)送確認(rèn);消息接收確認(rèn)。
★kafka的leader副本選舉?
如果某個分區(qū)patition的Leader掛了,那么其它跟隨者將會進(jìn)行選舉產(chǎn)生一個新的leader,之后所有的讀寫就會轉(zhuǎn)移到這個新的Leader上,在kafka中,其不是采用常見的多數(shù)選舉的方式進(jìn)行副本的Leader選舉,而是會在Zookeeper上針對每個Topic維護(hù)一個稱為ISR(in-sync replica,已同步的副本)的集合,顯然還有一些副本沒有來得及同步。只有這個ISR列表里面的才有資格成為leader(先使用ISR里面的第一個,如果不行依次類推,因為ISR里面的是同步副本,消息是最完整且各個節(jié)點都是一樣的)。
通過ISR,kafka需要的冗余度較低,可以容忍的失敗數(shù)比較高。假設(shè)某個topic有f+1個副本,kafka可以容忍f個不可用,當(dāng)然,如果全部ISR里面的副本都不可用,也可以選擇其他可用的副本,只是存在數(shù)據(jù)的不一致。、
kafka消息的檢索?
其實很簡單主要是用二分查找算法,比如我們要查找一條offest=10000的文件,kafka首先會在對應(yīng)分區(qū)下的log文件里采用二分查看定位到某個記錄該offest
=10000這條消息的log,然后從相應(yīng)的index文件定位其偏移量,然后拿著偏移量到log里面直接獲取。這樣就完成了一個消息的檢索過程。
★RabbitMQ 集群方式?
普通集群:
以兩個節(jié)點(rabbit01、rabbit02)為例來進(jìn)行說明。rabbit01和rabbit02兩個節(jié)點僅有相同的元數(shù)據(jù),即隊列的結(jié)構(gòu),但消息實體只存在于其中一個節(jié)點rabbit01(或者rabbit02)中。
當(dāng)消息進(jìn)入rabbit01節(jié)點的Queue后,consumer從rabbit02節(jié)點消費時,RabbitMQ會臨時在rabbit01、rabbit02間進(jìn)行消息傳輸,把A中的消息實體取出并經(jīng)過B發(fā)送給consumer。所以consumer應(yīng)盡量連接每一個節(jié)點,從中取消息。即對于同一個邏輯隊列,要在多個節(jié)點建立物理Queue。否則無論consumer連rabbit01或rabbit02,出口總在rabbit01,會產(chǎn)生瓶頸。當(dāng)rabbit01節(jié)點故障后,rabbit02節(jié)點無法取到rabbit01節(jié)點中還未消費的消息實體。如果做了消息持久化,那么得等rabbit01節(jié)點恢復(fù),然后才可被消費;如果沒有持久化的話,就會產(chǎn)生消息丟失的現(xiàn)象。
鏡像集群:
在普通集群的基礎(chǔ)上,把需要的隊列做成鏡像隊列,消息實體會主動在鏡像節(jié)點間同步,而不是在客戶端取數(shù)據(jù)時臨時拉取,也就是說多少節(jié)點消息就會備份多少份。該模式帶來的副作用也很明顯,除了降低系統(tǒng)性能外,如果鏡像隊列數(shù)量過多,加之大量的消息進(jìn)入,集群內(nèi)部的網(wǎng)絡(luò)帶寬將會被這種同步通訊大大消耗掉。所以在對可靠性要求較高的場合中適用
由于鏡像隊列之間消息自動同步,且內(nèi)部有選舉master機制,即使master節(jié)點宕機也不會影響整個集群的使用,達(dá)到去中心化的目的,從而有效的防止消息丟失及服務(wù)不可用等問題
★kafka高性能的原因?
Broker NIO異步消息處理,實現(xiàn)了IO線程與業(yè)務(wù)線程分離;
磁盤順序?qū)懀?/p>
零拷貝(跳過用戶緩沖區(qū)的拷貝,建立一個磁盤空間和內(nèi)存的直接映射,數(shù)據(jù)不再復(fù)制到用戶態(tài)緩沖區(qū));
分區(qū)/分段(每次文件操作都是對一個小文件的操作,非常輕便,同時也增加了并行處理能力);
批量發(fā)送 (可以指定緩存的消息達(dá)到某個量的時候就發(fā)出去,或者緩存了固定的時間后就發(fā)送出去,大大減少服務(wù)端的I/O次數(shù))
數(shù)據(jù)壓縮
★ZooKeeper分布式高可用
ZooKeeper 運行期間,集群中至少有過半的機器保存了最新數(shù)據(jù)。集群超過半數(shù)的機器能夠正常工作,集群就能夠?qū)ν馓峁┓?wù)。
zookeeper可以選出N臺機器作主機,它可以實現(xiàn)M:N的備份;keepalive只能選出1臺機器作主機,所以keepalive只能實現(xiàn)M:1的備份。
通常有以下兩種部署方案:雙機房部署(一個穩(wěn)定性更好、設(shè)備更可靠的機房,這個機房就是主要機房,而另外一個機房則更加廉價一些,例如,對于一個由 7 臺機器組成的 ZooKeeper 集群,通常在主要機房中部署 4 臺機器,剩下的 3 臺機器部署到另外一個機房中);三機房部署(無論哪個機房發(fā)生了故障,剩下兩個機房的機器數(shù)量都超過半數(shù)。在三個機房中都部署若干個機器來組成一個 ZooKeeper 集群。假設(shè)機器總數(shù)為 N,各機房機器數(shù):N1 = (N-1)/2 ,N2=1~(N-N1)/2 ,N3 = N - N1 - N2 )。
水平擴(kuò)容就是向集群中添加更多機器,Zookeeper2種方式(不完美),一種是集群整體重啟,另外一種是逐臺進(jìn)行服務(wù)器的重啟。
★如何設(shè)計秒殺
對于大促時候的秒殺活動,一般運營會配置靜態(tài)的活動頁面,配置靜態(tài)活動頁面主要有兩個目的一方面是為了便于在各種社交媒體分發(fā),另一方面是因為秒殺活動頁的流量是大促期間最大的,通過配置成靜態(tài)頁面可以將頁面發(fā)布在公有云上動態(tài)的橫向擴(kuò)展;
將秒殺活動的靜態(tài)頁面提前刷新到CDN節(jié)點,通過CDN節(jié)點的頁面緩存來緩解訪問壓力和公司網(wǎng)絡(luò)帶寬,CDN上緩存js、css和圖片;
將活動H5頁面部署在公有云的web server上,使用公有云最大的好處就是能夠根據(jù)活動的火爆程度動態(tài)擴(kuò)容而且成本較低,同時將訪問壓力隔離在公司系統(tǒng)外部;
在提供真正商品秒殺業(yè)務(wù)功能的app server上,需要進(jìn)行交易限流、熔斷控制,防止因為秒殺交易影響到其他正常服務(wù)的提供,我們在限流和熔斷方面使用了hystrix,在核心交易的controller層通過hystrix進(jìn)行交易并發(fā)限流控制,當(dāng)交易流量超出我們設(shè)定的限流最大值時,會對新交易進(jìn)行熔斷處理固定返回靜態(tài)失敗報文。
服務(wù)降級處理,除了上面講到的限流和熔斷控制,我們還設(shè)定了降級開關(guān),對于首頁、購物車、訂單查詢、大數(shù)據(jù)等功能都會進(jìn)行一定程度的服務(wù)降級,例如我們會對首頁原先動態(tài)生成的大數(shù)據(jù)頁面布局降級為所有人看到的是一樣的頁面、購物車也會降級為不在一級頁面的tabbar上的購物車圖標(biāo)上顯示商品數(shù)量、歷史訂單的查詢也會提供時間周期較短的查詢、大數(shù)據(jù)商品推薦也會提供一樣的商品推薦,通過這樣的降級處理能夠很好的保證各個系統(tǒng)在大促期間能夠正常的提供最基本的服務(wù),保證用戶能夠正常下單完成付款。
上面介紹的都是如何保證能扛住高并發(fā),下面介紹下整個方案中如何防止超賣現(xiàn)象的發(fā)生,我們?nèi)粘5南聠芜^程中防止超賣一般是通過在數(shù)據(jù)庫上實施樂觀鎖來完成,使用樂觀鎖雖然比for update這種悲觀鎖方式性能要好很多,但是還是無法滿足秒殺的上萬并發(fā)需求,我們的方案其實也很簡單實時庫存的扣減在緩存中進(jìn)行,異步扣減數(shù)據(jù)庫中的庫存,保證緩存中和數(shù)據(jù)庫中庫存的最終一致性。
在這個方案中我們使用的分布式緩存是redis,使用了codis集群方案穩(wěn)定性和高可用方面還是比較有保證的,因為redis是單線程寫,所以也不用擔(dān)心線程安全的問題,redis自身就能夠保證數(shù)據(jù)的強一致性,在下單的事務(wù)中包含了實時扣減緩存中的庫存和異步發(fā)送隊列,由隊列處理器再異步從隊列中取出訂單根據(jù)訂單信息扣減庫存系統(tǒng)數(shù)據(jù)庫中的商品數(shù)量。
高性能統(tǒng)計UV的方式?
(1)使用redis的set集合
(2)使用redis的bitmap(注意內(nèi)存消耗)
★緩存擊穿的解決辦法
加載DB時同步,其他則等待;DB端做SQL合并,Queue合并排隊處理;
部分緩存設(shè)置為永不過期;
讀取數(shù)據(jù)時候則等待500ms,500ms緩存應(yīng)該已經(jīng)加載完成;
★后臺系統(tǒng)怎么防止請求重復(fù)提交
前端js,控制按鈕。前端放置令牌。
數(shù)據(jù)庫唯一索引。redis看key是否存在。或者數(shù)據(jù)庫字段狀態(tài)。
有沒有遇到進(jìn)線上GC,出現(xiàn)的癥狀是什么樣的,怎么解決的?
利用堆快照,查看到底是哪些對象占用大量內(nèi)存導(dǎo)致經(jīng)常gc
★項目出現(xiàn)性能瓶頸了,會是哪些方面,怎么解決?
DB層面,有可能是sql,索引,表過大,數(shù)據(jù)庫壓力。
緩存層面:有可能緩存命中率差,redis性能瓶頸,需要擴(kuò)容
服務(wù)器壓力:服務(wù)器處理瓶頸
Java層面:代碼寫法
前端層面:cdn壓力,頁面壓力
情景題:如果一個外賣配送單子要發(fā)布,現(xiàn)在有200個騎手都想要接這一單,如何保證只有一個騎手接到單子?
分布式鎖,或者冪等接口,CAS樂觀鎖
場景題:美團(tuán)首頁每天會從10000個商家里面推薦50個商家置頂,每個商家有一個權(quán)值,你如何來推薦?第二天怎么更新推薦的商家?
可以借鑒下stackoverflow,視頻網(wǎng)站等等的推薦算法。
場景題:微信搶紅包問題
悲觀鎖,樂觀鎖,存儲過程放在mysql數(shù)據(jù)庫中。
場景題:1000個任務(wù),分給10個人做,你怎么分配,先在紙上寫個最簡單的版本,然后優(yōu)化。
全局隊列,把1000任務(wù)放在一個隊列里面,然后每個人都是取,完成任務(wù)。
分為10個隊列,每個人分別到自己對應(yīng)的隊列中去取務(wù)。
分布式服務(wù)如何跟蹤?
調(diào)用可以實現(xiàn)跟蹤系統(tǒng),可以在業(yè)務(wù)日志中添加調(diào)用鏈ID,各個環(huán)節(jié)RPC均添加調(diào)用時延,QPS等。
非業(yè)務(wù)組件應(yīng)該少加入業(yè)務(wù)代碼,服務(wù)調(diào)用采用買點,也會采用配置采樣率方式,買點即當(dāng)前節(jié)點的上下文信息,包含TraceId,RPCId,開始結(jié)束時間,類型,協(xié)議,調(diào)用方IP,端口,服務(wù)名等,以及其他異常信息,報文等擴(kuò)展,日志采用離線+實時的如flume結(jié)合kafka等,應(yīng)按照TraceId匯總?cè)罩竞蟀碦PCId順序整理。
Sentinel 工作原理?
每個 Sentinel 以每秒鐘一次的頻率向它所知的 Master,Slave 以及其他 Sentinel 實例發(fā)送一個 PING 命令;
2 如果一個實例(instance)距離最后一次有效回復(fù) PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被 Sentinel 標(biāo)記為主觀下線;
如果一個 Master 被標(biāo)記為主觀下線,則正在監(jiān)視這個 Master 的所有 Sentinel 要以每秒一次的頻率確認(rèn) Master 的確進(jìn)入了主觀下線狀態(tài);
當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定的值)在指定的時間范圍內(nèi)確認(rèn) Master 的確進(jìn)入了主觀下線狀態(tài),則 Master 會被標(biāo)記為客觀下線;
在一般情況下, 每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有 Master,Slave 發(fā)送 INFO 命令;當(dāng) Master 被 Sentinel 標(biāo)記為客觀下線時,Sentinel 向下線的 Master 的所有 Slave 發(fā)送 INFO 命令的頻率會從 10 秒一次改為每秒一次;
若沒有足夠數(shù)量的 Sentinel 同意 Master 已經(jīng)下線, Master 的客觀下線狀態(tài)就會被移除;
若 Master 重新向 Sentinel 的 PING 命令返回有效回復(fù), Master 的主觀下線狀態(tài)就會被移除。
redis的主從?
監(jiān)控( Monitoring ):Redis Sentinel 實時監(jiān)控主服務(wù)器和從服務(wù)器運行狀態(tài);
自動故障轉(zhuǎn)移:如果一個 master 不正常運行了,哨兵可以啟動一個故障轉(zhuǎn)移進(jìn)程,將一個 slave 升級成為 master,其他的 slave 被重新配置使用新的 master,并且應(yīng)用程序使用 Redis 服務(wù)端通知的新地址;
講講分布式唯一ID。
雪花算法:
1 bit:不用,為啥呢?因為二進(jìn)制里第一個 bit 為如果是 1,那么都是負(fù)數(shù),但是我們生成的 id 都是正數(shù),所以第一個 bit 統(tǒng)一都是 0。
41 bit:表示的是時間戳,單位是毫秒。41 bit 可以表示的數(shù)字多達(dá) 2^41 - 1,也就是可以標(biāo)識 2^41 - 1 個毫秒值,換算成年就是表示69年的時間。
10 bit:記錄工作機器 id,代表的是這個服務(wù)最多可以部署在 2^10臺機器上哪,也就是1024臺機器。但是 10 bit 里 5 個 bit 代表機房 id,5 個 bit 代表機器 id。意思就是最多代表 2^5個機房(32個機房),每個機房里可以代表 2^5 個機器(32臺機器)。
12 bit:這個是用來記錄同一個毫秒內(nèi)產(chǎn)生的不同 id,12 bit 可以代表的最大正整數(shù)是 2^12 - 1 = 4096,也就是說可以用這個 12 bit 代表的數(shù)字來區(qū)分同一個毫秒內(nèi)的 4096 個不同的 id。
★什么是一致性hash?
利用哈希環(huán)進(jìn)行一致性哈希
★如何使用redis和zookeeper實現(xiàn)分布式鎖?有什么區(qū)別優(yōu)缺點,會有什么問題,分別適用什么場景。(延伸:如果知道redlock,講講他的算法實現(xiàn),爭議在哪里)
Redis實現(xiàn)比較復(fù)雜,流程如下:
根據(jù)lockKey區(qū)進(jìn)行setnx(set not exist,顧名思義,如果key值為空,則正常設(shè)置,返回1,否則不會進(jìn)行設(shè)置并返回0)操作,如果設(shè)置成功,表示已經(jīng)獲得鎖,否則并沒有獲取鎖。
如果沒有獲得鎖,去Redis上拿到該key對應(yīng)的值,在該key上我們存儲一個時間戳(用毫秒表示,t1),為了避免死鎖以及其他客戶端占用該鎖超過一定時間(5秒),使用該客戶端當(dāng)前時間戳,與存儲的時間戳作比較。
如果沒有超過該key的使用時限,返回false,表示其他人正在占用該key,不能強制使用;如果已經(jīng)超過時限,那我們就可以進(jìn)行解鎖,使用我們的時間戳來代替該字段的值。
但是如果在setnx失敗后,get該值卻無法拿到該字段時,說明操作之前該鎖已經(jīng)被釋放,這個時候,最好的辦法就是重新執(zhí)行一遍setnx方法來獲取其值以獲得該鎖。
缺點:有可能master崩潰,導(dǎo)致多節(jié)點獲取到鎖。
從實現(xiàn)難度上來說,Zookeeper實現(xiàn)非常簡單,實現(xiàn)分布式鎖的基本邏輯:
客戶端調(diào)用create()方法創(chuàng)建名為“l(fā)ocknode/guid-lock-”的節(jié)點,需要注意的是,這里節(jié)點的創(chuàng)建類型需要設(shè)置為EPHEMERAL_SEQUENTIAL。
客戶端調(diào)用getChildren(“l(fā)ocknode”)方法來獲取所有已經(jīng)創(chuàng)建的子節(jié)點。
客戶端獲取到所有子節(jié)點path之后,如果發(fā)現(xiàn)自己在步驟1中創(chuàng)建的節(jié)點是所有節(jié)點中序號最小的,那么就認(rèn)為這個客戶端獲得了鎖。
如果創(chuàng)建的節(jié)點不是所有節(jié)點中需要最小的,那么則監(jiān)視比自己創(chuàng)建節(jié)點的序列號小的最大的節(jié)點,進(jìn)入等待。直到下次監(jiān)視的子節(jié)點變更的時候,再進(jìn)行子節(jié)點的獲取,判斷是否獲取鎖。
區(qū)別:
Redis分布式鎖,需要自己不斷去嘗試獲取鎖,比較消耗性能
ZooKeeper分布式鎖,獲取不到鎖,注冊個監(jiān)聽器即可,不需要不斷主動嘗試獲取鎖,性能開銷較小
如果Redis獲取鎖的那個客戶端掛了,那么只能等待超時時間之后才能釋放鎖
而對于ZooKeeper,因為創(chuàng)建的是臨時znode,只要客戶端掛了,znode就沒了,此時就自動釋放鎖
redlock算法實現(xiàn):
假設(shè)有5個完全獨立的redis主服務(wù)器
獲取當(dāng)前時間戳
client嘗試按照順序使用相同的key,value獲取所有redis服務(wù)的鎖,在獲取鎖的過程中的獲取時間比鎖過期時間短很多,這是為了不要過長時間等待已經(jīng)關(guān)閉的redis服務(wù)。并且試著獲取下一個redis實例。比如:TTL為5s,設(shè)置獲取鎖最多用1s,所以如果一秒內(nèi)無法獲取鎖,就放棄獲取這個鎖,從而嘗試獲取下個鎖
client通過獲取所有能獲取的鎖后的時間減去第一步的時間,這個時間差要小于TTL時間并且至少有3個redis實例成功獲取鎖,才算真正的獲取鎖成功
如果成功獲取鎖,則鎖的真正有效時間是 TTL減去第三步的時間差 的時間;比如:TTL 是5s,獲取所有鎖用了2s,則真正鎖有效時間為3s(其實應(yīng)該再減去時鐘漂移);
如果客戶端由于某些原因獲取鎖失敗,便會開始解鎖所有redis實例;因為可能已經(jīng)獲取了小于3個鎖,必須釋放,否則影響其他client獲取鎖
redlock的爭議點:(fgc導(dǎo)致的問題)
對于提升效率的場景下,RedLock 太重。
對于對正確性要求極高的場景下,RedLock 并不能保證正確性。
2pc 3pc 的區(qū)別,解決了哪些問題。
3pc 將2pc中的一階段拆為 canCommit和prepareCommit
二階段提交有幾個缺點:
同步阻塞問題。執(zhí)行過程中,所有參與節(jié)點都是事務(wù)阻塞型的。當(dāng)參與者占有公共資源時,其他第三方節(jié)點訪問公共資源不得不處于阻塞狀態(tài)。單點故障。由于協(xié)調(diào)者的重要性,一旦協(xié)調(diào)者發(fā)生故障。參與者會一直阻塞下去。尤其在第二階段,協(xié)調(diào)者發(fā)生故障,那么所有的參與者還都處于鎖定事務(wù)資源的狀態(tài)中,而無法繼續(xù)完成事務(wù)操作。(如果是協(xié)調(diào)者掛掉,可以重新選舉一個協(xié)調(diào)者,但是無法解決因為協(xié)調(diào)者宕機導(dǎo)致的參與者處于阻塞狀態(tài)的問題)數(shù)據(jù)不一致。在二階段提交的階段二中,當(dāng)協(xié)調(diào)者向參與者發(fā)送commit請求之后,發(fā)生了局部網(wǎng)絡(luò)異常或者在發(fā)送commit請求過程中協(xié)調(diào)者發(fā)生了故障,這回導(dǎo)致只有一部分參與者接受到了commit請求。而在這部分參與者接到commit請求之后就會執(zhí)行commit操作。但是其他部分未接到commit請求的機器則無法執(zhí)行事務(wù)提交。于是整個分布式系統(tǒng)便出現(xiàn)了數(shù)據(jù)部一致性的現(xiàn)象。
二階段無法解決的問題:協(xié)調(diào)者再發(fā)出commit消息之后宕機,而唯一接收到這條消息的參與者同時也宕機了。那么即使協(xié)調(diào)者通過選舉協(xié)議產(chǎn)生了新的協(xié)調(diào)者,這條事務(wù)的狀態(tài)也是不確定的,沒人知道事務(wù)是否被已經(jīng)提交。
3pc比2pc 減少事務(wù)阻塞范圍 。3pc在超時后會自動提交。相對于2PC,3PC主要解決的單點故障問題,并減少阻塞,因為一旦參與者無法及時收到來自協(xié)調(diào)者的信息之后,他會默認(rèn)執(zhí)行commit。
什么是paxos算法, 什么是zab協(xié)議?
paxos算法的推導(dǎo):
如果只有一個人投票的話,那么每個人必須接受第一個提議。
這樣又會導(dǎo)致三個人分別投不同的票,形不成大多數(shù)。
推導(dǎo)出可以選多次票,但多次票有可能投自己又投別人,形成多個大多數(shù),所以又加了限制條件,只能同意之前同意過的。
再推導(dǎo)出當(dāng)兩個機子重連的話,機子必須接受第一個發(fā)給他的提案,這樣就違背了之前選好的。
所以當(dāng)服務(wù)器重連的時候,必須發(fā)給他之前同意好的提案。
zab協(xié)議:
原子廣播:
ZAB 協(xié)議的消息廣播過程使用的是一個原子廣播協(xié)議,類似一個 二階段提交過程。對于客戶端發(fā)送的寫請求,全部由 Leader 接收,Leader 將請求封裝成一個事務(wù) Proposal,將其發(fā)送給所有 Follwer ,然后,根據(jù)所有 Follwer 的反饋,如果超過半數(shù)成功響應(yīng),則執(zhí)行 commit 操作(先提交自己,再發(fā)送 commit 給所有 Follwer)。
崩潰恢復(fù):
針對這些問題,ZAB 定義了 2 個原則:
ZAB 協(xié)議確保那些已經(jīng)在 Leader 提交的事務(wù)最終會被所有服務(wù)器提交。
ZAB 協(xié)議確保丟棄那些只在 Leader 提出/復(fù)制,但沒有提交的事務(wù)。
如果讓 Leader 選舉算法能夠保證新選舉出來的 Leader 服務(wù)器擁有集群總所有機器編號(即 ZXID 最大)的事務(wù),那么就能夠保證這個新選舉出來的 Leader 一定具有所有已經(jīng)提交的提案。
當(dāng) Follower 鏈接上 Leader 之后,Leader 服務(wù)器會根據(jù)自己服務(wù)器上最后被提交的 ZXID 和 Follower 上的 ZXID 進(jìn)行比對,比對結(jié)果要么回滾,要么和 Leader 同步。
★Dubbo的原理
數(shù)據(jù)怎么流轉(zhuǎn)的,怎么實現(xiàn)集群,負(fù)載均衡,服務(wù)注冊和發(fā)現(xiàn),重試轉(zhuǎn)發(fā),快速失敗的策略是怎樣的 。
第一層:service 層,接口層,給服務(wù)提供者和消費者來實現(xiàn)的
第二層:config 層,配置層,主要是對 dubbo 進(jìn)行各種配置的
第三層:proxy 層,服務(wù)代理層,無論是 consumer 還是 provider,dubbo 都會給你生成代理,代理之間進(jìn)行網(wǎng)絡(luò)通信
第四層:registry 層,服務(wù)注冊層,負(fù)責(zé)服務(wù)的注冊與發(fā)現(xiàn)
第五層:cluster 層,集群層,封裝多個服務(wù)提供者的路由以及負(fù)載均衡,將多個實例組合成一個服務(wù)
第六層:monitor 層,監(jiān)控層,對 rpc 接口的調(diào)用次數(shù)和調(diào)用時間進(jìn)行監(jiān)控
第七層:protocal 層,遠(yuǎn)程調(diào)用層,封裝 rpc 調(diào)用
第八層:exchange 層,信息交換層,封裝請求響應(yīng)模式,同步轉(zhuǎn)異步
第九層:transport 層,網(wǎng)絡(luò)傳輸層,抽象 mina 和 netty 為統(tǒng)一接口
第十層:serialize 層,數(shù)據(jù)序列化層
★一次RPC請求的流程是什么。
服務(wù)消費方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù);
client stub接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
client stub找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;
server stub收到消息后進(jìn)行解碼;
server stub根據(jù)解碼結(jié)果調(diào)用本地的服務(wù);
本地服務(wù)執(zhí)行并將結(jié)果返回給server stub;
server stub將返回結(jié)果打包成消息并發(fā)送至消費方;
client stub接收到消息,并進(jìn)行解碼;
服務(wù)消費方得到最終結(jié)果。
★解釋什么是MESI協(xié)議(緩存一致性)。
MESI協(xié)議保證了每個緩存中使用的共享變量的副本是一致的。它核心的思想是:當(dāng)CPU寫數(shù)據(jù)時,如果發(fā)現(xiàn)操作的變量是共享變量,即在其他CPU中也存在該變量的副本,會發(fā)出信號通知其他CPU將該變量的緩存行置為無效狀態(tài),因此當(dāng)其他CPU需要讀取這個變量時,發(fā)現(xiàn)自己緩存中緩存該變量的緩存行是無效的,那么它就會從內(nèi)存重新讀取。
(另外一種硬件層面的解決是總線鎖)
Zookeeper的用途,選舉的原理是什么,適用場景?
用途:類似文件系統(tǒng)的分布式協(xié)調(diào)服務(wù)。
選舉原理:
Zookeeper集群中只有超過半數(shù)以上的服務(wù)器啟動,集群才能正常工作;
在集群正常工作之前,myid小的服務(wù)器給myid大的服務(wù)器投票,直到集群正常工作,選出Leader;
選出Leader之后,之前的服務(wù)器狀態(tài)由Looking改變?yōu)镕ollowing,以后的服務(wù)器都是Follower。
適用場景:
命名服務(wù)
配置管理
集群管理
分布式鎖
隊列管理
Zookeeper watch機制原理?
客戶端注冊Watcher到服務(wù)端;
服務(wù)端發(fā)生數(shù)據(jù)變更;
服務(wù)端通知客戶端數(shù)據(jù)變更;
客戶端回調(diào)Watcher處理變更應(yīng)對邏輯。
什么叫數(shù)據(jù)一致性,你怎么理解數(shù)據(jù)一致性?
一致性又可以分為強一致性與弱一致性。
強一致性可以理解為在任意時刻,所有節(jié)點中的數(shù)據(jù)是一樣的。同一時間點,你在節(jié)點A中獲取到key1的值與在節(jié)點B中獲取到key1的值應(yīng)該都是一樣的。
弱一致性包含很多種不同的實現(xiàn),目前分布式系統(tǒng)中廣泛實現(xiàn)的是最終一致性。
所謂最終一致性,就是不保證在任意時刻任意節(jié)點上的同一份數(shù)據(jù)都是相同的,但是隨著時間的遷移,不同節(jié)點上的同一份數(shù)據(jù)總是在向趨同的方向變化。也可以簡單的理解為在一段時間后,節(jié)點間的數(shù)據(jù)會最終達(dá)到一致狀態(tài)。
如何實現(xiàn)分布式環(huán)境下的countDownLatch?
zookeeper,判斷某個節(jié)點下的子節(jié)點到達(dá)一定數(shù)目后,則執(zhí)行,否則等待。
★用過哪些MQ,和其他mq比較有什么優(yōu)缺點?
MQ系統(tǒng)的數(shù)據(jù)如何保證不丟失?
發(fā)送消息后和接收消息后 確認(rèn)機制 加上持久化
MQ有可能發(fā)生重復(fù)消費,如何避免,如何做到冪等。
唯一主鍵,或者使用redis做id,
異步模式的用途和意義。
避免阻塞
如何保證消息的有序性和消息處理的有序性?
使用同一個queue
消息的重發(fā)補發(fā)策略
實時隊列采用雙隊列模式,生產(chǎn)者將行為記錄寫入Queue1,worker服務(wù)從Queue1消費新鮮數(shù)據(jù),如果異常則寫入Queue2(主要保存異常數(shù)據(jù)),RetryWorker會監(jiān)聽Queue2,消費異常數(shù)據(jù),如果還未處理成功按照一定的策略等待或者將異常數(shù)據(jù)再寫入Queue2,如果數(shù)據(jù)發(fā)生積壓可以調(diào)整worker的消費游標(biāo),從最新數(shù)據(jù)重新開始消費,保證了最新data得到處理,中間未處理的一段則可以啟動backupWorker指定起止游標(biāo)在消費完指定區(qū)間的數(shù)據(jù)后,backupWorker會自動停止。
DB降級開關(guān)后,可直接寫入redis(storm),同時將數(shù)據(jù)寫入一份到Retry隊列,在開啟DB降級開關(guān)后消費Retry隊列中的數(shù)據(jù),從而把數(shù)據(jù)寫入到mysql中,達(dá)到最終一致性。MYSQL切分為分片為2的N次方,例如原來分為兩個庫d0和d1均放在s0服務(wù)器上,s0同時有備機s1,擴(kuò)容只要幾步驟:確保s0到s1服務(wù)器同步順利,沒有明顯延遲;s0暫時關(guān)閉讀寫權(quán)限;確保s1已經(jīng)完全同步到s0更新;s1開放讀寫權(quán)限;d1的dns由s0切換到s1;s0開放讀寫權(quán)限。
希望能幫助到大家
覺得還不錯可以點個在看
也可以在評論區(qū)留下自己想看的干貨哦~