java http get_我是如何进入阿里巴巴的-面向春招应届生Java面试指南(九)
基礎(chǔ)篇
基本功
面向?qū)ο蟮奶卣?/h2>
1.final, finally, finalize 的區(qū)別 final—修飾符(關(guān)鍵字)如果一個類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被聲明為 abstract的,又被聲明為final的。將變量或方法聲明為final,可以保證它們在使用中不被改變。被聲明為final的變量必須在聲明時給定初值,而在以后的引用中只能讀取,不可修改。被聲明為final的方法也同樣只能使用,不能重載。 finally—再異常處理時提供 finally 塊來執(zhí)行任何清除操作。如果拋出一個異常,那么相匹配的 catch 子句就會執(zhí)行,然后控制就會進入 finally 塊(如果有的話)。 finalize—方法名。Java 技術(shù)允許使用 finalize() 方法在垃圾收集器將對象從內(nèi)存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調(diào)用的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調(diào)用的。
公眾號推薦
- 全網(wǎng)唯一一個從0開始幫助Java開發(fā)者轉(zhuǎn)做大數(shù)據(jù)領(lǐng)域的公眾號~
- 大數(shù)據(jù)技術(shù)與架構(gòu)或者搜索import_bigdata關(guān)注~
- 海量【java和大數(shù)據(jù)的面試題+視頻資料】整理在公眾號,關(guān)注后可以下載~
http://2.int 和 Integer 有什么區(qū)別
1,無論如何,Integer與new Integer不會相等。不會經(jīng)歷拆箱過程,new出來的對象存放在堆,而非new的Integer常量則在常量池(在方法區(qū)),他們的內(nèi)存地址不一樣,所以為false。 2,兩個都是非new出來的Integer,如果數(shù)在-128到127之間,則是true,否則為false。因為java在編譯Integer i2 = 128的時候,被翻譯成:Integer i2 = Integer.valueOf(128);而valueOf()函數(shù)會對-128到127之間的數(shù)進行緩存。 3,兩個都是new出來的,都為false。還是內(nèi)存地址不一樣。 4,int和Integer(無論new否)比,都為true,因為會把Integer自動拆箱為int再去比
class TestInteger { public static void main(String[] args) { int i = 128; Integer i2 = 128; Integer i3 = new Integer(128); System.out.println(i == i2); //Integer會自動拆箱為int,所以為true System.out.println(i == i3); //true,理由同上 Integer i4 = 127;//編譯時被翻譯成:Integer i4 = Integer.valueOf(127); Integer i5 = 127; System.out.println(i4 == i5);//true Integer i6 = 128; Integer i7 = 128; System.out.println(i6 == i7);//false Integer i8 = new Integer(127); System.out.println(i5 == i8); //false Integer i9 = new Integer(128); Integer i10 = new Integer(123); System.out.println(i9 == i10); //false } } 。3.重載和重寫的區(qū)別
1、覆蓋的方法的標志必須要和被覆蓋的方法的標志完全匹配,才能達到覆蓋的效果;
2、覆蓋的方法的返回值必須和被覆蓋的方法的返回一致;
3、覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致,或者是其子類;
4、被覆蓋的方法不能為private,否則在其子類中只是新定義了一個方法,并沒有對其進行覆蓋。
2.Overload 特點
1、在使用重載時只能通過不同的參數(shù)樣式。例如,不同的參數(shù)類型,不同的參數(shù)個數(shù),不同的參數(shù)順序(當然,同一方法內(nèi)的幾個參數(shù)類型必須不一樣,例如可以是fun(int, float), 但是不能為fun(int, int));
2、不能通過訪問權(quán)限、返回類型、拋出的異常進行重載;
3、方法的異常類型和數(shù)目不會對重載造成影響;
4、對于繼承來說,如果某一方法在父類中是訪問權(quán)限是priavte,那么就不能在子類對其進行重載,如果定義的話,也只是定義了一個新方法,而不會達到重載的效果。 4.抽象類和接口有什么區(qū)別
第一點. 接口是抽象類的變體,接口中所有的方法都是抽象的。而抽象類是聲明方法的存在而不去實現(xiàn)它的類。 第二點. 接口可以多繼承,抽象類不行 第三點. 接口定義方法,不能實現(xiàn),而抽象類可以實現(xiàn)部分方法。 第四點. 接口中基本數(shù)據(jù)類型為static 而抽類象不是的。 當你關(guān)注一個事物的本質(zhì)的時候,用抽象類;當你關(guān)注一個操作的時候,用接口。 抽象類的功能要遠超過接口,但是,定義抽象類的代價高。因為高級語言來說(從實際設(shè)計上來說也是)每個類只能繼承一個類。在這個類中,你必須繼承或編寫出其所有子類的
說說反射的用途及實現(xiàn) Java反射機制主要用于實現(xiàn)以下功能。 (1)在運行時判斷任意一個對象所屬的類型。 (2)在運行時構(gòu)造任意一個類的對象。 (3)在運行時判斷任意一個類所具有的成員變量和方法。 (4)在運行時調(diào)用任意一個對象的方法,甚至可以調(diào)用private方法。 注意:上述功能都是在運行時環(huán)境中,而不是在編譯時環(huán)境中。
說說自定義注解的場景及實現(xiàn) restful下方法上定義@LoggedIn判斷是否需要登錄
HTTP 請求的 GET 與 POST 方式的區(qū)別
GET和POST是由HTTP協(xié)議定義的。在HTTP協(xié)議中,Method和Data(URL, Body, Header)是正交的兩個概念,也就是說,使用哪個Method與應用層的數(shù)據(jù)如何傳輸是沒有相互關(guān)系的。
HTTP沒有要求,如果Method是POST數(shù)據(jù)就要放在BODY中。也沒有要求,如果Method是GET,數(shù)據(jù)(參數(shù))就一定要放在URL中而不能放在BODY中。
那么,網(wǎng)上流傳甚廣的這個說法是從何而來的呢?我在HTML標準中,找到了相似的描述。這和網(wǎng)上流傳的說法一致。但是這只是HTML標準對HTTP協(xié)議的用法的約定。怎么能當成GET和POST的區(qū)別呢?
而且,現(xiàn)代的Web Server都是支持GET中包含BODY這樣的請求。雖然這種請求不可能從瀏覽器發(fā)出,但是現(xiàn)在的Web Server又不是只給瀏覽器用,已經(jīng)完全地超出了HTML服務(wù)器的范疇了。
HTTP協(xié)議明確地指出了,HTTP頭和Body都沒有長度的要求。而對于URL長度上的限制,有兩方面的原因造成:
session 與 cookie 區(qū)別
是一個在客戶端一個在服務(wù)端。因為Cookie存在客戶端所以用戶可以看見,所以也可以編輯偽造,不是十分安全。
Session過多的時候會消耗服務(wù)器資源,所以大型網(wǎng)站會有專門的Session服務(wù)器,而Cookie存在客戶端所以沒什么問題。
域的支持范圍不一樣,比方說http://a.com的Cookie在http://a.com下都能用,而http://www.a.com的Session在http://api.a.com下都不能用,解決這個問題的辦法是JSONP或者跨域資源共享。
session 分布式處理
1.基于數(shù)據(jù)庫的Session共享 2.基于NFS共享文件系統(tǒng) 3.基于memcached 的session,如何保證 memcached 本身的高可用性? 4. 基于resin/tomcat web容器本身的session復制機制 5. 基于TT/Redis 或 jbosscache 進行 session 共享。 6. 基于cookie 進行session共享
JDBC 流程
1)加載驅(qū)動程序。 2)建立連接。 3)創(chuàng)建語句。 4)執(zhí)行語句。 5)處理ResultSet
MVC 設(shè)計思想
equals 與 == 的區(qū)別 == 比較的是變量(棧)內(nèi)存中存放的對象的(堆)內(nèi)存地址,用來判斷兩個對象的地址是否相同,即是否是指相同一個對象。比較的是真正意義上的指針操作。 equals用來比較的是兩個對象的內(nèi)容是否相等,由于所有的類都是繼承自java.lang.Object類的,所以適用于所有對象,如果沒有對該方法進行覆蓋的話,調(diào)用的仍然是Object類中的方法,而Object中的equals方法返回的卻是==的判斷。
集合
List 和 Set 區(qū)別 List:1.可以允許重復的對象。 2.可以插入多個null元素。 3.是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。 4.常用的實現(xiàn)類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對于經(jīng)常需要從 List 中添加或刪除元素的場合更為合適。 Set:1.不允許重復對象 2. 無序容器,你無法保證每個元素的存儲順序,TreeSet通過 Comparator 或者 Comparable 維護了一個排序順序。 3. 只允許一個 null 元素 4.Set 接口最流行的幾個實現(xiàn)類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基于 HashMap 實現(xiàn)的 HashSet;TreeSet 還實現(xiàn)了 SortedSet 接口,因此 TreeSet 是一個根據(jù)其 compare() 和 compareTo() 的定義進行排序的有序容器。 1.Map不是collection的子接口或者實現(xiàn)類。Map是一個接口。 2.Map 的 每個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是唯一的。 3. TreeMap 也通過 Comparator 或者 Comparable 維護了一個排序順序。 4. Map 里你可以擁有隨意個 null 值但最多只能有一個 null 鍵。 5.Map 接口最流行的幾個實現(xiàn)類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)
Arraylist 與 LinkedList 區(qū)別
ArrayList 與 Vector 區(qū)別
HashMap 和 Hashtable 的區(qū)別 Hashtable和HashMap它們的性能方面的比較類似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。
HashSet 和 HashMap 區(qū)別 (1)HashSet是set的一個實現(xiàn)類,hashMap是Map的一個實現(xiàn)類,同時hashMap是hashTable的替代品(為什么后面會講到). (2)HashSet以對象作為元素,而HashMap以(key-value)的一組對象作為元素,且HashSet拒絕接受重復的對象.HashMap可以看作三個視圖:key的Set,value的Collection,Entry的Set。 這里HashSet就是其實就是HashMap的一個視圖。 HashSet內(nèi)部就是使用Hashmap實現(xiàn)的,和Hashmap不同的是它不需要Key和Value兩個值。 往hashset中插入對象其實只不過是內(nèi)部做了 public boolean add(Object o) { return map.put(o, PRESENT)==null; }
HashMap 和 ConcurrentHashMap 的區(qū)別
HashMap 的工作原理及代碼實現(xiàn)
ConcurrentHashMap 的工作原理及代碼實現(xiàn) jdk1.7中采用Segment + HashEntry的方式進行實現(xiàn) 1.8中放棄了Segment臃腫的設(shè)計,取而代之的是采用Node + CAS + Synchronized來保證并發(fā)安全進行實現(xiàn)
線程
1.創(chuàng)建線程的方式及實現(xiàn)
1)繼承Thread類創(chuàng)建線程 2)實現(xiàn)Runnable接口創(chuàng)建線程 3)使用Callable和Future創(chuàng)建線程
2.sleep() 、join()、yield()有什么區(qū)別
sleep() sleep()方法需要指定等待的時間,它可以讓當前正在執(zhí)行的線程在指定的時間內(nèi)暫停執(zhí)行,進入阻塞狀態(tài),該方法既可以讓其他同優(yōu)先級或者高優(yōu)先級的線程得到執(zhí)行的機會,也可以讓低優(yōu)先級的線程得到執(zhí)行機會。但是sleep()方法不會釋放“鎖標志”,也就是說如果有synchronized同步塊,其他線程仍然不能訪問共享數(shù)據(jù)。 wait() wait()方法需要和notify()及notifyAll()兩個方法一起介紹,這三個方法用于協(xié)調(diào)多個線程對共享數(shù)據(jù)的存取,所以必須在synchronized語句塊內(nèi)使用,也就是說,調(diào)用wait(),notify()和notifyAll()的任務(wù)在調(diào)用這些方法前必須擁有對象的鎖。注意,它們都是Object類的方法,而不是Thread類的方法。 wait()方法與sleep()方法的不同之處在于,wait()方法會釋放對象的“鎖標志”。當調(diào)用某一對象的wait()方法后,會使當前線程暫停執(zhí)行,并將當前線程放入對象等待池中,直到調(diào)用了notify()方法后,將從對象等待池中移出任意一個線程并放入鎖標志等待池中,只有鎖標志等待池中的線程可以獲取鎖標志,它們隨時準備爭奪鎖的擁有權(quán)。當調(diào)用了某個對象的notifyAll()方法,會將對象等待池中的所有線程都移動到該對象的鎖標志等待池。 除了使用notify()和notifyAll()方法,還可以使用帶毫秒?yún)?shù)的wait(long timeout)方法,效果是在延遲timeout毫秒后,被暫停的線程將被恢復到鎖標志等待池。 此外,wait(),notify()及notifyAll()只能在synchronized語句中使用,但是如果使用的是ReenTrantLock實現(xiàn)同步,該如何達到這三個方法的效果呢?解決方法是使用ReenTrantLock.newCondition()獲取一個Condition類對象,然后Condition的await(),signal()以及signalAll()分別對應上面的三個方法。
yield() yield()方法和sleep()方法類似,也不會釋放“鎖標志”,區(qū)別在于,它沒有參數(shù),即yield()方法只是使當前線程重新回到可執(zhí)行狀態(tài),所以執(zhí)行yield()的線程有可能在進入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行,另外yield()方法只能使同優(yōu)先級或者高優(yōu)先級的線程得到執(zhí)行機會,這也和sleep()方法不同。
join() join()方法會使當前線程等待調(diào)用join()方法的線程結(jié)束后才能繼續(xù)執(zhí)行 3.說說 CountDownLatch 原理 https://www.jianshu.com/p/38c39e00ee4c 4.說說 CyclicBarrier 原理 https://www.jianshu.com/p/060761df128b 說說 Semaphore 原理
說說 Exchanger 原理 https://www.jianshu.com/p/1eab24ca3a22 說說 CountDownLatch 與 CyclicBarrier 區(qū)別
ThreadLocal 原理分析
ThreadLocal提供了set和get訪問器用來訪問與當前線程相關(guān)聯(lián)的線程局部變量。 可以從ThreadLocal的get函數(shù)中看出來,其中g(shù)etmap函數(shù)是用t作為參數(shù),這里t就是當前執(zhí)行的線程。
從而得知,get函數(shù)就是從當前線程的threadlocalmap中取出當前線程對應的變量的副本【注意,變量是保存在線程中的,而不是保存在ThreadLocal變量中】。當前線程中,有一個變量引用名字是threadLocals,這個引用是在ThreadLocal類中createmap函數(shù)內(nèi)初始化的。每個線程都有一個這樣的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal對象聲明的變量類型作為參數(shù)。這樣,我們所使用的ThreadLocal變量的實際數(shù)據(jù),通過get函數(shù)取值的時候,就是通過取出Thread中threadLocals引用的map,然后從這個map中根據(jù)當前threadLocal作為參數(shù),取出數(shù)據(jù)。現(xiàn)在,變量的副本從哪里取出來的(本文章提出的第一個問題)已經(jīng)確認解決了。
ThreadLocal操作值的時候是取得當前線程的ThreadLocalMap對象,然后把值設(shè)置到了這個對象中,這樣對于不同的線程得到的就是不同的ThreadLocalMap,那么向其中保存值或者修改值都只是會影響到當前線程,這樣就保證了線程安全。
講講線程池的實現(xiàn)原理
Java線程池的工廠類:Executors類, 初始化4種類型的線程池: newFixedThreadPool() 說明:初始化一個指定線程數(shù)的線程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作為阻塞隊列 特點:即使當線程池沒有可執(zhí)行任務(wù)時,也不會釋放線程。 newCachedThreadPool() 說明:初始化一個可以緩存線程的線程池,默認緩存60s,線程池的線程數(shù)可達到Integer.MAX_VALUE,即2147483647,內(nèi)部使用SynchronousQueue作為阻塞隊列; 特點:在沒有任務(wù)執(zhí)行時,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源;當提交新任務(wù)時,如果沒有空閑線程,則創(chuàng)建新線程執(zhí)行任務(wù),會導致一定的系統(tǒng)開銷; 因此,使用時要注意控制并發(fā)的任務(wù)數(shù),防止因創(chuàng)建大量的線程導致而降低性能。 newSingleThreadExecutor() 說明:初始化只有一個線程的線程池,內(nèi)部使用LinkedBlockingQueue作為阻塞隊列。 特點:如果該線程異常結(jié)束,會重新創(chuàng)建一個新的線程繼續(xù)執(zhí)行任務(wù),唯一的線程可以保證所提交任務(wù)的順序執(zhí)行 newScheduledThreadPool() 特定:初始化的線程池可以在指定的時間內(nèi)周期性的執(zhí)行所提交的任務(wù),在實際的業(yè)務(wù)場景中可以使用該線程池定期的同步數(shù)據(jù)。 總結(jié):除了newScheduledThreadPool的內(nèi)部實現(xiàn)特殊一點之外,其它線程池內(nèi)部都是基于ThreadPoolExecutor類(Executor的子類)實現(xiàn)的。
線程池的幾種方式
ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,timeUnit,workQueue,threadFactory,handle);
方法參數(shù): corePoolSize:核心線程數(shù) maxPoolSize:最大線程數(shù) keepAliveTime:線程存活時間(在corePore<*<maxPoolSize情況下有用) timeUnit:存活時間的時間單位 workQueue:阻塞隊列(用來保存等待被執(zhí)行的任務(wù)) 注:關(guān)于workQueue參數(shù)的取值,JDK提供了4種阻塞隊列類型供選擇: ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊列,按FIFO排序任務(wù); inkedBlockingQuene:基于鏈表結(jié)構(gòu)的阻塞隊列,按FIFO排序任務(wù),吞吐量通常要高于
SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于ArrayBlockingQuene; PriorityBlockingQuene:具有優(yōu)先級的無界阻塞隊列; threadFactory:線程工廠,主要用來創(chuàng)建線程; handler:表示當拒絕處理任務(wù)時的策略,有以下四種取值 注:當線程池的飽和策略,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續(xù)提交任務(wù),必須采取一種策略處理該任務(wù),線程池提供了4種策略: ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復此過程) ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù) 當然也可以根據(jù)應用場景實現(xiàn)RejectedExecutionHandler接口,自定義飽和策略,如記錄日志或持久化存儲不能處理的任務(wù)。
線程的生命周期 當線程被創(chuàng)建并啟動以后,它既不是一啟動就進入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。在線程的生命周期中,它要經(jīng)過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態(tài)。尤其是當線程啟動以后,它不可能一直"霸占"著CPU獨自運行,所以CPU需要在多條線程之間切換,于是線程狀態(tài)也會多次在運行、阻塞之間切換 鎖機制 說說線程安全問題
volatile 實現(xiàn)原理 volatile 關(guān)鍵字,具有兩個特性:1. 內(nèi)存的可見性,2. 禁止指令重排序優(yōu)化。 內(nèi)存可見性是指:被 volatile 關(guān)鍵字修飾的變量,當線程要對這個變量執(zhí)行的寫操作,都不會寫入本地緩存,而是直接刷入主內(nèi)存中。當線程讀取被 volatile 關(guān)鍵字修飾的變量時,也是直接從主內(nèi)存中讀取。(簡單的說,一個線程修改的狀態(tài)對另一個線程是可見的)。注意:volatile 不能保證原子性。 禁止指令重排序優(yōu)化:有volatile修飾的變量,賦值后多執(zhí)行了一個 “l(fā)oad addl $0x0, (%esp)” 操作,這個操作相當于一個內(nèi)存屏障,保證指令重排序時不會把后面的指令重排序到內(nèi)存屏障之前的位置。
synchronize 實現(xiàn)原理 synchronized 代碼塊是通過 monitorenter 和 monitorexit 指令實現(xiàn)的。synchronized 方法雖然在 vm 字節(jié)碼層面并沒有任何特別的指令來實現(xiàn)被 synchronized 修飾的方法,而是在 Class 文件的方法表中將該方法的 access_flags 字段中的 synchronized 標志位置1,表示該方法是同步方法。鎖的實現(xiàn)有偏向鎖、輕量級鎖和重量級鎖,其中偏向鎖和輕量級鎖是 JDK 針對鎖的優(yōu)化措施。在多線程的競爭下鎖會升級,依次從偏向鎖 -> 輕量級鎖 -> 重量級鎖,這里的鎖只能升級但不能降級。在 Java 對象頭中的 Mark Word 中存儲了關(guān)于鎖的標志位,其中:無鎖和偏向鎖為 00, 輕量級鎖為 01,重量級鎖為 10。 引入偏向鎖主要目的是:為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執(zhí)行路徑(在無競爭的情況下把整個同步都消除掉,連 CAS 操作都不做了)。 引入輕量級鎖的主要目的是:在沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗(在無競爭的情況下使用 CAS 操作去消除同步使用的互斥量)。 重量級鎖通過對象內(nèi)部的監(jiān)視器(monitor)實現(xiàn),其中 monitor 的本質(zhì)是依賴于底層操作系統(tǒng)的 Mutex Lock 實現(xiàn),操作系統(tǒng)實現(xiàn)線程之間的切換需要從用戶態(tài)到內(nèi)核態(tài)的切換,切換成本非常高。
synchronized 與 lock 的區(qū)別 兩者區(qū)別: 1.首先synchronized是java內(nèi)置關(guān)鍵字,在jvm層面,Lock是個java類; 2.synchronized無法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖; 3.synchronized會自動釋放鎖(a 線程執(zhí)行完同步代碼會釋放鎖 ;b 線程執(zhí)行過程中發(fā)生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖; 4.用synchronized關(guān)鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結(jié)束了; 5.synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可) 6.Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
CAS 樂觀鎖 樂觀鎖 樂觀鎖( Optimistic Locking)其實是一種思想。相對悲觀鎖而言,樂觀鎖假設(shè)認為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進行檢測,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。 上面提到的樂觀鎖的概念中其實已經(jīng)闡述了他的具體實現(xiàn)細節(jié):主要就是兩個步驟:沖突檢測和數(shù)據(jù)更新。其實現(xiàn)方式有一種比較典型的就是Compare and Swap(CAS)。 CAS CAS是項樂觀鎖技術(shù),當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。 CAS 操作包含三個操作數(shù) —— 內(nèi)存位置(V)、預期原值(A)和新值(B)。如果內(nèi)存位置的值與預期原值相匹配,那么處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現(xiàn)在的值即可。”這其實和樂觀鎖的沖突檢查+數(shù)據(jù)更新的原理是一樣的。
ABA 問題
樂觀鎖用到的機制就是CAS,Compare and Swap。 CAS有3個操作數(shù),內(nèi)存值V,舊的預期值A(chǔ),要修改的新值B。當且僅當預期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。 樂觀鎖的業(yè)務(wù)場景及實現(xiàn)方式 CAS看起來很爽,但是會導致“ABA問題”。 CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某時刻的數(shù)據(jù),而在下時刻比較并替換,那么在這個時間差類會導致數(shù)據(jù)的變化。 比如說一個線程one從內(nèi)存位置V中取出A,這時候另一個線程two也從內(nèi)存中取出A,并且two進行了一些操作變成了B,然后two又將V位置的數(shù)據(jù)變成A,這時候線程one進行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功。盡管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。如果鏈表的頭在變化了兩次后恢復了原值,但是不代表鏈表就沒有變化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。這允許一對變化的元素進行原子操作。
核心篇
數(shù)據(jù)存儲
MySQL 索引使用的注意事項
說說反模式設(shè)計
說說分庫與分表設(shè)計
分庫與分表帶來的分布式困境與應對之策
說說 SQL 優(yōu)化之道
1、應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。 2、對查詢進行優(yōu)化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。 3、應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如: select id from t where num is null 可以在num上設(shè)置默認值0,確保表中num列沒有null值,然后這樣查詢: select id from t where num=0 4、盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如: select id from t where num=10 or num=20 可以這樣查詢: select id from t where num=10 union all select id from t where num=20 5、下面的查詢也將導致全表掃描:(不能前置百分號) select id from t where name like ‘�c%’ 若要提高效率,可以考慮全文檢索。 6、in 和 not in 也要慎用,否則會導致全表掃描,如: select id from t where num in(1,2,3) 對于連續(xù)的數(shù)值,能用 between 就不要用 in 了: select id from t where num between 1 and 3 7、如果在 where 子句中使用參數(shù),也會導致全表掃。因為SQL只有在運行時才會解析局部變量,但優(yōu)化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描: select id from t where num=@num 可以改為強制查詢使用索引: select id from t with(index(索引名)) where num=@num 8、應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如: select id from t where num/2=100 應改為: select id from t where num=100*2 9、應盡量避免在where子句中對字段進行函數(shù)操作,這將導致引擎放棄使用索引而進行全表掃描。如: select id from t where substring(name,1,3)=’abc’–name以abc開頭的id select id from t where datediff(day,createdate,’2005-11-30′)=0–’2005-11-30′生成的id 應改為: select id from t where name like ‘a(chǎn)bc%’ select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′ 10、不要在 where 子句中的“=”左邊進行函數(shù)、算術(shù)運算或其他表達式運算,否則系統(tǒng)將可能無法正確使用索引。 11、在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引,否則該索引將不會被使 用,并且應盡可能的讓字段順序與索引順序相一致。 12、不要寫一些沒有意義的查詢,如需要生成一個空表結(jié)構(gòu): select col1,col2 into #t from t where 1=0 這類代碼不會返回任何結(jié)果集,但是會消耗系統(tǒng)資源的,應改成這樣: create table #t(…) 13、很多時候用 exists 代替 in 是一個好的選擇: select num from a where num in(select num from b) 用下面的語句替換: select num from a where exists(select 1 from b where num=a.num) 14、并不是所有索引對查詢都有效,SQL是根據(jù)表中數(shù)據(jù)來進行查詢優(yōu)化的,當索引列有大量數(shù)據(jù)重復時,SQL查詢可能不會去利用索引,如一表中有字段 sex,male、female幾乎各一半,那么即使在sex上建了索引也對查詢效率起不了作用。 15、索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數(shù)最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。 16.應盡可能的避免更新 clustered 索引數(shù)據(jù)列,因為 clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調(diào)整,會耗費相當大的資源。若應用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列,那么需要考慮是否應將該索引建為 clustered 索引。 17、盡量使用數(shù)字型字段,若只含數(shù)值信息的字段盡量不要設(shè)計為字符型,這會降低查詢和連接的性能,并會增加存儲開銷。這是因為引擎在處理查詢和連接時會 逐個比較字符串中每一個字符,而對于數(shù)字型而言只需要比較一次就夠了。 18、盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小,可以節(jié)省存儲空間,其次對于查詢來說,在一個相對較小的字段內(nèi)搜索效率顯然要高些。 19、任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。 20、盡量使用表變量來代替臨時表。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)。 21、避免頻繁創(chuàng)建和刪除臨時表,以減少系統(tǒng)表資源的消耗。 22、臨時表并不是不可使用,適當?shù)厥褂盟鼈兛梢允鼓承├谈行?#xff0c;例如,當需要重復引用大型表或常用表中的某個數(shù)據(jù)集時。但是,對于一次性事件,最好使 用導出表。 23、在新建臨時表時,如果一次性插入數(shù)據(jù)量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數(shù)據(jù)量不大,為了緩和系統(tǒng)表的資源,應先create table,然后insert。 24、如果使用到了臨時表,在存儲過程的最后務(wù)必將所有的臨時表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統(tǒng)表的較長時間鎖定。 25、盡量避免使用游標,因為游標的效率較差,如果游標操作的數(shù)據(jù)超過1萬行,那么就應該考慮改寫。 26、使用基于游標的方法或臨時表方法之前,應先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。 27、與臨時表一樣,游標并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 游標通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時。在結(jié)果集中包括“合計”的例程通常要比使用游標執(zhí)行的速度快。如果開發(fā)時 間允許,基于游標的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。 28、在所有的存儲過程和觸發(fā)器的開始處設(shè)置 SET NOCOUNT ON ,在結(jié)束時設(shè)置 SET NOCOUNT OFF 。無需在執(zhí)行存儲過程和觸發(fā)器的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息。 29、盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大,應該考慮相應需求是否合理。 30、盡量避免大事務(wù)操作,提高系統(tǒng)并發(fā)能力。MySQL 遇到的死鎖問題
存儲引擎的 InnoDB 與 MyISAM
數(shù)據(jù)庫索引的原理
為什么要用 B-tree
聚集索引與非聚集索引的區(qū)別
limit 20000 加載很慢怎么解決
1.子查詢優(yōu)化法 先找出第一條數(shù)據(jù),然后大于等于這條數(shù)據(jù)的id就是要獲取的數(shù)據(jù) 2.倒排表優(yōu)化法 倒排表法類似建立索引,用一張表來維護頁數(shù),然后通過高效的連接得到數(shù)據(jù) 缺點:只適合數(shù)據(jù)數(shù)固定的情況,數(shù)據(jù)不能刪除,維護頁表困難 3.反向查找優(yōu)化法 當偏移超過一半記錄數(shù)的時候,先用排序,這樣偏移就反轉(zhuǎn)了 缺點:order by優(yōu)化比較麻煩,要增加索引,索引影響數(shù)據(jù)的修改效率,并且要知道總記錄數(shù),偏移大于數(shù)據(jù)的一半 4.limit限制優(yōu)化法 把limit偏移量限制低于某個數(shù)。。超過這個數(shù)等于沒數(shù)據(jù),我記得alibaba的dba說過他們是這樣做的 5.只查索引法選擇合適的分布式主鍵方案
最常見的方式。利用數(shù)據(jù)庫,全數(shù)據(jù)庫唯一。
優(yōu)點:
1)簡單,代碼方便,性能可以接受。
2)數(shù)字ID天然排序,對分頁或者需要排序的結(jié)果很有幫助。
缺點: 1)不同數(shù)據(jù)庫語法和實現(xiàn)不同,數(shù)據(jù)庫遷移的時候或多數(shù)據(jù)庫版本支持的時候需要處理。 2)在單個數(shù)據(jù)庫或讀寫分離或一主多從的情況下,只有一個主庫可以生成。有單點故障的風險。 3)在性能達不到要求的情況下,比較難于擴展。 4)如果遇見多個系統(tǒng)需要合并或者涉及到數(shù)據(jù)遷移會相當痛苦。 5)分表分庫的時候會有麻煩。 優(yōu)化方案: 1)針對主庫單點,如果有多個Master庫,則每個Master庫設(shè)置的起始數(shù)字不一樣,步長一樣,可以是Master的個數(shù)。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12。這樣就可以有效生成集群中的唯一ID,也可以大大降低ID生成數(shù)據(jù)庫操作的負載。 2. UUID 常見的方式。可以利用數(shù)據(jù)庫也可以利用程序生成,一般來說全球唯一。 優(yōu)點 1)簡單,代碼方便。 2)生成ID性能非常好,基本不會有性能問題。 3)全球唯一,在遇見數(shù)據(jù)遷移,系統(tǒng)數(shù)據(jù)合并,或者數(shù)據(jù)庫變更等情況下,可以從容應對。 缺點: 1)沒有排序,無法保證趨勢遞增。 2)UUID往往是使用字符串存儲,查詢的效率比較低。 3)存儲空間比較大,如果是海量數(shù)據(jù)庫,就需要考慮存儲量的問題。 4)傳輸數(shù)據(jù)量大 5)不可讀。 4. Redis生成ID 當使用數(shù)據(jù)庫來生成ID性能不夠要求的時候,我們可以嘗試使用Redis來生成ID。這主要依賴于Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現(xiàn)。 5. Twitter的snowflake算法
ObjectId 規(guī)則
聊聊 MongoDB 使用場景
倒排索引
聊聊 ElasticSearch 使用場景
緩存使用
Redis 有哪些類型 Redis 內(nèi)部結(jié)構(gòu) 聊聊 Redis 使用場景 Redis 持久化機制 Redis 如何實現(xiàn)持久化 Redis 集群方案與實現(xiàn) Redis 為什么是單線程的 緩存奔潰 緩存降級 使用緩存的合理性問題 消息隊列 消息隊列的使用場景 消息的重發(fā)補償解決思路 消息的冪等性解決思路 消息的堆積解決思路 自己如何實現(xiàn)消息隊列 如何保證消息的有序性框架
Spring
BeanFactory 和 ApplicationContext 有什么區(qū)別 BeanFacotry是spring中比較原始的Factory。如XMLBeanFactory就是一種典型的BeanFactory。原始的BeanFactory無法支持spring的許多插件,如AOP功能、Web應用等。 ApplicationContext接口,它由BeanFactory接口派生而來,因而提供BeanFactory所有的功能。ApplicationContext以一種更向面向框架的方式工作以及對上下文進行分層和實現(xiàn)繼承,ApplicationContext包還提供了以下的功能: ? MessageSource, 提供國際化的消息訪問 ? 資源訪問,如URL和文件 ? 事件傳播 ? 載入多個(有繼承關(guān)系)上下文 ,使得每一個上下文都專注于一個特定的層次,比如應用的web層 Spring Bean 的生命周期 Spring上下文中的Bean也類似,【Spring上下文的生命周期】 1. 實例化一個Bean,也就是我們通常說的new 2. 按照Spring上下文對實例化的Bean進行配置,也就是IOC注入 3. 如果這個Bean實現(xiàn)了BeanNameAware接口,會調(diào)用它實現(xiàn)的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID 4. 如果這個Bean實現(xiàn)了BeanFactoryAware接口,會調(diào)用它實現(xiàn)的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個方法獲取到其他Bean) 5. 如果這個Bean實現(xiàn)了ApplicationContextAware接口,會調(diào)用setApplicationContext(ApplicationContext)方法,傳入Spring上下文,該方式同樣可以實現(xiàn)步驟4,但比4更好,以為ApplicationContext是BeanFactory的子接口,有更多的實現(xiàn)方法 6. 如果這個Bean關(guān)聯(lián)了BeanPostProcessor接口,將會調(diào)用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經(jīng)常被用作是Bean內(nèi)容的更改,并且由于這個是在Bean初始化結(jié)束時調(diào)用After方法,也可用于內(nèi)存或緩存技術(shù) 7. 如果這個Bean在Spring配置文件中配置了init-method屬性會自動調(diào)用其配置的初始化方法 8. 如果這個Bean關(guān)聯(lián)了BeanPostProcessor接口,將會調(diào)用postAfterInitialization(Object obj, String s)方法 注意:以上工作完成以后就可以用這個Bean了,那這個Bean是一個single的,所以一般情況下我們調(diào)用同一個ID的Bean會是在內(nèi)容地址相同的實例 9. 當Bean不再需要時,會經(jīng)過清理階段,如果Bean實現(xiàn)了DisposableBean接口,會調(diào)用其實現(xiàn)的destroy方法 10. 最后,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調(diào)用其配置的銷毀方法 以上10步驟可以作為面試或者筆試的模板,另外我們這里描述的是應用Spring上下文Bean的生命周期,如果應用Spring的工廠也就是BeanFactory的話去掉第5步就Ok了Spring IOC 如何實現(xiàn)IOC容器就是具有依賴注入功能的容器,IOC容器負責實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。應用程序無需直接在代碼中new相關(guān)的對象,應用程序由IOC容器進行組裝。在Spring中BeanFactory是IOC容器的實際代表者。 Spring IOC容器如何知道哪些是它管理的對象呢?這就需要配置文件,Spring IOC容器通過讀取配置文件中的配置元數(shù)據(jù),通過元數(shù)據(jù)對應用中的各個對象進行實例化及裝配。一般使用基于xml配置文件進行配置元數(shù)據(jù),而且Spring與配置文件完全解耦的,可以使用其他任何可能的方式進行配置元數(shù)據(jù),比如注解、基于java文件的、基于屬性文件的配置都可以。說說 Spring AOP Spring AOP 實現(xiàn)原理 1.IOC 許多應用都是通過彼此間的相互合作來實現(xiàn)業(yè)務(wù)邏輯的,如類A要調(diào)用類B的方法,以前我們都是在類A中,通過自身new一個類B,然后在調(diào)用類B的方法,現(xiàn)在我們把new類B的事情交給spring來做,在我們調(diào)用的時候,容器會為我們實例化。 2. IOC容器的初始化過程 資源定位,即定義bean的xml-------》載入--------》IOC容器注冊,注冊beanDefinition IOC容器的初始化過程,一般不包含bean的依賴注入的實現(xiàn),在spring IOC設(shè)計中,bean的注冊和依賴注入是兩個過程,,依賴注入一般發(fā)生在應用第一次索取bean的時候,但是也可以在xm中配置,在容器初始化的時候,這個bean就完成了初始化。 3. 三種注入方式,構(gòu)造器、接口、set注入,我們常用的是set注入 4. bean是如何創(chuàng)建--- 工廠模式 5. 數(shù)據(jù)是如何注入-------反射 6.AOP 面向切面編程,在我們的應用中,經(jīng)常需要做一些事情,但是這些事情與核心業(yè)務(wù)無關(guān),比如,要記錄所有update*方法的執(zhí)行時間時間,操作人等等信息,記錄到日志, 通過spring的AOP技術(shù),就可以在不修改update*的代碼的情況下完成該需求。 7.AOP的實現(xiàn)原理------代理動態(tài)代理(cglib 與 JDK) 靜態(tài)代理是通過在代碼中顯式定義一個業(yè)務(wù)實現(xiàn)類一個代理,在代理類中對同名的業(yè)務(wù)方法進行包裝,用戶通過代理類調(diào)用被包裝過的業(yè)務(wù)方法; JDK動態(tài)代理是通過接口中的方法名,在動態(tài)生成的代理類中調(diào)用業(yè)務(wù)實現(xiàn)類的同名方法; CGlib動態(tài)代理是通過繼承業(yè)務(wù)類,生成的動態(tài)代理類是業(yè)務(wù)類的子類,通過重寫業(yè)務(wù)方法進行代理;Spring 事務(wù)實現(xiàn)方式 實現(xiàn)spring事務(wù)的四種方式分別為: (1)編程式事務(wù)管理:需要手動編寫代碼,在實際開發(fā)中很少使用 (2)聲明式事務(wù)管理: (2.1)基于TransactionProxyFactoryBean的方式,需要為每個進行事務(wù)管理的類做相應配置 (2.2)基于AspectJ的XML方式,不需要改動類,在XML文件中配置好即可 (2.3)基于注解的方式,配置簡單,需要在業(yè)務(wù)層類中添加注解Spring 事務(wù)底層原理 工作原理及實現(xiàn) a、劃分處理單元——IOC 由于spring解決的問題是對單個數(shù)據(jù)庫進行局部事務(wù)處理的,具體的實現(xiàn)首相用spring中的IOC劃分了事務(wù)處理單元。并且將對事務(wù)的各種配置放到了ioc容器中(設(shè)置事務(wù)管理器,設(shè)置事務(wù)的傳播特性及隔離機制)。 b、AOP攔截需要進行事務(wù)處理的類 Spring事務(wù)處理模塊是通過AOP功能來實現(xiàn)聲明式事務(wù)處理的,具體操作(比如事務(wù)實行的配置和讀取,事務(wù)對象的抽象),用TransactionProxyFactoryBean接口來使用AOP功能,生成proxy代理對象,通過TransactionInterceptor完成對代理方法的攔截,將事務(wù)處理的功能編織到攔截的方法中。 讀取ioc容器事務(wù)配置屬性,轉(zhuǎn)化為spring事務(wù)處理需要的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(TransactionAttributeSourceAdvisor),轉(zhuǎn)化為TransactionAttribute表示的數(shù)據(jù)對象。 c、對事物處理實現(xiàn)(事務(wù)的生成、提交、回滾、掛起) spring委托給具體的事務(wù)處理器實現(xiàn)。實現(xiàn)了一個抽象和適配。適配的具體事務(wù)處理器:DataSource數(shù)據(jù)源支持、hibernate數(shù)據(jù)源事務(wù)處理支持、JDO數(shù)據(jù)源事務(wù)處理支持,JPA、JTA數(shù)據(jù)源事務(wù)處理支持。這些支持都是通過設(shè)計PlatformTransactionManager、AbstractPlatforTransaction一系列事務(wù)處理的支持。 為常用數(shù)據(jù)源支持提供了一系列的TransactionManager。 d、結(jié)合 PlatformTransactionManager實現(xiàn)了TransactionInterception接口,讓其與TransactionProxyFactoryBean結(jié)合起來,形成一個Spring聲明式事務(wù)處理的設(shè)計體系。 如何自定義注解實現(xiàn)功能Spring MVC 運行流程Spring MVC 啟動流程Spring 的單例實現(xiàn)原理 大家真正要記住的是Spring對bean實例的創(chuàng)建是采用單例注冊表的方式進行實現(xiàn)的,而這個注冊表的緩存是HashMap對象,如果配置文件中的配置信息不要求使用單例,Spring會采用新建實例的方式返回對象實例Spring 框架中用到了哪些設(shè)計模式 Spring框架中使用到了大量的設(shè)計模式,下面列舉了比較有代表性的: 代理模式—在AOP和remoting中被用的比較多。 單例模式—在spring配置文件中定義的bean默認為單例模式。 模板方法—用來解決代碼重復的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。 工廠模式—BeanFactory用來創(chuàng)建對象的實例。 適配器--spring aop 裝飾器--spring data hashmapper 觀察者-- spring 時間驅(qū)動模型 回調(diào)--Spring ResourceLoaderAware回調(diào)接口 Spring 其他產(chǎn)品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)Netty
為什么選擇 Netty 說說業(yè)務(wù)中,Netty 的使用場景原生的 NIO 在 JDK 1.7 版本存在 epoll bug https://www.cnblogs.com/JAYIT/p/8241634.html Selector BUG出現(xiàn)的原因 若Selector的輪詢結(jié)果為空,也沒有wakeup或新消息處理,則發(fā)生空輪詢,CPU使用率100%, Netty的解決辦法 對Selector的select操作周期進行統(tǒng)計,每完成一次空的select操作進行一次計數(shù), 若在某個周期內(nèi)連續(xù)發(fā)生N次空輪詢,則觸發(fā)了epoll死循環(huán)bug。 重建Selector,判斷是否是其他線程發(fā)起的重建請求,若不是則將原SocketChannel從舊的Selector上去除注冊,重新注冊到新的Selector上,并將原來的Selector關(guān)閉。什么是TCP 粘包/拆包、TCP粘包/拆包的解決辦法 發(fā)生TCP粘包或拆包有很多原因,現(xiàn)列出常見的幾點,可能不全面,歡迎補充, 1、要發(fā)送的數(shù)據(jù)大于TCP發(fā)送緩沖區(qū)剩余空間大小,將會發(fā)生拆包。 2、待發(fā)送數(shù)據(jù)大于MSS(最大報文長度),TCP在傳輸前將進行拆包。 3、要發(fā)送的數(shù)據(jù)小于TCP發(fā)送緩沖區(qū)的大小,TCP將多次寫入緩沖區(qū)的數(shù)據(jù)一次發(fā)送出去,將會發(fā)生粘包。 4、接收數(shù)據(jù)端的應用層沒有及時讀取接收緩沖區(qū)中的數(shù)據(jù),將發(fā)生粘包。 粘包、拆包解決辦法 通過以上分析,我們清楚了粘包或拆包發(fā)生的原因,那么如何解決這個問題呢?解決問題的關(guān)鍵在于如何給每個數(shù)據(jù)包添加邊界信息,常用的方法有如下幾個: 1、發(fā)送端給每個數(shù)據(jù)包添加包首部,首部中應該至少包含數(shù)據(jù)包的長度,這樣接收端在接收到數(shù)據(jù)后,通過讀取包首部的長度字段,便知道每一個數(shù)據(jù)包的實際長度了。 2、發(fā)送端將每個數(shù)據(jù)包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩沖區(qū)中讀取固定長度的數(shù)據(jù)就自然而然的把每個數(shù)據(jù)包拆分開來。 3、可以在數(shù)據(jù)包之間設(shè)置邊界,如添加特殊符號,這樣,接收端通過這個邊界就可以將不同的數(shù)據(jù)包拆分開。Netty 線程模型 Netty通過Reactor模型基于多路復用器接收并處理用戶請求,內(nèi)部實現(xiàn)了兩個線程池,boss線程池和work線程池,其中boss線程池的線程負責處理請求的accept事件,當接收到accept事件的請求時,把對應的socket封裝到一個NioSocketChannel中,并交給work線程池,其中work線程池負責請求的read和write事件,由對應的Handler處理。 單線程模型:所有I/O操作都由一個線程完成,即多路復用、事件分發(fā)和處理都是在一個Reactor線程上完成的。既要接收客戶端的連接請求,向服務(wù)端發(fā)起連接,又要發(fā)送/讀取請求或應答/響應消息。一個NIO 線程同時處理成百上千的鏈路,性能上無法支撐,速度慢,若線程進入死循環(huán),整個程序不可用,對于高負載、大并發(fā)的應用場景不合適。 多線程模型:有一個NIO 線程(Acceptor) 只負責監(jiān)聽服務(wù)端,接收客戶端的TCP 連接請求;NIO 線程池負責網(wǎng)絡(luò)IO 的操作,即消息的讀取、解碼、編碼和發(fā)送;1 個NIO 線程可以同時處理N 條鏈路,但是1 個鏈路只對應1 個NIO 線程,這是為了防止發(fā)生并發(fā)操作問題。但在并發(fā)百萬客戶端連接或需要安全認證時,一個Acceptor 線程可能會存在性能不足問題。 主從多線程模型:Acceptor 線程用于綁定監(jiān)聽端口,接收客戶端連接,將SocketChannel 從主線程池的Reactor 線程的多路復用器上移除,重新注冊到Sub 線程池的線程上,用于處理I/O 的讀寫等操作,從而保證mainReactor只負責接入認證、握手等操作;說說 Netty 的零拷貝 Netty的零拷貝實現(xiàn)? Netty的接收和發(fā)送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內(nèi)存進行Socket讀寫,不需要進行字節(jié)緩沖區(qū)的二次拷貝。堆內(nèi)存多了一次內(nèi)存拷貝,JVM會將堆內(nèi)存Buffer拷貝一份到直接內(nèi)存中,然后才寫入Socket中。ByteBuffer由ChannelConfig分配,而ChannelConfig創(chuàng)建ByteBufAllocator默認使用Direct BufferCompositeByteBuf 類可以將多個 ByteBuf 合并為一個邏輯上的 ByteBuf, 避免了傳統(tǒng)通過內(nèi)存拷貝的方式將幾個小Buffer合并成一個大的Buffer。addComponents方法將 header 與 body 合并為一個邏輯上的 ByteBuf, 這兩個 ByteBuf 在CompositeByteBuf 內(nèi)部都是單獨存在的, CompositeByteBuf 只是邏輯上是一個整體通過 FileRegion 包裝的FileChannel.tranferTo方法 實現(xiàn)文件傳輸, 可以直接將文件緩沖區(qū)的數(shù)據(jù)發(fā)送到目標 Channel,避免了傳統(tǒng)通過循環(huán)write方式導致的內(nèi)存拷貝問題。通過 wrap方法, 我們可以將 byte[] 數(shù)組、ByteBuf、ByteBuffer等包裝成一個 Netty ByteBuf 對象, 進而避免了拷貝操作。Selector BUG:若Selector的輪詢結(jié)果為空,也沒有wakeup或新消息處理,則發(fā)生空輪詢,CPU使用率100%,Netty的解決辦法:對Selector的select操作周期進行統(tǒng)計,每完成一次空的select操作進行一次計數(shù),若在某個周期內(nèi)連續(xù)發(fā)生N次空輪詢,則觸發(fā)了epoll死循環(huán)bug。重建Selector,判斷是否是其他線程發(fā)起的重建請求,若不是則將原SocketChannel從舊的Selector上去除注冊,重新注冊到新的Selector上,并將原來的Selector關(guān)閉。Netty 內(nèi)部執(zhí)行流程 服務(wù)端依次發(fā)生的步驟 建立服務(wù)端監(jiān)聽套接字ServerSocketChannel,以及對應的管道pipeline; 啟動boss線程,將ServerSocketChannel注冊到boss線程持有的selector中,并將注冊返回的selectionKey賦值給ServerSocketChannel關(guān)聯(lián)的selectionKey變量; 在ServerSocketChannel對應的管道中觸發(fā)channelRegistered事件; 綁定IP和端口 觸發(fā)channelActive事件,并將ServerSocketChannel關(guān)聯(lián)的selectionKey的OP_ACCEPT位置為1。 客戶端發(fā)起connect請求后,boss線程正在運行的select循環(huán)檢測到了該ServerSocketChannel的ACCEPT事件就緒,則通過accept系統(tǒng)調(diào)用建立一個已連接套接字SocketChannel,并為其創(chuàng)建對應的管道; 在服務(wù)端監(jiān)聽套接字對應的管道中觸發(fā)channelRead事件; channelRead事件由ServerBootstrapAcceptor的channelRead方法響應:為已連接套接字對應的管道加入ChannelInitializer處理器;啟動一個worker線程,并將已連接套接字的注冊任務(wù)加入到worker線程的任務(wù)隊列中; worker線程執(zhí)行已連接套接字的注冊任務(wù):將已連接套接字注冊到worker線程持有的selector中,并將注冊返回的selectionKey賦值給已連接套接字關(guān)聯(lián)的selectionKey變量;在已連接套接字對應的管道中觸發(fā)channelRegistered事件;channelRegistered事件由ChannelInitializer的channelRegistered方法響應:將自定義的處理器(譬如EchoServerHandler)加入到已連接套接字對應的管道中;在已連接套接字對應的管道中觸發(fā)channelActive事件;channelActive事件由已連接套接字對應的管道中的inbound處理器的channelActive方法響應;將已連接套接字關(guān)聯(lián)的selectionKey的OP_READ位置為1;至此,worker線程關(guān)聯(lián)的selector就開始監(jiān)聽已連接套接字的READ事件了。 在worker線程運行的同時,Boss線程接著在服務(wù)端監(jiān)聽套接字對應的管道中觸發(fā)channelReadComplete事件。 客戶端向服務(wù)端發(fā)送消息后,worker線程正在運行的selector循環(huán)會檢測到已連接套接字的READ事件就緒。則通過read系統(tǒng)調(diào)用將消息從套接字的接受緩沖區(qū)中讀到AdaptiveRecvByteBufAllocator(可以自適應調(diào)整分配的緩存的大小)分配的緩存中; 在已連接套接字對應的管道中觸發(fā)channelRead事件; channelRead事件由EchoServerHandler處理器的channelRead方法響應:執(zhí)行write操作將消息存儲到ChannelOutboundBuffer中; 在已連接套接字對應的管道中觸發(fā)ChannelReadComplete事件; ChannelReadComplete事件由EchoServerHandler處理器的channelReadComplete方法響應:執(zhí)行flush操作將消息從ChannelOutboundBuffer中flush到套接字的發(fā)送緩沖區(qū)中; 客戶端依次發(fā)生的步驟 建立套接字SocketChannel,以及對應的管道pipeline; 啟動客戶端線程,將SocketChannel注冊到客戶端線程持有的selector中,并將注冊返回的selectionKey賦值給SocketChannel關(guān)聯(lián)的selectionKey變量; 觸發(fā)channelRegistered事件; channelRegistered事件由ChannelInitializer的channelRegistered方法響應:將客戶端自定義的處理器(譬如EchoClientHandler)按順序加入到管道中; 向服務(wù)端發(fā)起connect請求,并將SocketChannel關(guān)聯(lián)的selectionKey的OP_CONNECT位置為1; 開始三次握手,客戶端線程正在運行的select循環(huán)檢測到了該SocketChannel的CONNECT事件就緒,則將關(guān)聯(lián)的selectionKey的OP_CONNECT位置為0,再通過調(diào)用finishConnect完成連接的建立; 觸發(fā)channelActive事件; channelActive事件由EchoClientHandler的channelActive方法響應,通過調(diào)用ctx.writeAndFlush方法將消息發(fā)往服務(wù)端; 首先將消息存儲到ChannelOutboundBuffer中;(如果ChannelOutboundBuffer存儲的所有未flush的消息的大小超過高水位線writeBufferHighWaterMark(默認值為64 * 1024),則會觸發(fā)ChannelWritabilityChanged事件) 然后將消息從ChannelOutboundBuffer中flush到套接字的發(fā)送緩沖區(qū)中;(如果ChannelOutboundBuffer存儲的所有未flush的消息的大小小于低水位線,則會觸發(fā)ChannelWritabilityChanged事件)Netty 重連實現(xiàn) 使用 Netty 實現(xiàn)心跳機制的關(guān)鍵就是利用 IdleStateHandler 來產(chǎn)生對應的 idle 事件. 一般是客戶端負責發(fā)送心跳的 PING 消息, 因此客戶端注意關(guān)注 ALL_IDLE 事件, 在這個事件觸發(fā)后, 客戶端需要向服務(wù)器發(fā)送 PING 消息, 告訴服務(wù)器"我還存活著". 服務(wù)器是接收客戶端的 PING 消息的, 因此服務(wù)器關(guān)注的是 READER_IDLE 事件, 并且服務(wù)器的 READER_IDLE 間隔需要比客戶端的 ALL_IDLE 事件間隔大(例如客戶端ALL_IDLE 是5s 沒有讀寫時觸發(fā), 因此服務(wù)器的 READER_IDLE 可以設(shè)置為10s) 當服務(wù)器收到客戶端的 PING 消息時, 會發(fā)送一個 PONG 消息作為回復. 一個 PING-PONG 消息對就是一個心跳交互. 在client在連接服務(wù)器的方法上加上一個監(jiān)聽,當發(fā)生斷線的時候進行重新連接即可。unb微服務(wù)篇
微服務(wù) 前后端分離是如何做的 微服務(wù)哪些框架 你怎么理解 RPC 框架 說說 RPC 的實現(xiàn)原理 1)服務(wù)消費方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù); 2)client stub接收到調(diào)用后負責將方法、參數(shù)等組裝成能夠進行網(wǎng)絡(luò)傳輸?shù)南Ⅲw; 3)client stub找到服務(wù)地址,并將消息發(fā)送到服務(wù)端; 4)server stub收到消息后進行解碼; 5)server stub根據(jù)解碼結(jié)果調(diào)用本地的服務(wù); 6)本地服務(wù)執(zhí)行并將結(jié)果返回給server stub; 7)server stub將返回結(jié)果打包成消息并發(fā)送至消費方; 8)client stub接收到消息,并進行解碼; 9)服務(wù)消費方得到最終結(jié)果。 使用到的技術(shù) 1、動態(tài)代理 生成 client stub和server stub需要用到 Java 動態(tài)代理技術(shù) ,我們可以使用JDK原生的動態(tài)代理機制,可以使用一些開源字節(jié)碼工具框架 如:CgLib、Javassist等。2、序列化 為了能在網(wǎng)絡(luò)上傳輸和接收 Java對象,我們需要對它進行 序列化和反序列化操作。 * 序列化:將Java對象轉(zhuǎn)換成byte[]的過程,也就是編碼的過程; * 反序列化:將byte[]轉(zhuǎn)換成Java對象的過程;可以使用Java原生的序列化機制,但是效率非常低,推薦使用一些開源的、成熟的序列化技術(shù),例如:protobuf、Thrift、hessian、Kryo、Msgpack關(guān)于序列化工具性能比較可以參考:jvm-serializers3、NIO 當前很多RPC框架都直接基于netty這一IO通信框架,比如阿里巴巴的HSF、dubbo,Hadoop Avro,推薦使用Netty 作為底層通信框架。4、服務(wù)注冊中心 可選技術(shù): * Redis * Zookeeper * Consul * Etcd說說 Dubbo 的實現(xiàn)原理 dubbo作為rpc框架,實現(xiàn)的效果就是調(diào)用遠程的方法就像在本地調(diào)用一樣。如何做到呢?就是本地有對遠程方法的描述,包括方法名、參數(shù)、返回值,在dubbo中是遠程和本地使用同樣的接口;然后呢,要有對網(wǎng)絡(luò)通信的封裝,要對調(diào)用方來說通信細節(jié)是完全不可見的,網(wǎng)絡(luò)通信要做的就是將調(diào)用方法的屬性通過一定的協(xié)議(簡單來說就是消息格式)傳遞到服務(wù)端;服務(wù)端按照協(xié)議解析出調(diào)用的信息;執(zhí)行相應的方法;在將方法的返回值通過協(xié)議傳遞給客戶端;客戶端再解析;在調(diào)用方式上又可以分為同步調(diào)用和異步調(diào)用;簡單來說基本就這個過程你怎么理解 RESTful 說說如何設(shè)計一個良好的 API 如何理解 RESTful API 的冪等性如何保證接口的冪等性 全局唯一ID 如果使用全局唯一ID,就是根據(jù)業(yè)務(wù)的操作和內(nèi)容生成一個全局ID,在執(zhí)行操作前先根據(jù)這個全局唯一ID是否存在,來判斷這個操作是否已經(jīng)執(zhí)行。如果不存在則把全局ID,存儲到存儲系統(tǒng)中,比如數(shù)據(jù)庫、Redis等。如果存在則表示該方法已經(jīng)執(zhí)行。 從工程的角度來說,使用全局ID做冪等可以作為一個業(yè)務(wù)的基礎(chǔ)的微服務(wù)存在,在很多的微服務(wù)中都會用到這樣的服務(wù),在每個微服務(wù)中都完成這樣的功能,會存在工作量重復。另外打造一個高可靠的冪等服務(wù)還需要考慮很多問題,比如一臺機器雖然把全局ID先寫入了存儲,但是在寫入之后掛了,這就需要引入全局ID的超時機制。使用全局唯一ID是一個通用方案,可以支持插入、更新、刪除業(yè)務(wù)操作。但是這個方案看起來很美但是實現(xiàn)起來比較麻煩,下面的方案適用于特定的場景,但是實現(xiàn)起來比較簡單。 去重表這種方法適用于在業(yè)務(wù)中有唯一標的插入場景中,比如在以上的支付場景中,如果一個訂單只會支付一次,所以訂單ID可以作為唯一標識。這時,我們就可以建一張去重表,并且把唯一標識作為唯一索引,在我們實現(xiàn)時,把創(chuàng)建支付單據(jù)和寫入去去重表,放在一個事務(wù)中,如果重復創(chuàng)建,數(shù)據(jù)庫會拋出唯一約束異常,操作就會回滾。 插入或更新這種方法插入并且有唯一索引的情況,比如我們要關(guān)聯(lián)商品品類,其中商品的ID和品類的ID可以構(gòu)成唯一索引,并且在數(shù)據(jù)表中也增加了唯一索引。這時就可以使用InsertOrUpdate操作。在MySQL數(shù)據(jù)庫中如下:insert into goods_category (goods_id,category_id,create_time,update_time) values(#{goodsId},#{categoryId},now(),now()) on DUPLICATE KEY UPDATE update_time=now()多版本控制 這種方法適合在更新的場景中,比如我們要更新商品的名字,這時我們就可以在更新的接口中增加一個版本號,來做冪等說說 CAP 定理、 BASE 理論 CAP理論 一個經(jīng)典的分布式系統(tǒng)理論。CAP理論告訴我們:一個分布式系統(tǒng)不可能同時滿足一致性(C:Consistency)、可用性(A:Availability)和分區(qū)容錯性(P:Partition tolerance)這三個基本需求,最多只能同時滿足其中兩項。 1、一致性 在分布式環(huán)境下,一致性是指數(shù)據(jù)在多個副本之間能否保持一致的特性。在一致性的需求下,當一個系統(tǒng)在數(shù)據(jù)一致的狀態(tài)下執(zhí)行更新操作后,應該保證系統(tǒng)的數(shù)據(jù)仍然處于一直的狀態(tài)。 對于一個將數(shù)據(jù)副本分布在不同分布式節(jié)點上的系統(tǒng)來說,如果對第一個節(jié)點的數(shù)據(jù)進 行了更新操作并且更新成功后,卻沒有使得第二個節(jié)點上的數(shù)據(jù)得到相應的更新,于是在對第二個節(jié)點的數(shù)據(jù)進行讀取操作時,獲取的依然是老數(shù)據(jù)(或稱為臟數(shù) 據(jù)),這就是典型的分布式數(shù)據(jù)不一致的情況。在分布式系統(tǒng)中,如果能夠做到針對一個數(shù)據(jù)項的更新操作執(zhí)行成功后,所有的用戶都可以讀取到其最新的值,那么 這樣的系統(tǒng)就被認為具有強一致性 2、可用性 可用性是指系統(tǒng)提供的服務(wù)必須一直處于可用的狀態(tài),對于用戶的每一個操作請求總是能夠在有限的時間內(nèi)返回結(jié)果。這里的重點是"有限時間內(nèi)"和"返回結(jié)果"。 "有限時間內(nèi)"是指,對于用戶的一個操作請求,系統(tǒng)必須能夠在指定的時間內(nèi)返回對 應的處理結(jié)果,如果超過了這個時間范圍,那么系統(tǒng)就被認為是不可用的。另外,"有限的時間內(nèi)"是指系統(tǒng)設(shè)計之初就設(shè)計好的運行指標,通常不同系統(tǒng)之間有很 大的不同,無論如何,對于用戶請求,系統(tǒng)必須存在一個合理的響應時間,否則用戶便會對系統(tǒng)感到失望。 "返回結(jié)果"是可用性的另一個非常重要的指標,它要求系統(tǒng)在完成對用戶請求的處理后,返回一個正常的響應結(jié)果。正常的響應結(jié)果通常能夠明確地反映出隊請求的處理結(jié)果,即成功或失敗,而不是一個讓用戶感到困惑的返回結(jié)果。 3、分區(qū)容錯性 分區(qū)容錯性約束了一個分布式系統(tǒng)具有如下特性:分布式系統(tǒng)在遇到任何網(wǎng)絡(luò)分區(qū)故障的時候,仍然需要能夠保證對外提供滿足一致性和可用性的服務(wù),除非是整個網(wǎng)絡(luò)環(huán)境都發(fā)生了故障。 網(wǎng)絡(luò)分區(qū)是指在分布式系統(tǒng)中,不同的節(jié)點分布在不同的子網(wǎng)絡(luò)(機房或異地網(wǎng)絡(luò)) 中,由于一些特殊的原因?qū)е逻@些子網(wǎng)絡(luò)出現(xiàn)網(wǎng)絡(luò)不連通的狀況,但各個子網(wǎng)絡(luò)的內(nèi)部網(wǎng)絡(luò)是正常的,從而導致整個系統(tǒng)的網(wǎng)絡(luò)環(huán)境被切分成了若干個孤立的區(qū)域。 需要注意的是,組成一個分布式系統(tǒng)的每個節(jié)點的加入與退出都可以看作是一個特殊的網(wǎng)絡(luò)分區(qū)。 既然一個分布式系統(tǒng)無法同時滿足一致性、可用性、分區(qū)容錯性三個特點,所以我們就需要拋棄一樣: 用一張表格說明一下: 選 擇 說 明 CA 放棄分區(qū)容錯性,加強一致性和可用性,其實就是傳統(tǒng)的單機數(shù)據(jù)庫的選擇 AP 放棄一致性(這里說的一致性是強一致性),追求分區(qū)容錯性和可用性,這是很多分布式系統(tǒng)設(shè)計時的選擇,例如很多NoSQL系統(tǒng)就是如此 CP 放棄可用性,追求一致性和分區(qū)容錯性,基本不會選擇,網(wǎng)絡(luò)問題會直接讓整個系統(tǒng)不可用 需要明確的一點是,對于一個分布式系統(tǒng)而言,分區(qū)容錯性是一個最基本的要求。因為 既然是一個分布式系統(tǒng),那么分布式系統(tǒng)中的組件必然需要被部署到不同的節(jié)點,否則也就無所謂分布式系統(tǒng)了,因此必然出現(xiàn)子網(wǎng)絡(luò)。而對于分布式系統(tǒng)而言,網(wǎng) 絡(luò)問題又是一個必定會出現(xiàn)的異常情況,因此分區(qū)容錯性也就成為了一個分布式系統(tǒng)必然需要面對和解決的問題。因此系統(tǒng)架構(gòu)師往往需要把精力花在如何根據(jù)業(yè)務(wù) 特點在C(一致性)和A(可用性)之間尋求平衡。BASE理論 BASE是Basically Available(基本可用)、Soft state(軟狀態(tài))和Eventually consistent(最終一致性)三個短語的縮寫。BASE理論是對CAP中一致性和可用性權(quán)衡的結(jié)果,其來源于對大規(guī)模互聯(lián)網(wǎng)系統(tǒng)分布式實踐的總結(jié), 是基于CAP定理逐步演化而來的。BASE理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據(jù)自身業(yè)務(wù)特點,采用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終一致性。接下來看一下BASE中的三要素: 1、基本可用 基本可用是指分布式系統(tǒng)在出現(xiàn)不可預知故障的時候,允許損失部分可用性----注意,這絕不等價于系統(tǒng)不可用。比如: (1)響應時間上的損失。正常情況下,一個在線搜索引擎需要在0.5秒之內(nèi)返回給用戶相應的查詢結(jié)果,但由于出現(xiàn)故障,查詢結(jié)果的響應時間增加了1~2秒 (2)系統(tǒng)功能上的損失:正常情況下,在一個電子商務(wù)網(wǎng)站上進行購物的時候,消費者幾乎能夠順利完成每一筆訂單,但是在一些節(jié)日大促購物高峰的時候,由于消費者的購物行為激增,為了保護購物系統(tǒng)的穩(wěn)定性,部分消費者可能會被引導到一個降級頁面 2、軟狀態(tài) 軟狀態(tài)指允許系統(tǒng)中的數(shù)據(jù)存在中間狀態(tài),并認為該中間狀態(tài)的存在不會影響系統(tǒng)的整體可用性,即允許系統(tǒng)在不同節(jié)點的數(shù)據(jù)副本之間進行數(shù)據(jù)同步的過程存在延時 3、最終一致性 最終一致性強調(diào)的是所有的數(shù)據(jù)副本,在經(jīng)過一段時間的同步之后,最終都能夠達到一個一致的狀態(tài)。因此,最終一致性的本質(zhì)是需要系統(tǒng)保證最終數(shù)據(jù)能夠達到一致,而不需要實時保證系統(tǒng)數(shù)據(jù)的強一致性。 總的來說,BASE理論面向的是大型高可用可擴展的分布式系統(tǒng),和傳統(tǒng)的事物ACID特性是相反的,它完全不同于ACID的強一致性模型,而是通過犧牲強一致性來獲得可用性,并允許數(shù)據(jù)在一段時間內(nèi)是不一致的,但最終達到一致狀態(tài)。但同時,在實際的分布式場景中,不同業(yè)務(wù)單元和組件對數(shù)據(jù)一致性的要求是不同的,因此在具體的分布式系統(tǒng)架構(gòu)設(shè)計過程中,ACID特性和BASE理論往往又會結(jié)合在一起。怎么考慮數(shù)據(jù)一致性問題 說說最終一致性的實現(xiàn)方案 你怎么看待微服務(wù) 微服務(wù)與 SOA 的區(qū)別 如何拆分服務(wù) 微服務(wù)如何進行數(shù)據(jù)庫管理 如何應對微服務(wù)的鏈式調(diào)用異常 資源隔離 重試 熔斷 對于快速追蹤與定位問題 微服務(wù)的安全 分布式 談?wù)剺I(yè)務(wù)中使用分布式的場景 Session 分布式方案 分布式鎖的場景 分布是鎖的實現(xiàn)方案 分布式事務(wù)集群與負載均衡的算法與實現(xiàn) Dubbo 負載均衡策略提供下列四種方式: Random LoadBalance 隨機,按權(quán)重設(shè)置隨機概率。 Dubbo的默認負載均衡策略 在一個截面上碰撞的概率高,但調(diào)用量越大分布越均勻,而且按概率使用權(quán)重后也比較均勻,有利于動態(tài)調(diào)整提供者權(quán)重。RoundRobin LoadBalance 輪循,按公約后的權(quán)重設(shè)置輪循比率。 存在慢的提供者累積請求問題,比如:第二臺機器很慢,但沒掛,當請求調(diào)到第二臺時就卡在那,久而久之,所有請求都卡在調(diào)到第二臺上。LeastActive LoadBalance 最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機,活躍數(shù)指調(diào)用前后計數(shù)差。 使慢的提供者收到更少請求,因為越慢的提供者的調(diào)用前后計數(shù)差會越大。ConsistentHash LoadBalance 一致性Hash,相同參數(shù)的請求總是發(fā)到同一提供者。 當某一臺提供者掛時,原本發(fā)往該提供者的請求,基于虛擬節(jié)點,平攤到其它提供者,不會引起劇烈變動。說說分庫與分表設(shè)計 分庫與分表帶來的分布式困境與應對之策公眾號推薦
- 全網(wǎng)唯一一個從0開始幫助Java開發(fā)者轉(zhuǎn)做大數(shù)據(jù)領(lǐng)域的公眾號~
- 大數(shù)據(jù)技術(shù)與架構(gòu)或者搜索import_bigdata關(guān)注~
- 海量【java和大數(shù)據(jù)的面試題+視頻資料】整理在公眾號,關(guān)注后可以下載~
總結(jié)
以上是生活随笔為你收集整理的java http get_我是如何进入阿里巴巴的-面向春招应届生Java面试指南(九)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 运营商服务器系统,浪潮服务器助力运营商三
- 下一篇: php 显示对像编码,PHP面向对象之旅