面試:第五章:中級常問
Spring 特性
Spring的核心特性就是IOC和AOP,IOC(Inversion of Control),即“控制反轉”;AOP(Aspect-OrientedProgramming),即“面向切面編程”。
IOC:IOC,另外一種說法叫DI(Dependency Injection),即依賴注入。它并不是一種技術實現,而是一種設計思想。在任何一個有實際開發(fā)意義的程序項目中,我們會使用很多類來描述它們特有的功能,并且通過類與類之間的相互協(xié)作來完成特定的業(yè)務邏輯。這個時候,每個類都需要負責管理與自己有交互的類的引用和依賴,代碼將會變的異常難以維護和極度的高耦合。而IOC的出現正是用來解決這個問題,我們通過IOC將這些相互依賴對象的創(chuàng)建、協(xié)調工作交給Spring容器去處理,每個對象只需要關注其自身的業(yè)務邏輯關系就可以了。在這樣的角度上來看,獲得依賴的對象的方式,進行了反轉,變成了由spring容器控制對象如何獲取外部資源(包括其他對象和文件資料等等)。
AOP:面向切面編程,往往被定義為促使軟件系統(tǒng)實現關注點的分離的技術。系統(tǒng)是由許多不同的組件所組成的,每一個組件各負責一塊特定功能。除了實現自身核心功能之外,這些組件還經常承擔著額外的職責。例如日志、事務管理和安全這樣的核心服務經常融入到自身具有核心業(yè)務邏輯的組件中去。這些系統(tǒng)服務經常被稱為橫切關注點,因為它們會跨越系統(tǒng)的多個組件。
JDBC的理解
JDBC(Java DataBase Connectivity,java數據庫連接)是一種用于執(zhí)行SQL語句的Java API,可以為多種關系數據庫提供統(tǒng)一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準,據此可以構建更高級的工具和接口,使數據庫開發(fā)人員能夠編寫數據庫應用程序
有了JDBC,向各種關系數據發(fā)送SQL語句就是一件很容易的事。換言之,有了JDBC API,就不必為訪問Sybase數據庫專門寫一個程序,為訪問Oracle數據庫又專門寫一個程序,或為訪問Informix數據庫又編寫另一個程序等等,程序員只需用JDBC API寫一個程序就夠了,它可向相應數據庫發(fā)送SQL調用。
Ajax異步和同步
同步是指:發(fā)送方發(fā)出數據后,等接收方發(fā)回響應以后才發(fā)下一個數據包的通訊方式。
異步是指:發(fā)送方發(fā)出數據后,不等接收方發(fā)回響應,接著發(fā)送下個數據包的通訊方式。
同步通信方式要求通信雙方以相同的時鐘頻率進行,而且準確協(xié)調,通過共享一個單個時鐘或定時脈沖源保證發(fā)送方和接收方的準確同步,效率較高;
異步通信方式不要求雙方同步,收發(fā)方可采用各自的時鐘源,雙方遵循異步的通信協(xié)議,以字符為數據傳輸單位,發(fā)送方傳送字符的時間間隔不確定,發(fā)送效率比同步傳送效率低。
使用者可以同步或異步實現服務調用。從使用者的觀點來看,這兩種方式的不同之處在于:
同步——使用者通過單個線程調用服務;該線程發(fā)送請求,在服務運行時阻塞,并且等待響應。
異步——使用者通過兩個線程調用服務;一個線程發(fā)送請求,而另一個單獨的線程接收響應。
秒殺活動
秒殺架構設計理念
限流: 鑒于只有少部分用戶能夠秒殺成功,所以要限制大部分流量,只允許少部分流量進入服務后端。
削峰:對于秒殺系統(tǒng)瞬時會有大量用戶涌入,所以在搶購一開始會有很高的瞬間峰值。高峰值流量是壓垮系統(tǒng)很重要的原因,所以如何把瞬間的高流量變成一段時間平穩(wěn)的流量也是設計秒殺系統(tǒng)很重要的思路。實現削峰的常用的方法有利用緩存和消息中間件等技術。
異步處理:秒殺系統(tǒng)是一個高并發(fā)系統(tǒng),采用異步處理模式可以極大地提高系統(tǒng)并發(fā)量,其實異步處理就是削峰的一種實現方式。
內存緩存:秒殺系統(tǒng)最大的瓶頸一般都是數據庫讀寫,由于數據庫讀寫屬于磁盤IO,性能很低,如果能夠把部分數據或業(yè)務邏輯轉移到內存緩存,效率會有極大地提升。
可拓展:當然如果我們想支持更多用戶,更大的并發(fā),最好就將系統(tǒng)設計成彈性可拓展的,如果流量來了,拓展機器就好了。像淘寶、京東等雙十一活動時會增加大量機器應對交易高峰。
前端方案
瀏覽器端(js):
頁面靜態(tài)化:將活動頁面上的所有可以靜態(tài)的元素全部靜態(tài)化,并盡量減少動態(tài)元素。通過CDN來抗峰值。
禁止重復提交:用戶提交之后按鈕置灰,禁止重復提交
用戶限流:在某一時間段內只允許用戶提交一次請求,比如可以采取IP限流
后端方案
服務端控制器層(網關層)
限制uid(UserID)訪問頻率:我們上面攔截了瀏覽器訪問的請求,但針對某些惡意攻擊或其它插件,在服務端控制層需要針對同一個訪問uid,限制訪問頻率。
服務層
上面只攔截了一部分訪問請求,當秒殺的用戶量很大時,即使每個用戶只有一個請求,到服務層的請求數量還是很大。比如我們有100W用戶同時搶100臺手機,服務層并發(fā)請求壓力至少為100W。
采用消息隊列緩存請求:既然服務層知道庫存只有100臺手機,那完全沒有必要把100W個請求都傳遞到數據庫啊,那么可以先把這些請求都寫到消息隊列緩存一下,數據庫層訂閱消息減庫存,減庫存成功的請求返回秒殺成功,失敗的返回秒殺結束。
利用緩存應對讀請求:對類似于12306等購票業(yè)務,是典型的讀多寫少業(yè)務,大部分請求是查詢請求,所以可以利用緩存分擔數據庫壓力。
利用緩存應對寫請求:緩存也是可以應對寫請求的,比如我們就可以把數據庫中的庫存數據轉移到Redis緩存中,所有減庫存操作都在Redis中進行,然后再通過后臺進程把Redis中的用戶秒殺請求同步到數據庫中。
數據庫層
數據庫層是最脆弱的一層,一般在應用設計時在上游就需要把請求攔截掉,數據庫層只承擔“能力范圍內”的訪問請求。所以,上面通過在服務層引入隊列和緩存,讓最底層的數據庫高枕無憂。
案例:利用消息中間件和緩存實現簡單的秒殺系統(tǒng)
Redis是一個分布式緩存系統(tǒng),支持多種數據結構,我們可以利用Redis輕松實現一個強大的秒殺系統(tǒng)。
我們可以采用Redis 最簡單的key-value數據結構,用一個原子類型的變量值(AtomicInteger)作為key,把用戶id作為value,庫存數量便是原子變量的最大值。對于每個用戶的秒殺,我們使用 RPUSH key value插入秒殺請求, 當插入的秒殺請求數達到上限時,停止所有后續(xù)插入。
然后我們可以在臺啟動多個工作線程,使用 LPOP key 讀取秒殺成功者的用戶id,然后再操作數據庫做最終的下訂單減庫存操作。
當然,上面Redis也可以替換成消息中間件如ActiveMQ、RabbitMQ等,也可以將緩存和消息中間件 組合起來,緩存系統(tǒng)負責接收記錄用戶請求,消息中間件負責將緩存中的請求同步到數據庫。
單點登陸如果在另一臺電腦上登陸并修改了密碼怎么辦?
(Single Sign On),簡稱為 SSO,是目前比較流行的企業(yè)業(yè)務整合的解決方案之一。SSO的定義是在多個應用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統(tǒng)。
當用戶第一次訪問應用系統(tǒng)的時候,因為還沒有登錄,會被引導到認證系統(tǒng)中進行登錄;根據用戶提供的登錄信息,認證系統(tǒng)進行身份校驗,如果通過校驗,應該返回給用戶一個認證的憑據--ticket;用戶再訪問別的應用的時候,就會將這個ticket帶上,作為自己認證的憑據,應用系統(tǒng)接受到請求之后會把ticket送到認證系統(tǒng)進行校驗,檢查ticket的合法性。如果通過校驗,用戶就可以在不用再次登錄的情況下訪問應用系統(tǒng)2和應用系統(tǒng)3了。
要實現SSO,需要以下主要的功能:
所有應用系統(tǒng)共享一個身份認證系統(tǒng)。
統(tǒng)一的認證系統(tǒng)是SSO的前提之一。認證系統(tǒng)的主要功能是將用戶的登錄信息和用戶信息庫相比較,對用戶進行登錄認證;認證成功后,認證系統(tǒng)應該生成統(tǒng)一的認證標志(ticket),返還給用戶。另外,認證系統(tǒng)還應該對ticket進行效驗,判斷其有效性。
所有應用系統(tǒng)能夠識別和提取ticket信息
要實現SSO的功能,讓用戶只登錄一次,就必須讓應用系統(tǒng)能夠識別已經登錄過的用戶。應用系統(tǒng)應該能對ticket進行識別和提取,通過與認證系統(tǒng)的通訊,能自動判斷當前用戶是否登錄過,從而完成單點登錄的功能。
當用戶在另一終端登陸并修改密碼,則對應的ticket附帶的信息會發(fā)生改變,導致原有ticket因無法通過校驗而失效。因此要求用戶使用新的密碼重新登陸。
在我們的電商項目中,單點登陸使用的驗證字符串叫token。這里的ticket是門票的意思,與我們學的token對應相同。
Redis宕機之后,購物車中的數據如何處理?如何緩解mysql壓力?
用redis保存的*.rdb文件恢復即可。
另外redis還有AOF功能,啟動時可以自動恢復到前一條查詢。
這樣做在一定程度上減少數據丟失。但重啟redis會需要從關系型數據庫中讀取數據,增大mysql的壓力。
依據實際情況,如果redis之前有主從復制,則可在其他節(jié)點redis上拿到數據。如果公司沒錢,則只能暫時限制客戶端訪問量,優(yōu)先恢復redis數據。
Zookeeper待機的情況下,dubbo如何工作?
1. Zookeeper的作用:
zookeeper用來注冊服務和進行負載均衡,哪一個服務由哪一個機器來提供必需讓調用者知道,簡單來說就是ip地址和服務名稱的對應關系。當然也可以 通過硬編碼的方式把這種對應關系在調用方業(yè)務代碼中實現,但是如果提供服務的機器掛掉調用者無法知曉,如果不更改代碼會繼續(xù)請求掛掉的機器提供服務。 zookeeper通過心跳機制可以檢測掛掉的機器并將掛掉機器的ip和服務對應關系從列表中刪除。至于支持高并發(fā),簡單來說就是橫向擴展,在不更改代碼 的情況通過添加機器來提高運算能力。通過添加新的機器向zookeeper注冊服務,服務的提供者多了能服務的客戶就多了。
2. dubbo:
是管理中間層的工具,在業(yè)務層到數據倉庫間有非常多服務的接入和服務提供者需要調度,dubbo提供一個框架解決這個問題。
注意這里的dubbo只是一個框架,至于你架子上放什么是完全取決于你的,就像一個汽車骨架,你需要配你的輪子引擎。這個框架中要完成調度必須要有一個分布式的注冊中心,儲存所有服務的元數據,你可以用zk,也可以用別的,只是大家都用zk。
3. zookeeper和dubbo的關系:
Dubbo的將注冊中心進行抽象,是得它可以外接不同的存儲媒介給注冊中心提供服務,有ZooKeeper,Memcached,Redis等。
引入了ZooKeeper作為存儲媒介,也就把ZooKeeper的特性引進來。首先是負載均衡,單注冊中心的承載能力是有限的,在流量達到一定程度的時 候就需要分流,負載均衡就是為了分流而存在的,一個ZooKeeper群配合相應的Web應用就可以很容易達到負載均衡;資源同步,單單有負載均衡還不 夠,節(jié)點之間的數據和資源需要同步,ZooKeeper集群就天然具備有這樣的功能;命名服務,將樹狀結構用于維護全局的服務地址列表,服務提供者在啟動 的時候,向ZK上的指定節(jié)點/dubbo/${serviceName}/providers目錄下寫入自己的URL地址,這個操作就完成了服務的發(fā)布。 其他特性還有Mast選舉,分布式鎖等。
從MQ在完成訂單之后,發(fā)送消息鎖定庫存。消息始終失敗。
網關是如何實現?
就是定義一個Servlet接收請求。然后經過preFilter(封裝請求參數),routeFilter(轉發(fā)請求),postFilter(輸出內容)。三個過濾器之間,共享request、response以及其他的一些全局變量。
(1)將request,response放入threadlocal中
(2)執(zhí)行三組過濾器
(3)清除threadlocal中的的環(huán)境變量
Redis和mysql數據同步是先刪除redis還是先刪除mysql?
不管是先寫庫,再刪除緩存;還是先刪緩存,再寫庫,都有可能出現數據不一致的情況
因為寫和讀是并發(fā)的,沒法保證順序,如果刪了緩存,還沒有來得及寫庫,另一個線程就來讀取,發(fā)現緩存為空,則去數據庫中讀取數據寫入緩存,此時緩存中為臟數據。如果先寫了庫,再刪除緩存前,寫庫的線程宕機了,沒有刪除掉緩存,則也會出現數據不一致情況。 如果是redis集群,或者主從模式,寫主讀從,由于redis復制存在一定的時間延遲,也有可能導致數據不一致。
這時候,考慮先刪除數據庫內容,再刪redis。因為在庫存等實時數據都是直接在數據庫中讀取,從業(yè)務邏輯上來說,我們允許查詢時的數據緩存誤差,但是不允許結算時的數據存在誤差。
Hashmap為什么線程不安全,如何讓它線程安全
HashMap在put的時候,插入的元素超過了容量(由負載因子決定)的范圍就會觸發(fā)擴容操作,就是rehash,這個會重新將原數組的內容重新hash到新的擴容數組中,在多線程的環(huán)境下,存在同時其他的元素也在進行put操作,如果hash值相同,可能出現同時在同一數組下用鏈表表示,造成閉環(huán),導致在get時會出現死循環(huán),所以HashMap是線程不安全的。
使用 java.util.Hashtable 類,此類是線程安全的。
使用 java.util.concurrent.ConcurrentHashMap,此類是線程安全的。
使用 java.util.Collections.synchronizedMap() 方法包裝 HashMap object,得到線程安全的Map,并在此Map上進行操作。
Spring cloud原理 網關
Eureka是微服務架構中的注冊中心,專門負責服務的注冊與發(fā)現。Eureka Client組件,這個組件專門負責將這個服務的信息注冊到Eureka Server中。Eureka Server是一個注冊中心,里面有一個注冊表,保存了各服務所在的機器和端口號。
Zuul,也就是微服務網關。這個組件是負責網絡路由的。而且有一個網關之后,還有很多好處,比如可以做統(tǒng)一的降級、限流、認證授權、安全,等等。
Dubbo+zookeeper如何集群
Dubbo 是Alibaba開源的分布式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地松耦合)。從服務模型的角度來看,Dubbo采用的是一種非常簡單的模型,要么是提供方提供服務,要么是消費方消費服務,所以基于這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。
ZooKeeper 是 Apache 的一個頂級項目,為分布式應用提供高效、高可用的分布式協(xié)調服務,提供了諸如數據發(fā)布/訂閱、負載均衡、命名服務、分布式協(xié)調/通知和分布式鎖等分布式基礎服務。由于 ZooKeeper 便捷的使用方式、卓越的性能和良好的穩(wěn)定性,被廣泛地應用于諸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系統(tǒng)中。
Nginx 是一款自由的、開源的、高性能的HTTP服務器和反向代理服務器;同時也是一個IMAP、POP3、SMTP代理服務器;Nginx可以作為一個HTTP服務器進行網站的發(fā)布處理,另外Nginx可以作為反向代理進行負載均衡的實現。
設計模式在項目中如何體現
1、模板方法模式
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中,如JdbcTemplate
2、代理
spring的Proxy模式在aop中有體現
3、觀察者
定義對象間的一種一對多的依賴關系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。
spring中Observer模式常用的地方是listener的實現。如ApplicationListener。
4、適配器(Adapter )
MethodBeforeAdviceAdapter類
5、策略模式
使用了java的繼承和多態(tài)
案例1:加減法計算器,定義一個計算類接口,加法和減法類都實現它,加的時候傳入加法對象。
案例2:導出excel,pdf,word時,分別創(chuàng)建不同的對象
簡單理解:執(zhí)行多個事情時,創(chuàng)建多個對象
6、單例模式
解決一個全局使用的類頻繁的創(chuàng)建與銷毀
7、工廠模式
分為三種:簡單工廠,工廠方法,抽象工廠
根據“需求”生產“產品”,解耦“需求”“工廠”和“產品”。
簡單工廠:通過構造時傳入的標識來生產產品,不同產品都在同一個工廠中生產,每新增加一種產品,需要改工廠類,來判斷,這種判斷會隨著產品的增加而增加,給擴展和維護帶來麻煩
簡單工廠項目案例:根據傳入的 不同(比如1對應支付流水,2 對應訂單流水),生成不同類型的流水號
工廠方法:(使一個類的使用延遲到子類)
其中的工廠類根據傳入的A.class類型,反射出實例。
產品接口,產品類A,產品類B,工廠類可以生成不同的產品類對象,如果要隨著產品的增加而增加,工廠類不變,只需新增一個產品類C即可。
項目案例:郵件服務器,有三種協(xié)議,POP3,IMAP,HTTP,把這三種做完產品類,在定義個工廠方法
抽象工廠:一個工廠生產多個產品,它們是一個產品族,不同的產品族的產品派生于不同的抽象產品
MQ丟包如何解決
transaction機制就是說,發(fā)送消息前,開啟事物(channel.txSelect()),然后發(fā)送消息,如果發(fā)送過程中出現什么異常,事物就會回滾(channel.txRollback()),如果發(fā)送成功則提交事物(channel.txCommit())。然而缺點就是吞吐量下降了。
所有在該信道上面發(fā)布的消息都將會被指派一個唯一的ID(從1開始),一旦消息被投遞到所有匹配的隊列之后,rabbitMQ就會發(fā)送一個Ack給生產者(包含消息的唯一ID),這就使得生產者知道消息已經正確到達目的隊列了.如果rabiitMQ沒能處理該消息,則會發(fā)送一個Nack消息給你,你可以進行重試操作。
分布式事務在項目中如何體現
一、兩階段提交(2PC)
兩階段提交這種解決方案屬于犧牲了一部分可用性來換取的一致性。在實現方面,在 .NET 中,可以借助 TransactionScop 提供的 API 來編程實現分布式系統(tǒng)中的兩階段提交,比如WCF中就有實現這部分功能。不過在多服務器之間,需要依賴于DTC來完成事務一致性
優(yōu)點: 盡量保證了數據的強一致,適合對數據強一致要求很高的關鍵領域。(其實也不能100%保證強一致)
缺點: 實現復雜,犧牲了可用性,對性能影響較大,不適合高并發(fā)高性能場景,如果分布式系統(tǒng)跨接口調用,目前 .NET 界還沒有實現方案
二、補償事務(TCC)
TCC 其實就是采用的補償機制,其核心思想是:針對每個操作,都要注冊一個與其對應的確認和補償(撤銷)操作。它分為三個階段:
Try 階段主要是對業(yè)務系統(tǒng)做檢測及資源預留
Confirm 階段主要是對業(yè)務系統(tǒng)做確認提交,Try階段執(zhí)行成功并開始執(zhí)行 Confirm階段時,默認 Confirm階段是不會出錯的。即:只要Try成功,Confirm一定成功。
Cancel 階段主要是在業(yè)務執(zhí)行錯誤,需要回滾的狀態(tài)下執(zhí)行的業(yè)務取消,預留資源釋放。
優(yōu)點: 跟2PC比起來,實現以及流程相對簡單了一些,但數據的一致性比2PC也要差一些
缺點: 缺點還是比較明顯的,在2,3步中都有可能失敗。TCC屬于應用層的一種補償方式,所以需要程序員在實現的時候多寫很多補償的代碼,在一些場景中,一些業(yè)務流程可能用TCC不太好定義及處理
三、本地消息表(異步確保)(使用最多的技術方案)
消息生產方,需要額外建一個消息表,并記錄消息發(fā)送狀態(tài)。消息表和業(yè)務數據要在一個事務里提交,也就是說他們要在一個數據庫里面。然后消息會經過MQ發(fā)送到消息的消費方。如果消息發(fā)送失敗,會進行重試發(fā)送。
消息消費方,需要處理這個消息,并完成自己的業(yè)務邏輯。此時如果本地事務處理成功,表明已經處理成功了,如果處理失敗,那么就會重試執(zhí)行。如果是業(yè)務上面的失敗,可以給生產方發(fā)送一個業(yè)務補償消息,通知生產方進行回滾等操作。
生產方和消費方定時掃描本地消息表,把還沒處理完成的消息或者失敗的消息再發(fā)送一遍。
四、MQ 事務消息
有一些第三方的MQ是支持事務消息的,比如RocketMQ,他們支持事務消息的方式也是類似于采用的二階段提交,但是市面上一些主流的MQ都是不支持事務消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中間件為例,其思路大致為:
第一階段Prepared消息,會拿到消息的地址。
第二階段執(zhí)行本地事務,第三階段通過第一階段拿到的地址去訪問消息,并修改狀態(tài)。
也就是說在業(yè)務方法內要想消息隊列提交兩次請求,一次發(fā)送消息和一次確認消息。如果確認消息發(fā)送失敗了RocketMQ會定期掃描消息集群中的事務消息,這時候發(fā)現了Prepared消息,它會向消息發(fā)送者確認,所以生產方需要實現一個check接口,RocketMQ會根據發(fā)送端設置的策略來決定是回滾還是繼續(xù)發(fā)送確認消息。這樣就保證了消息發(fā)送與本地事務同時成功或同時失敗。
Spring管理bean的作用域,為什么不會被GC處理?
當通過spring容器創(chuàng)建一個Bean實例時,不僅可以完成Bean實例的實例化,還可以為Bean指定特定的作用域。Spring支持如下5種作用域:
singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean將只有一個實例
prototype:原型模式,每次通過容器的getBean方法獲取prototype定義的Bean時,都將產生一個新的Bean實例
request:對于每次HTTP請求,使用request定義的Bean都將產生一個新實例,即每次HTTP請求將會產生不同的Bean實例。只有在Web應用中使用Spring時,該作用域才有效
session:對于每次HTTP Session,使用session定義的Bean豆?jié){產生一個新實例。同樣只有在Web應用中使用Spring時,該作用域才有效
globalsession:每個全局的HTTP Session,使用session定義的Bean都將產生一個新實例。典型情況下,僅在使用portlet context的時候有效。同樣只有在Web應用中使用Spring時,該作用域才有效
其中比較常用的是singleton和prototype兩種作用域。對于singleton作用域的Bean,每次請求該Bean都將獲得相同的實例。容器負責跟蹤Bean實例的狀態(tài),負責維護Bean實例的生命周期行為;如果一個Bean被設置成prototype作用域,程序每次請求該id的Bean,Spring都會新建一個Bean實例,然后返回給程序。在這種情況下,Spring容器僅僅使用new 關鍵字創(chuàng)建Bean實例,一旦創(chuàng)建成功,容器不在跟蹤實例,也不會維護Bean實例的狀態(tài)。
如果不指定Bean的作用域,Spring默認使用singleton作用域。Java在創(chuàng)建Java實例時,需要進行內存申請;銷毀實例時,需要完成垃圾回收,這些工作都會導致系統(tǒng)開銷的增加。因此,prototype作用域Bean的創(chuàng)建、銷毀代價比較大。而singleton作用域的Bean實例一旦創(chuàng)建成功,可以重復使用。因此,除非必要,否則盡量避免將Bean被設置成prototype作用域。
spring底層使用map來存放bean實體,而map的鍵值是強引用,所以不會被GC,可以重復使用
上傳圖片的過程中圖片是在前端壓縮還是后端壓縮
前端,減少服務器壓力,從最開始就降低了傳輸的數據量。
分布式事務的具體實現?有哪些模塊運用了?
實現:前面有
運用:
Redis和MySQL如何對接
應用Redis實現數據的讀寫,同時利用隊列處理器定時將數據寫入mysql,此種情況存在的問題主要是如何保證mysql與redis的數據同步,二者數據同步的關鍵在于mysql數據庫中主鍵,方案是在redis啟動時去mysql讀取所有表鍵值存入redis中,往redis寫數據時,對redis主鍵自增并進行讀取,若mysql更新失敗,則需要及時清除緩存及同步redis主鍵。
Spring aop注解
設置事務隔離級別
多線程問題(原理)如何查看Stop之后的線程
Spring對bean是如何解析
所謂bean的解析就是將我們的xml文件中的bean解析出來,上面的入口看到使用的是ClassPathXmlApplicationContext來獲取ApplicationContext,所以,分析的入口也就從ClassPathXmlApplicationContext類中相應的構造函數開始。
getBean() 方法開始創(chuàng)建過程,getBean()有一系列的重載方法,最終都是調用doGetBean() 方法
getSingleton 方法嘗試從緩存中獲取單例 bean
當前 bean 是單例且緩存不存在則通過 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法創(chuàng)建單例對象
主要包含下下面三個主要方法:
createBeanInstance
populateBean
initializeBean
createBeanInstance 方法用于創(chuàng)建 Bean 實例
populateBean 方法主要給 Bean 進行屬性注入
initializeBean 方法主要處理各種回調
為什么InnoDB支持事務而myisam不支持
MyISAM:這個是默認類型,它是基于傳統(tǒng)的ISAM類型,ISAM是Indexed Sequential Access Method (有索引的順序訪問方法) 的縮寫,它是存儲記錄和文件的標準方法.與其他存儲引擎比較,MyISAM具有檢查和修復表格的大多數工具. MyISAM表格可以被壓縮,而且它們支持全文搜索.它們不是事務安全的,而且也不支持外鍵。如果事物回滾將造成不完全回滾,不具有原子性。如果執(zhí)行大量的SELECT,MyISAM是更好的選擇。
InnoDB:這種類型是事務安全的.它與BDB類型具有相同的特性,它們還支持外鍵.InnoDB表格速度很快.具有比BDB還豐富的特性,因此如果需要一個事務安全的存儲引擎,建議使用它.如果你的數據執(zhí)行大量的INSERT或UPDATE,出于性能方面的考慮,應該使用InnoDB表
冪等性防止訂單重復提交
1、token機制,防止頁面重復提交
2、唯一索引,防止新增臟數據
3、悲觀鎖樂觀鎖機制
4、 分布式鎖
Mysql除了可以做讀寫分離集群外還可以做什么集群
Mysql有主從復制集群和讀寫分離集群
項目中哪些體現了動態(tài)代理
Aop面向切面使用動態(tài)代理,有jdk和cglig
CurrentHashmap的并發(fā)度 Synchronized和writeeverywhere
樂觀鎖和悲觀鎖在代碼層面和sql層面如何實現
Sql層面:
一、悲觀鎖
1、排它鎖,當事務在操作數據時把這部分數據進行鎖定,直到操作完畢后再解鎖,其他事務操作才可操作該部分數據。這將防止其他進程讀取或修改表中的數據。
2、實現:大多數情況下依靠數據庫的鎖機制實現
一般使用 select ...for update 對所選擇的數據進行加鎖處理,例如select * from account where name=”Max” for update, 這條sql 語句鎖定了account 表中所有符合檢索條件(name=”Max”)的記錄。本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。
二、樂觀鎖
1、如果有人在你之前更新了,你的更新應當是被拒絕的,可以讓用戶重新操作。
2、實現:大多數基于數據版本(Version)記錄機制實現
具體可通過給表加一個版本號或時間戳字段實現,當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當我們提交更新的時候,判斷當前版本信息與第一次取出來的版本值大小,如果數據庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數據,拒絕更新,讓用戶重新操作。
代碼層面:
悲觀鎖:一段執(zhí)行邏輯加上悲觀鎖,不同線程同時執(zhí)行時,只能有一個線程執(zhí)行,其他的線程在入口處等待,直到鎖被釋放.
樂觀鎖:一段執(zhí)行邏輯加上樂觀鎖,不同線程同時執(zhí)行時,可以同時進入執(zhí)行,在最后更新數據的時候要檢查這些數據是否被其他線程修改了(版本和執(zhí)行初是否相同),沒有修改則進行更新,否則放棄本次操作。
Jdk1.7和1.8之后的鎖有什么不同
MySQL存儲過程
SQL語句需要先編譯然后執(zhí)行,而存儲過程(Stored Procedure)是一組為了完成特定功能的SQL語句集,經編譯后存儲在數據庫中,用戶通過指定存儲過程的名字并給定參數(如果該存儲過程帶有參數)來調用執(zhí)行它。
存儲過程是可編程的函數,在數據庫中創(chuàng)建并保存,可以由SQL語句和控制結構組成。當想要在不同的應用程序或平臺上執(zhí)行相同的函數,或者封裝特定功能時,存儲過程是非常有用的。數據庫中的存儲過程可以看做是對編程中面向對象方法的模擬,它允許控制數據的訪問方式。
存儲過程的優(yōu)點:
(1).增強SQL語言的功能和靈活性:存儲過程可以用控制語句編寫,有很強的靈活性,可以完成復雜的判斷和較復雜的運算。
(2).標準組件式編程:存儲過程被創(chuàng)建后,可以在程序中被多次調用,而不必重新編寫該存儲過程的SQL語句。而且數據庫專業(yè)人員可以隨時對存儲過程進行修改,對應用程序源代碼毫無影響。
(3).較快的執(zhí)行速度:如果某一操作包含大量的Transaction-SQL代碼或分別被多次執(zhí)行,那么存儲過程要比批處理的執(zhí)行速度快很多。因為存儲過程是預編譯的。在首次運行一個存儲過程時查詢,優(yōu)化器對其進行分析優(yōu)化,并且給出最終被存儲在系統(tǒng)表中的執(zhí)行計劃。而批處理的Transaction-SQL語句在每次運行時都要進行編譯和優(yōu)化,速度相對要慢一些。
(4).減少網絡流量:針對同一個數據庫對象的操作(如查詢、修改),如果這一操作所涉及的Transaction-SQL語句被組織進存儲過程,那么當在客戶計算機上調用該存儲過程時,網絡中傳送的只是該調用語句,從而大大減少網絡流量并降低了網絡負載。
(5).作為一種安全機制來充分利用:通過對執(zhí)行某一存儲過程的權限進行限制,能夠實現對相應的數據的訪問權限的限制,避免了非授權用戶對數據的訪問,保證了數據的安全。
MySQL存儲過程的創(chuàng)建
語法
CREATE PROCEDURE 過程名([[IN|OUT|INOUT] 參數名 數據類型[,[IN|OUT|INOUT] 參數名 數據類型…]]) [特性 ...] 過程體
DELIMITER //
CREATE PROCEDURE myproc(OUT s int)
BEGIN
SELECT COUNT(*) INTO s FROM students;
END
//
DELIMITER ;
分隔符
MySQL默認以";"為分隔符,如果沒有聲明分割符,則編譯器會把存儲過程當成SQL語句進行處理,因此編譯過程會報錯,所以要事先用“DELIMITER //”聲明當前段分隔符,讓編譯器把兩個"http://"之間的內容當做存儲過程的代碼,不會執(zhí)行這些代碼;“DELIMITER ;”的意為把分隔符還原。
參數
存儲過程根據需要可能會有輸入、輸出、輸入輸出參數,如果有多個參數用","分割開。MySQL存儲過程的參數用在存儲過程的定義,共有三種參數類型,IN,OUT,INOUT:
· IN參數的值必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,為默認值
· OUT:該值可在存儲過程內部被改變,并可返回
· INOUT:調用時指定,并且可被改變和返回
過程體
過程體的開始與結束使用BEGIN與END進行標識。
Spring boot和spring cloud的區(qū)別與聯系
Spring boot 是 Spring 的一套快速配置腳手架,可以基于spring boot 快速開發(fā)單個微服務,Spring Boot,看名字就知道是Spring的引導,就是用于啟動Spring的,使得Spring的學習和使用變得快速無痛。不僅適合替換原有的工程結構,更適合微服務開發(fā)。
Spring Cloud基于Spring Boot,為微服務體系開發(fā)中的架構問題,提供了一整套的解決方案——服務注冊與發(fā)現,服務消費,服務保護與熔斷,網關,分布式調用追蹤,分布式配置管理等。
Spring Cloud是一個基于Spring Boot實現的云應用開發(fā)工具;Spring boot專注于快速、方便集成的單個個體,Spring Cloud是關注全局的服務治理框架;spring boot使用了默認大于配置的理念,很多集成方案已經幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring boot來實現。
分布式鎖(zookeeper,redis,數據庫)如何實現
一、基于數據庫實現的分布式鎖
基于表實現的分布式鎖
CREATE TABLE `methodLock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法名',
`desc` varchar(1024) NOT NULL DEFAULT '備注信息',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數據時間,自動生成',
PRIMARY KEY (`id`),
UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';
當我們想要鎖住某個方法時,執(zhí)行以下SQL:
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
因為我們對method_name做了唯一性約束,這里如果有多個請求同時提交到數據庫的話,數據庫會保證只有一個操作可以成功,那么我們就可以認為操作成功的那個線程獲得了該方法的鎖,可以執(zhí)行方法體內容。
當方法執(zhí)行完畢之后,想要釋放鎖的話,需要執(zhí)行以下Sql:
delete from methodLock where method_name ='method_name'
上面這種簡單的實現有以下幾個問題:
這把鎖強依賴數據庫的可用性,數據庫是一個單點,一旦數據庫掛掉,會導致業(yè)務系統(tǒng)不可用。
這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在數據庫中,其他線程無法再獲得到鎖。
這把鎖只能是非阻塞的,因為數據的insert操作,一旦插入失敗就會直接報錯。沒有獲得鎖的線程并不會進入排隊隊列,要想再次獲得鎖就要再次觸發(fā)獲得鎖操作。
這把鎖是非重入的,同一個線程在沒有釋放鎖之前無法再次獲得該鎖。因為數據中數據已經存在了。
這把鎖是非公平鎖,所有等待鎖的線程憑運氣去爭奪鎖。
當然,我們也可以有其他方式解決上面的問題。
數據庫是單點?搞兩個數據庫,數據之前雙向同步。一旦掛掉快速切換到備庫上。
沒有失效時間?只要做一個定時任務,每隔一定時間把數據庫中的超時數據清理一遍。
非阻塞的?搞一個while循環(huán),直到insert成功再返回成功。
非重入的?在數據庫表中加個字段,記錄當前獲得鎖的機器的主機信息和線程信息,那么下次再獲取鎖的時候先查詢數據庫,如果當前機器的主機信息和線程信息在數據庫可以查到的話,直接把鎖分配給他就可以了。
非公平的?再建一張中間表,將等待鎖的線程全記錄下來,并根據創(chuàng)建時間排序,只有最先創(chuàng)建的允許獲取鎖
基于排他鎖實現的分布式鎖
除了可以通過增刪操作數據表中的記錄以外,其實還可以借助數據中自帶的鎖來實現分布式的鎖。
我們還用剛剛創(chuàng)建的那張數據庫表??梢酝ㄟ^數據庫的排他鎖來實現分布式鎖。 基于MySql的InnoDB引擎,可以使用以下方法來實現加鎖操作:
public boolean lock(){
connection.setAutoCommit(false);
while(true){
try{
result = select * from methodLock where method_name=xxx for update;
if(result==null){
return true;
}
}catch(Exception e){
}
sleep(1000);
}
return false;
}
在查詢語句后面增加for update,數據庫會在查詢過程中給數據庫表增加排他鎖。當某條記錄被加上排他鎖之后,其他線程無法再在該行記錄上增加排他鎖。
我們可以認為獲得排它鎖的線程即可獲得分布式鎖,當獲取到鎖之后,可以執(zhí)行方法的業(yè)務邏輯,執(zhí)行完方法之后,再通過以下方法解鎖:
public void unlock(){ connection.commit(); }
通過connection.commit();操作來釋放鎖。
這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題。
阻塞鎖? for update語句會在執(zhí)行成功后立即返回,在執(zhí)行失敗時一直處于阻塞狀態(tài),直到成功。
鎖定之后服務宕機,無法釋放?使用這種方式,服務宕機之后數據庫會自己把鎖釋放掉。
但是還是無法直接解決數據庫單點、可重入和公平鎖的問題。
總結一下使用數據庫來實現分布式鎖的方式,這兩種方式都是依賴數據庫的一張表,一種是通過表中的記錄的存在情況確定當前是否有鎖存在,另外一種是通過數據庫的排他鎖來實現分布式鎖。
數據庫實現分布式鎖的優(yōu)點
直接借助數據庫,容易理解。
數據庫實現分布式鎖的缺點
會有各種各樣的問題,在解決問題的過程中會使整個方案變得越來越復雜。
操作數據庫需要一定的開銷,性能問題需要考慮。
二、基于緩存的分布式鎖
相比較于基于數據庫實現分布式鎖的方案來說,基于緩存來實現在性能方面會表現的更好一點。
目前有很多成熟的緩存產品,包括Redis,memcached等。這里以Redis為例來分析下使用緩存實現分布式鎖的方案。
基于Redis實現分布式鎖在網上有很多相關文章,其中主要的實現方式是使用Jedis.setNX方法來實現。
public boolean trylock(String key) {
ResultCode code = jedis.setNX(key, "This is a Lock.");
if (ResultCode.SUCCESS.equals(code))
return true;
else
return false;
}
public boolean unlock(String key){
ldbTairManager.invalid(NAMESPACE, key);
}
以上實現方式同樣存在幾個問題:
1、單點問題。
2、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在redis中,其他線程無法再獲得到鎖。
3、這把鎖只能是非阻塞的,無論成功還是失敗都直接返回。
4、這把鎖是非重入的,一個線程獲得鎖之后,在釋放鎖之前,無法再次獲得該鎖,因為使用到的key在redis中已經存在。無法再執(zhí)行setNX操作。
5、這把鎖是非公平的,所有等待的線程同時去發(fā)起setNX操作,運氣好的線程能獲取鎖。
當然,同樣有方式可以解決。
現在主流的緩存服務都支持集群部署,通過集群來解決單點問題。
沒有失效時間?redis的setExpire方法支持傳入失效時間,到達時間之后數據會自動刪除。
非阻塞?while重復執(zhí)行。
非可重入?在一個線程獲取到鎖之后,把當前主機信息和線程信息保存起來,下次再獲取之前先檢查自己是不是當前鎖的擁有者。
非公平?在線程獲取鎖之前先把所有等待的線程放入一個隊列中,然后按先進先出原則獲取鎖。
redis集群的同步策略是需要時間的,有可能A線程setNX成功后拿到鎖,但是這個值還沒有更新到B線程執(zhí)行setNX的這臺服務器,那就會產生并發(fā)問題。
redis的作者Salvatore Sanfilippo,提出了Redlock算法,該算法實現了比單一節(jié)點更安全、可靠的分布式鎖管理(DLM)。
Redlock算法假設有N個redis節(jié)點,這些節(jié)點互相獨立,一般設置為N=5,這N個節(jié)點運行在不同的機器上以保持物理層面的獨立。
算法的步驟如下:
1、客戶端獲取當前時間,以毫秒為單位。
2、客戶端嘗試獲取N個節(jié)點的鎖,(每個節(jié)點獲取鎖的方式和前面說的緩存鎖一樣),N個節(jié)點以相同的key和value獲取鎖??蛻舳诵枰O置接口訪問超時,接口超時時間需要遠遠小于鎖超時時間,比如鎖自動釋放的時間是10s,那么接口超時大概設置5-50ms。這樣可以在有redis節(jié)點宕機后,訪問該節(jié)點時能盡快超時,而減小鎖的正常使用。
3、客戶端計算在獲得鎖的時候花費了多少時間,方法是用當前時間減去在步驟一獲取的時間,只有客戶端獲得了超過3個節(jié)點的鎖,而且獲取鎖的時間小于鎖的超時時間,客戶端才獲得了分布式鎖。
4、客戶端獲取的鎖的時間為設置的鎖超時時間減去步驟三計算出的獲取鎖花費時間。
5、如果客戶端獲取鎖失敗了,客戶端會依次刪除所有的鎖。
使用Redlock算法,可以保證在掛掉最多2個節(jié)點的時候,分布式鎖服務仍然能工作,這相比之前的數據庫鎖和緩存鎖大大提高了可用性,由于redis的高效性能,分布式緩存鎖性能并不比數據庫鎖差。
但是,有一位分布式的專家寫了一篇文章《How to do distributed locking》,質疑Redlock的正確性。
該專家提到,考慮分布式鎖的時候需要考慮兩個方面:性能和正確性。
如果使用高性能的分布式鎖,對正確性要求不高的場景下,那么使用緩存鎖就足夠了。
如果使用可靠性高的分布式鎖,那么就需要考慮嚴格的可靠性問題。而Redlock則不符合正確性。為什么不符合呢?專家列舉了幾個方面。
現在很多編程語言使用的虛擬機都有GC功能,在Full GC的時候,程序會停下來處理GC,有些時候Full GC耗時很長,甚至程序有幾分鐘的卡頓,文章列舉了HBase的例子,HBase有時候GC幾分鐘,會導致租約超時。而且Full GC什么時候到來,程序無法掌控,程序的任何時候都可能停下來處理GC,比如下圖,客戶端1獲得了鎖,正準備處理共享資源的時候,發(fā)生了Full GC直到鎖過期。這樣,客戶端2又獲得了鎖,開始處理共享資源。在客戶端2處理的時候,客戶端1 Full GC完成,也開始處理共享資源,這樣就出現了2個客戶端都在處理共享資源的情況。
給鎖帶上token,token就是version的概念,每次操作鎖完成,token都會加1,在處理共享資源的時候帶上token,只有指定版本的token能夠處理共享資源。
使用緩存實現分布式鎖的優(yōu)點
性能好。
使用緩存實現分布式鎖的缺點
實現過于負責,需要考慮的因素太多。
基于Zookeeper實現的分布式鎖
基于zookeeper臨時有序節(jié)點可以實現的分布式鎖。
大致思想即為:每個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節(jié)點的目錄下,生成一個唯一的瞬時有序節(jié)點。 判斷是否獲取鎖的方式很簡單,只需要判斷有序節(jié)點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節(jié)點刪除即可。同時,其可以避免服務宕機導致的鎖無法釋放,而產生的死鎖問題。
來看下Zookeeper能不能解決前面提到的問題。
鎖無法釋放?使用Zookeeper可以有效的解決鎖無法釋放的問題,因為在創(chuàng)建鎖的時候,客戶端會在ZK中創(chuàng)建一個臨時節(jié)點,一旦客戶端獲取到鎖之后突然掛掉(Session連接斷開),那么這個臨時節(jié)點就會自動刪除掉。其他客戶端就可以再次獲得鎖。
非阻塞鎖?使用Zookeeper可以實現阻塞的鎖,客戶端可以通過在ZK中創(chuàng)建順序節(jié)點,并且在節(jié)點上綁定監(jiān)聽器,一旦節(jié)點有變化,Zookeeper會通知客戶端,客戶端可以檢查自己創(chuàng)建的節(jié)點是不是當前所有節(jié)點中序號最小的,如果是,那么自己就獲取到鎖,便可以執(zhí)行業(yè)務邏輯了。
不可重入?使用Zookeeper也可以有效的解決不可重入的問題,客戶端在創(chuàng)建節(jié)點的時候,把當前客戶端的主機信息和線程信息直接寫入到節(jié)點中,下次想要獲取鎖的時候和當前最小的節(jié)點中的數據比對一下就可以了。如果和自己的信息一樣,那么自己直接獲取到鎖,如果不一樣就再創(chuàng)建一個臨時的順序節(jié)點,參與排隊。
單點問題?使用Zookeeper可以有效的解決單點問題,ZK是集群部署的,只要集群中有半數以上的機器存活,就可以對外提供服務。
公平問題?使用Zookeeper可以解決公平鎖問題,客戶端在ZK中創(chuàng)建的臨時節(jié)點是有序的,每次鎖被釋放時,ZK可以通知最小節(jié)點來獲取鎖,保證了公平。
問題又來了,我們知道Zookeeper需要集群部署,會不會出現Redis集群那樣的數據同步問題呢?
Zookeeper是一個保證了弱一致性即最終一致性的分布式組件。
Zookeeper采用稱為Quorum Based Protocol的數據同步協(xié)議。假如Zookeeper集群有N臺Zookeeper服務器(N通常取奇數,3臺能夠滿足數據可靠性同時有很高讀寫性能,5臺在數據可靠性和讀寫性能方面平衡最好),那么用戶的一個寫操作,首先同步到N/2 + 1臺服務器上,然后返回給用戶,提示用戶寫成功?;赒uorum Based Protocol的數據同步協(xié)議決定了Zookeeper能夠支持什么強度的一致性。
在分布式環(huán)境下,滿足強一致性的數據儲存基本不存在,它要求在更新一個節(jié)點的數據,需要同步更新所有的節(jié)點。這種同步策略出現在主從同步復制的數據庫中。但是這種同步策略,對寫性能的影響太大而很少見于實踐。因為Zookeeper是同步寫N/2+1個節(jié)點,還有N/2個節(jié)點沒有同步更新,所以Zookeeper不是強一致性的。
用戶的數據更新操作,不保證后續(xù)的讀操作能夠讀到更新后的值,但是最終會呈現一致性。犧牲一致性,并不是完全不管數據的一致性,否則數據是混亂的,那么系統(tǒng)可用性再高分布式再好也沒有了價值。犧牲一致性,只是不再要求關系型數據庫中的強一致性,而是只要系統(tǒng)能達到最終一致性即可。
Zookeeper是否滿足因果一致性,需要看客戶端的編程方式。
不滿足因果一致性的做法
A進程向Zookeeper的/z寫入一個數據,成功返回
A進程通知B進程,A已經修改了/z的數據
B讀取Zookeeper的/z的數據
由于B連接的Zookeeper的服務器有可能還沒有得到A寫入數據的更新,那么B將讀不到A寫入的數據
滿足因果一致性的做法
B進程監(jiān)聽Zookeeper上/z的數據變化
A進程向Zookeeper的/z寫入一個數據,成功返回前,Zookeeper需要調用注冊在/z上的監(jiān)聽器,Leader將數據變化的通知告訴B
B進程的事件響應方法得到響應后,去取變化的數據,那么B一定能夠得到變化的值
這里的因果一致性提現在Leader和B之間的因果一致性,也就是是Leader通知了數據有變化
第二種事件監(jiān)聽機制也是對Zookeeper進行正確編程應該使用的方法,所以,Zookeeper應該是滿足因果一致性的
所以我們在基于Zookeeper實現分布式鎖的時候,應該使用滿足因果一致性的做法,即等待鎖的線程都監(jiān)聽Zookeeper上鎖的變化,在鎖被釋放的時候,Zookeeper會將鎖變化的通知告訴滿足公平鎖條件的等待線程。
可以直接使用zookeeper第三方庫客戶端,這個客戶端中封裝了一個可重入的鎖服務。
Dubbo原理,ES權重如何實現
Spring boot和dubbo的區(qū)別
Ik分詞器分出中英文的原因是什么
后臺訂單倒計時如何對接(同步)前端頁面
Spring代理是什么
項目上線后,日志如何處理
輸出成文本文檔保存記錄
如何使用集合分組
ThreadLocal作用和使用場景,數據傳遞是否同步
ThreadLocal提供一個方便的方式,可以根據不同的線程存放一些不同的特征屬性,可以方便的在線程中進行存取。
在Hibernate中是通過使用ThreadLocal來實現的。在getSession方法中,如果ThreadLocal存在session,則返回session,否則創(chuàng)建一個session放入ThreadLocal中
總結一下就是在ThreadLocal中存放了一個session。
實際上ThreadLocal中并沒有存放任何的對象或引用,在上面的的代碼中ThreadLocal的實例threadSession只相當于一個標記的作用。而存放對象的真正位置是正在運行的Thread線程對象,每個Thread對象中都存放著一個ThreadLocalMap類型threadLocals對象,這是一個映射表map,這個map的鍵是一個ThreadLocal對象,值就是我們想存的局部對象。
在線程中存放一些就像session的這種特征變量,會針對不同的線程,有不同的值。
因此,不同步。
線程池滿了怎么辦
Dubbo失敗策略
Failover Cluster 模式
1.失敗自動切換,當出現失敗,重試其它服務器。(缺省)
2. 通常用于讀操作,但重試會帶來更長延遲。
3. 可通過retries=”2”來設置重試次數(不含第一次)。
Failfast Cluster
快速失敗,只發(fā)起一次調用,失敗立即報錯。
通常用于非冪等性的寫操作,比如新增記錄。
Failsafe Cluster
失敗安全,出現異常時,直接忽略。
通常用于寫入審計日志等操作。
.Failback Cluster
失敗自動恢復,后臺記錄失敗請求,定時重發(fā)。
通常用于消息通知操作。
Forking Cluster
并行調用多個服務器,只要一個成功即返回。
通常用于實時性要求較高的讀操作,但需要浪費更多服務資源。
可通過forks=”2”來設置最大并行數。
Broadcast Cluster
廣播調用所有提供者,逐個調用,任意一臺報錯則報錯。(2.1.0開始支持)
通常用于通知所有提供者更新緩存或日志等本地資源信息。
Redis中watch機制和原理
我們常用redis的watch和multi來處理一些涉及并發(fā)的操作,redis的watch+multi實際是一種樂觀鎖
watch命令描述
WATCH命令可以監(jiān)控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),之后的事務就不會執(zhí)行。監(jiān)控一直持續(xù)到EXEC命令(事務中的命令是在EXEC之后才執(zhí)行的,所以在MULTI命令后可以修改WATCH監(jiān)控的鍵值)