阿里云专访Redisson作者Rui Gu:构建开源企业级Redis客户端之路
摘要:?本文為阿里云同學(xué)在RedisConf2018上對Redisson開源客戶端作者Rui Gu做的一個專訪,主要介紹了Rui Gu參與開啟Redisson客戶端開發(fā)的歷程,同時也詳細(xì)介紹了Redisson的架構(gòu)模型還有在分布式鎖上的工作,最后Rui Gu介紹了Redisson和開源的協(xié)作,同時介紹了后續(xù)Redisson客戶端的長期發(fā)展目標(biāo)。
筆者代表阿里云參加了RedisConf 2018的會議,在會議上對開源Redisson客戶端的作者Rui Gu做了一個訪談,Rui Gu在Redis社區(qū)國際上的影響力還有在開源上的工作給筆者留下了深刻的印象,以下是訪談的具體內(nèi)容。
以上照片為阿里云夏周、Rui Gu、阿里云白宸、阿里澤賢
當(dāng)初為什么參與設(shè)計開發(fā)Redisson?
自04年從事工業(yè)自動化、工業(yè)IoT工作至今,涉及到很多場景需要對一系列設(shè)備進(jìn)行監(jiān)控和信號處理等工作。該類場景對實時處理能力,系統(tǒng)穩(wěn)定性,高可用性,容災(zāi)能力等等要求非常高。從12年時決定采用Redis作為實時數(shù)據(jù)庫時就產(chǎn)生了許多想法。Redis與Java這樣的編程語言中的常用數(shù)據(jù)結(jié)構(gòu)看似相像卻又不同,一直希望能夠用什么方法將兩者聯(lián)系起來。13年開始商用Redis以后這種想法越加強烈。于是在工作之余自行開始了一些相關(guān)的摸索與實踐,最終決定采用動態(tài)類的形式讓Redis的數(shù)據(jù)結(jié)構(gòu)操作起來更像Java對應(yīng)的結(jié)構(gòu)。誰知遠(yuǎn)在莫斯科的Nikita似乎也有類似的想法,他從14年元旦便開始了實際應(yīng)用的開發(fā),并很快的開源了Redisson。于此同時我的實踐也有了許些進(jìn)展,并初步的實現(xiàn)了一些基本功能。不過由于工作上的種種原因,再加上當(dāng)時自己也缺乏足夠的信心,畢竟這是條沒人走過的路,大半年過去了進(jìn)展比較緩慢。殊不知Nikita面對這同樣的問題,但是他不僅艱難地堅持了下來,而且絲毫沒有放棄的意思。14年下半年時我開始注意到了Redisson項目,仔細(xì)了解了以后頓時產(chǎn)生了很強的共鳴,雖然和我的實踐有著同樣的理念卻又是不同的出發(fā)點。于是乎,在有了這樣的火花以后,我們開始了相互之間的溝通和交流,最后在15年初時決定,放棄自己的實踐項目,加入Redisson。至此,在這條沒人走過的路上我們不再獨行。
Redisson解決了什么問題?相比其他Redis客戶端它有什么優(yōu)勢?
2.1)IoT行業(yè)里,一組設(shè)備的各種實時狀態(tài)值往往是作為一個具有業(yè)務(wù)意義的對象,由JVM管理在內(nèi)存里,如果將這個對象存儲到Redis數(shù)據(jù)庫的String結(jié)構(gòu)里,每次更新一個狀態(tài)值,就需要做一次序列化和反序列化。同時還有可能面臨著同一時刻操作同一個對象的不同狀態(tài)值帶來的并發(fā)難題。實際應(yīng)用時采用了Redis提供的Hash數(shù)據(jù)結(jié)構(gòu)來儲存這個對象,只有這樣才能有效地避免這類問題的發(fā)生。盡管Redis的Hash結(jié)構(gòu)和Java里的HashMap極為相似,但是在程序操作Redis的時候不能像操作HashMap一樣便捷。而且如果對Redis相關(guān)命令的用法不能稔熟于心,或在細(xì)節(jié)之處處理不當(dāng),便會最終造成業(yè)務(wù)上的各種問題。Redisson的Map就是為了填補Redis的Hash和Java的HashMap兩者之間的空缺而產(chǎn)生。
圖1 - Jedis的Hash操作
圖2 - Java的ConcurrentHashMap
圖3 - Redisson的ConcurrentHashMap
2.2)工控和某些IoT場景對實時處理能力要求很高,所有的信號都必須實現(xiàn)毫秒級響應(yīng)。這類場景還具有并發(fā)量巨大的特點。與社交電商等場景不同的是這類應(yīng)用場景基本沒有峰谷流量,時時刻刻都是峰值。因此其它場景里常見的削峰填谷措施在這里只能加重負(fù)擔(dān)。在這樣的場景下如果使用像Jedis這樣采用同步編程模型的客戶端時,就需要隨時確保并發(fā)線程數(shù)與連接數(shù)一對一,否則獲取不到可用連接會直接報錯。相比之下Redisson利用了Netty異步編程框架,使用了與Redis服務(wù)端結(jié)構(gòu)類似的事件循環(huán)(EventLoop)式的線程池,并結(jié)合連接池的方式彈性管理連接。最終做到了使用少量的連接既可以滿足對大量線程的要求,從根本上緩解線程之間的競爭關(guān)系。同時異步操作的模式還能夠避免數(shù)據(jù)請求造成業(yè)務(wù)線程的阻塞。
2.3)Redis 發(fā)展至今經(jīng)歷了多次技術(shù)變遷。官方版在迭代的過程中不但增加了許多有用的功能,同時也發(fā)展了幾種高可用性方案。于此同時,社區(qū)和云計算商在官方版上進(jìn)而開發(fā)出了多種基于代理(Proxy)的高可用方案。相比之下,這些方案各有優(yōu)劣,適用場景也各自不一。多樣化的方案在帶來便利的同時也帶來了麻煩。比如在業(yè)務(wù)擴容,從簡單的單機或主從模式遷移到哨兵或集群模式;或是業(yè)務(wù)遷移,從自建的Redis環(huán)境遷移到云上;亦或是項目的持續(xù)×××付CD/CI過程中,不同的階段使用不同Redis運行模式等等情況。往往需要開發(fā)人員針對不同的高可用方案開發(fā)出一套與之匹配的使用方法。使得一個項目對Redis運行模式的耦合度高,在Redis運行模式變化時就必須更改業(yè)務(wù)代碼。Redisson針對這種情況提供了一套便捷的文件化配置方法,在無需修改程序代碼的情況下,通過不同的JSON,YAML或SpringXML文件實現(xiàn)對不同Redis運行模式和環(huán)境的支持。這既降低了開發(fā)難度,也降低了運維難度。
Redisson在分布式鎖方面的工作非常多,能否介紹下這方面的實踐?
對于Redis分布式鎖的實現(xiàn)方式,網(wǎng)上討論相關(guān)文章都基本都“爛大街”了。然而幾乎所有相關(guān)介紹都是在單純使用setnx命令的基礎(chǔ)上進(jìn)行一個簡單封裝,且少有文章分析這樣設(shè)計的缺陷。在這個博客滿天飛,代碼隨便貼的時代,這樣的局面無形之中給了大家一個假象,就是Redis分布式鎖只能是以這樣簡單的形式存在,即便有缺陷也只能在業(yè)務(wù)代碼里規(guī)避。那么為什么不換位思考一下,即用稍微復(fù)雜點的設(shè)計來彌補它的不足,從而換取業(yè)務(wù)上的靈活性呢?再重新設(shè)計Redis分布式鎖之前,我們先了解一下單純使用setnx命令封裝的分布式鎖有哪些不足。
1). 不具備可重入性
在執(zhí)行setnx命令時,通常采用業(yè)務(wù)上指定的名稱作為key名,用時間或隨機值作為value來實現(xiàn)。這樣的實現(xiàn)方式不具備追蹤請求線程的能力,同時也不具備統(tǒng)計重入次數(shù)的能力,甚至有些實現(xiàn)方式都不具備操作的原子性。當(dāng)遇到業(yè)務(wù)上需要在多個地方用到同樣一個鎖的時候,很顯然使用不具有可重入的鎖會很容易發(fā)生死鎖的現(xiàn)象。特別是在有遞歸邏輯的場景里,發(fā)生死鎖的幾率會更高。Java并發(fā)工具包里的Lock對象和sychronized語塊都具有可重入性,對于經(jīng)常使用這些工具的人來說,往往會很容易忽略setnx的這個缺陷。
2). 不支持續(xù)約
在分布式環(huán)境中,為了保證鎖的活性和避免程序宕機造成的死鎖現(xiàn)象,分布式鎖往往會引入一個失效時間,超過這個時間則認(rèn)為自動解鎖。這樣的設(shè)計前提是開發(fā)人員對這個自動解鎖時間的粒度有一個很好的把握,太短了可能會出現(xiàn)任務(wù)沒做完鎖就失效了,而太長了在出現(xiàn)程序宕機或業(yè)務(wù)節(jié)點掛掉時,其它節(jié)點需要等很長時間才能恢復(fù),而難以保證業(yè)務(wù)的SLA。setnx的設(shè)計缺乏一個延續(xù)有效期的續(xù)約機制,無法保證業(yè)務(wù)能夠先工作做完再解鎖,也不能確保在某個程序宕機或業(yè)務(wù)節(jié)點掛掉的時候,其它節(jié)點能夠很快的恢復(fù)業(yè)務(wù)處理能力。
3). 不具備阻塞的能力
平常大家多少都接觸過的鎖,由于加鎖策略(Locking Strategy)的差別,使得每種鎖都有各自不同的特性。但是在通常情況下這些鎖都具備兩個共性:一是互斥性,二是阻塞性。互斥性是指在任何時刻最多只能有一個線程獲得通行的資格。阻塞性是指的在有競爭的情況下,未獲取到資源的線程會停止繼續(xù)操作,直到成功獲取到資源或取消操作。很顯然setnx命令只提供了互斥的特性,卻沒有提供阻塞的能力。雖然在業(yè)務(wù)代碼里可以引入自旋機制來進(jìn)行再次獲取,但這僅僅是把原本應(yīng)該在鎖里實現(xiàn)的功能搬到了業(yè)務(wù)代碼里,通過增加業(yè)務(wù)代碼的復(fù)雜程度來簡化鎖的實現(xiàn)似乎顯得有點南轅北轍。
Redisson的分布式鎖在滿足以上三個基本要求的同時還增加了線程安全的特點。利用Redis的Hash結(jié)構(gòu)作為儲存單元,將業(yè)務(wù)指定的名稱作為key,將隨機UUID和線程ID作為field,最后將加鎖的次數(shù)作為value來儲存。同時UUID作為鎖的實例變量保存在客戶端。將UUID和線程ID作為標(biāo)簽在運行多個線程同時使用同一個鎖的實例時,仍然保證了操作的獨立性,滿足了線程安全的要求。
加鎖時通過Lua腳本先檢查鎖是否存在,如不存在則創(chuàng)建hash相關(guān)字段并設(shè)定過期時間后返回,這表示加鎖成功。如果該hash字段已經(jīng)存在,再檢查隨機字段和線程id是否一致。如果一致則遞增value的值并重新更新過期時間后返回,此時表示同一節(jié)點同一線程再次成功加鎖,從而保證了可重入性。如果hash存在且字段不一致,說明其他節(jié)點或線程已經(jīng)擁有了這個鎖。因此Lua腳本返回這個hash的當(dāng)前有效期。當(dāng)結(jié)果返回到在客戶端后,如果加鎖成功,則通過線程池依照設(shè)定好的參數(shù)定時執(zhí)行續(xù)約,最后通知請求線程繼續(xù)后續(xù)操作。如果加鎖沒有成功,則監(jiān)聽一個以這個key為后綴的pubsub頻道,直到收到解鎖消息后再次重試。?
解鎖時通過Lua腳本先檢查鎖是否存在,如果已經(jīng)不存在則直接發(fā)布解鎖消息并返回。如果任然存在則檢查標(biāo)簽是否存在,如果不存在則表示這個鎖并不為本線程所擁有,這種情況請求線程將收到報錯。如果存在則表示該鎖正是被該線程所擁有。在這種情況下,遞減標(biāo)簽字段后判斷,如果返回的加鎖數(shù)量仍然大于0,說明當(dāng)前的鎖仍然有效,僅僅只是重入次數(shù)減少了。相反這表示鎖已經(jīng)完全解開,則立即刪除該鎖并發(fā)布解鎖信息。
Redisson的可重入鎖解決了setnx鎖的許多先天性不足,但是由于它仍然是以單一一個key的方式儲存在固定的一個Redis節(jié)點里,并且有自動失效期。這樣的設(shè)計雖然可以很大程度上避免客戶端程序宕機或業(yè)務(wù)節(jié)點掛掉造成的影響,但是隨之帶來的弊端是遇到服務(wù)端Redis進(jìn)程宕機或節(jié)點掛掉的情況,還是有可能會造成鎖的信息丟失,這樣的缺陷顯然無法滿足某些特定場景提出的高可用性要求。
介于這種情況,Redis作者Salvatore提出了一個基于多個節(jié)點的高可用分布式鎖的算法,起名叫紅鎖(RedLock:?https://redis.io/topics/distlock)。在這種算法下,客戶端需要同時在多個節(jié)點里同時嘗試獲取一個獨立的鎖,只有當(dāng)一次性成功獲取了大多數(shù)鎖的情況下才能被視為贏得了高可用分布式鎖,否則需要解除已經(jīng)部分獲取到的鎖,等待一個隨機時間后再次重試。
在算法設(shè)計上,Salvatore依然采用的是setnx作為舉例講解分布式鎖的互斥特性。在算法實現(xiàn)上,Redisson的RedissonRedLock采用的是前面提到的更加靈活方便的可重入鎖。Redisson的擴展算法是Redis官網(wǎng)唯一認(rèn)可的Java實現(xiàn)。
雖然Redlock的算法提供了高可用的特性,但建立在大多數(shù)可見原則的前提下,這樣的算法適用性仍然有一定局限。Redisson為此提供了基于增強型的算法的高可用分布式聯(lián)鎖RedissonMultiLock。這種算法要求客戶端必須成功獲取全部節(jié)點的鎖才被視為加鎖成功,從而更進(jìn)一步提高了算法的可靠性。
4.能否介紹下Redisson最前沿的發(fā)展方向??
Redisson的發(fā)展路線決定了它在Redis的功能擴展及應(yīng)用方式上始終走在業(yè)界的前列,其中最具有代表性的便是本地緩存功能了。2016年為了解決一企業(yè)版用戶的切實需求開發(fā)了這一功能。其原理是采用犧牲客戶端自身內(nèi)存的空間的方式,換取在頻繁獲取某些常用數(shù)據(jù)時消耗在網(wǎng)絡(luò)上的時間。該功能在同年9月開源后便立即受到了廣大用戶的關(guān)注。這一功能的出現(xiàn)加速了傳統(tǒng)IT用戶從其他類似平臺遷移到Redis的速度。其受歡迎程度大大超乎了Nikita和我的想象。以至于每年都有企業(yè)用戶不遠(yuǎn)×××去Redis大會等類似國際交流大會,并分享它們使用Redisson從其他平臺向Redis遷移過程和經(jīng)驗。也正是因為這種趨勢而引起了Redis作者Salvatore的注意,在同一些用戶面對面溝通交流之后,Salvatore決定將客戶端緩存功能作為Redis今后發(fā)展的重要方向,并為此提出了RESP3協(xié)議。RESP3的出現(xiàn)將為客戶端緩存功能提供服務(wù)端協(xié)調(diào)的能力。同時Salvatore還邀請Redisson團(tuán)隊作為專家組成員參與Redis客戶端緩存標(biāo)準(zhǔn)的指定。
5.Redisson做為開源項目如何保證持續(xù)的發(fā)展??
為了保證Redisson項目的可持續(xù)性的健康發(fā)展,為了避免像其他開源項目面臨的一段時間以后就無人維護(hù)的尷尬局面,17年初Nikita和我商量后決定在開源項目基礎(chǔ)上提供收費咨詢服務(wù),為項目的正常運作提供必要的資金。同時還針對大型企業(yè)用戶遇到的特殊場景提供了企業(yè)級的綜合性解決方案,最后將這些所有的方案與企業(yè)級SLA支持服務(wù)打包作為Redisson PRO正式面向企業(yè)用戶。
相對于其他客戶端而言,雖然Redisson項目創(chuàng)立的時間較短,但已經(jīng)受到了來自不同行業(yè)企業(yè)的信任,其中不乏許多行業(yè)領(lǐng)頭羊企業(yè),其中最值得介紹的是這幾個世界級的企業(yè)用戶:
? 計算機行業(yè)的IBM。想必大家都熟悉IBM,PC機的鼻祖。,業(yè)界少有同時具有超強硬件軟件研發(fā)能力的企業(yè),即便如此,IBM也心甘情愿的使用Redisson,這種信任是對我們最大的支持。
? 航空國防制造業(yè)的波音。在它們主動聯(lián)系我們以前,我很難想象波音也會對Redisson感興趣。事實上波音除了造飛機以外,它也是全球最大的飛行航圖提供商和移動電子飛行包的方案提供商,幾乎每個航空公司都是他們的用戶。Redisson為他們的在線飛行導(dǎo)航業(yè)務(wù)提供了扎實的基礎(chǔ)。
? 保險業(yè)的美國國際集團(tuán)(AIG)。美國國際集團(tuán)成立于1919年的中國上海,它是首個將保險概念帶給中國人的西方企業(yè),其業(yè)務(wù)遍布全球130多個國家和地區(qū)。雖然08年經(jīng)濟危機中,遭遇股價瞬間暴跌的慘劇將AIG推入了吃瓜群主的視線,但它今天仍是一個擁有99年歷史,總資產(chǎn)為6千多億美元的國際性大型企業(yè)。在經(jīng)過AIG團(tuán)隊長時間的調(diào)研后,Redisson被用于支持其名目眾多的金融保險業(yè)務(wù)。
? 金融機構(gòu)標(biāo)準(zhǔn)普爾(S&P Global)。提到經(jīng)濟危機就不得不提一下世界權(quán)威金融分析機構(gòu)標(biāo)準(zhǔn)普爾。它是美國證券交易委員會(SEC)認(rèn)可的三大評級組織之一,專門為投資者提供信用評級,投資研究和咨詢等服務(wù)。在業(yè)內(nèi)外的知名度很高,享有盛名的S&P 500美國股指便是由它創(chuàng)建并維護(hù)著。標(biāo)準(zhǔn)普爾不僅對外提供針對上市企業(yè)的評級,還提供針對國家政府的評級。它在2011年時斷然降低美國政府的評級,并把其前景調(diào)整為負(fù)面以后,立馬引發(fā)了金融業(yè)的劇烈波動。但正是這個呼風(fēng)喚雨無所不能,連美國政府都不放眼里的機構(gòu)也成為了Redisson的忠實用戶,并將其用于提供復(fù)雜的金融數(shù)據(jù)的分析和處理。由此可見Redisson的信任評級是非常的高[奸笑]。
原文鏈接
轉(zhuǎn)載于:https://blog.51cto.com/13679539/2135136
總結(jié)
以上是生活随笔為你收集整理的阿里云专访Redisson作者Rui Gu:构建开源企业级Redis客户端之路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 京东金条怎么协商延期还款 京东金条怎样协
- 下一篇: 赛腾股份是做什么的