暖化使冰河湖面積大增 英環團上街倡議

摘錄自2020年9月2日公視、自由時報報導

根據「自然氣候變化」最新報告顯示,隨著全球氣候暖化影響,冰河湖最近30年來擴張速度迅速,引發氾濫隱憂。英國有環保團體為了敦促政府修法,號召舉行10天「反抗滅絕」活動,至少有五位民眾因為在國會大廈鄰近道路攔路靜坐遭警方逮捕。

綜合外電報導,在於英國國會廣場、卡地夫市政廳、聖彼得廣場遊行後,反抗滅絕的成員將於這3個地點靜坐抗議10天。部分示威者甚至在國會廣場附近的馬路中央席地而坐,他們表示,唯有國會議員表態支持「氣候與生態緊急狀況法案」(Climate and Ecological Emergency Bill),他們才會停止聚集與阻斷道路,造成倫敦交通於1日下午大打結。

警方在示威現場部署了大量的警力,限制示威者能示威的地方及時間,並預防示威者對當地經濟、社區造成嚴重的破壞。

氣候變遷
國際新聞
英國
冰河湖
氣候暖化
環保團體

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

印尼人每年吃掉百萬隻狗 動保人士籲嚴格取締

摘錄自2020年09月06日中央社報導

印尼近年來保護狗的意識成長,但每年仍有逾百萬隻狗被屠殺來吃,跨省運輸狗的違法行為氾濫,今年勢難達成脫離狂犬病疫區目標。動保人士呼籲完全禁食狗肉,加強執法,杜絕狗肉交易。

印尼2013年紀念9月28日世界狂犬病日時,訂下今年要自狂犬病疫區除名的目標。但到今年初,全國34個省和特區的25省仍有狂犬病疫情。

由「雅加達動物救援網」(Jakarta Animal Aid Network,JAAN)等動物保護團體組成的「印尼無狗肉」聯盟(Dog Meat Free Indonesia,DMFI)兩年多前調查顯示,亞洲每年有約3000萬隻狗遭屠殺食用,印尼至少有上百萬隻。

JAAN共同創辦人佛蘭肯(Karin Franken)4日接受中央社記者訪問時說,調查發現印尼狗肉交易的規模如此之大,「我們感到很驚訝」,各地市場的大小規模不同,但「基本上幾乎每個地方都有」。她舉例,特別是在蘇拉威西(Sulawesi)、蘇門答臘(Sumatra)及中爪哇省的梭羅市(Solo)。

印尼農業部約一年半前頒布指示,不再承認吃狗肉合法,也禁止任何機關發出狗肉食用的相關證明。

生物多樣性
國際新聞
印尼
狗肉

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

環團抗議氣候變遷 打亂英國多家報紙派送作業

摘錄自2020年09月05日中央社報導

英國氣候變遷抗議人士今早封鎖兩家報紙印刷廠,打亂了「泰晤士報」(The Times)、「每日電訊報」(Daily Telegraph)及「太陽報」(The Sun)等報的派送作業。

法新社報導,環保團體「反抗滅絕」(Extinction Rebellion)表示,矛頭對準這兩家印報廠是「為了揭露這些媒體未能準確報導氣候和生態危機」。

數十名示威者昨夜抵達現場,以車輛及其他障礙物阻擋廠外道路,警方連夜逮捕了63人。

「反抗滅絕」發表聲明說,他們希望擾亂新聞集團(News Corp.)旗下的這些報紙,以及「每日郵報」(Daily Mail)及「倫敦標準晚報」(London Evening Standard)等右翼報紙。

擁有及經營這些印刷廠的公司Newsprinters表示,印報作業已轉移到其他地點,並為「送報時間延誤」而對訂戶表達歉意。

「泰晤士報」也對無法買到這份報紙的讀者致歉,同時推文表示,報社「正努力將報紙儘快送到零售商手上」。

氣候變遷
國際新聞
英國
反抗滅絕
報紙

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

去年花了多少錢?看你的賬單適合開什麼車

百公里加速不到4秒的旅行車,選裝后輕鬆上200萬,這樣的終極買菜車到底是扮豬吃老虎還是披着豹皮的狼呢。廣州一個平方的房價就可以買到如此精緻漂亮的一款小車,夫復何求。‍聽說立標版與加長E在一起會更配喲。老款月銷量輕鬆破萬,新A4L的表現顯然沒達到奧迪的預期,上市3個月終端給出十幾個點的優惠,早知如此何必當初。

上周,盆友圈上演了一波滿屏儘是曬賬單的大戲。一年到頭了,直到這時候才發現我居然有這麼一大群土豪朋友竟渾然不知,罪過罪過!

既然如此,賬單你也曬了,富你也炫了,b也裝完了,那麼……

是時候把上次吃飯AA的錢還給我了吧?

其實咱們汽車圈在2016年也有不少值得總結的地方,那今天就由叫獸來和大家一起來理理下2016汽車年度賬單吧!

介於C級車與D級車之間的大尺寸、豪華精緻的做工、包含64%鋁合金材料的鋼鋁混合車身等等一切都代表了目前在Made in China汽車的最高水準。

天貓用18秒賣了100台瑪莎拉蒂,聽說連馬雲爸爸和瑪莎總裁都驚呆了!

上市不到8個月賣了90507輛的,相比9代思域成幾何倍數的增長,不是鹹魚翻身是什麼?

首款量產互聯網SUV,成為又一款月銷破2萬的SUV,不負網紅美名。

百公里加速不到4秒的旅行車,選裝后輕鬆上200萬,這樣的終極買菜車到底是扮豬吃老虎還是披着豹皮的狼呢?

廣州一個平方的房價就可以買到如此精緻漂亮的一款小車,夫復何求?

聽說立標版與加長E在一起會更配喲!

老款月銷量輕鬆破萬,新A4L的表現顯然沒達到奧迪的預期,上市3個月終端給出十幾個點的優惠,早知如此何必當初!

又一款進軍20萬級別的自主品牌SUV,市場一車難求的情況也不難預見GS8的成功,挺為國產爭面子!

我等Diao絲的“藍天白雲夢”不再遙遠,坐等2月上市!

油電混合 + 3秒破敗 + 289萬 = 東瀛法拉利NSX

盆友圈瘋狂刷支付寶賬單這事兒,從營銷層面來講,馬雲爸爸這次又讓自家產品在對手家裡肆無忌憚的火了一把,而且還是免費的。就事情本質而言,其實就是支付寶幫大家做了一年的賬目匯總,也好爭取來年把手剁得更狠!反正2016年叫獸的支付寶賬單隻夠剁一台這貨:

至於今年的目標嘛,爭取多一台寶馬,不對,是寶駿,那你們呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

差點就翻車!用生命試駕中國最屌電動車

你TM就拿補貼造了這麼一個&^%$&%*^$(&出來。只要你開着它溜出個幾米,跨過減速帶,那種直接生硬的震動會讓你的香臀不知所措。對於知豆我覺得不需要說太多了,因為我直接建議大家去租一輛好好體驗一把。而奇瑞EQ則像是一輛升級再升級版的知豆。

相信近些年大家對新能源車是有了不少的了解,不論是國內還是國外的車企都着手推出新能源車型。而在國內車企推出新能源車型還能獲得國家或地方政府補貼,消費者也能選擇更多不同的車型。

然而,並不是每家車企都會出良心之作。於是,就有了這麼三輛新能源車,知豆D2,北汽EV160和奇瑞eQ,這三輛車的官方售價分別為15.88-18.88萬元、17.78-18.99萬和15.99-16.49萬元。如果沒有補貼按這價格來賣,估計排隊噴它們的人能繞地球三圈。

不過,今天我們不討論錢的問題。單從產品出發,就聊它們到底是不是一輛“車”。

上一年我們就試駕過了知豆D2,這輛雙門雙座的小車,簡直就是一輛升級版的老年代步車。什麼用料,質感,品牌統統要拋到一邊,因為不值得去說了。不過相比於其它兩者,知豆D2擁有一个中控大屏,不得不說麻雀雖小但逼格還是得有。

不知道有多少朋友體驗過坐進知豆D2里的感覺,但那處濃烈的塑料感絕對會讓你開懷的笑出聲來。你TM就拿補貼造了這麼一個&^%$&%*^$(&出來?只要你開着它溜出個幾米,跨過減速帶,那種直接生硬的震動會讓你的香臀不知所措。對於知豆我覺得不需要說太多了,因為我直接建議大家去租一輛好好體驗一把。

而奇瑞EQ則像是一輛升級再升級版的知豆。在租車點清一色雪白的車體,搭載暗紫色的封閉輪轂,還沒進去之前你會有點懷疑,但只要你坐進去,扣好安全帶,試了N次將車子啟動之後,你還是會覺得它至少還像輛車。用手輕輕一扭,將旋鈕式擋把切換至D擋,你就可以開始屬於你的傳奇之旅了。如果奇瑞的工程師們能把旋鈕做成啟動后能緩緩升上來的話,絕對會是新能源界的一段佳話。

踩住剎車,右腳油門到底,上車不來一發溫柔的彈射起步絕對不能算開過這車。在大長直的公路上稍稍變線,可以感受到強有力的側翻臨界體驗。更可貴的是,這車能開到100km/h。別問我怎麼知道的,肯定是工程師們為了讓你好受些將液晶時速表的数字調大了。

相比之下,北汽EV160就可以算是一輛車了。它不像知豆那麼簡陋,也不像奇瑞eQ那樣發飄,開完前兩輛車之後再開北汽EV160,你會感覺世界的秩序會稍稍正常一點。如果你哪天因為不可描述的原因買了這麼一輛車,那我覺得你需要上某寶收一個奔馳的標,貼在前進氣一點違和感都沒有。

除了洋氣的外觀之外,北汽EV160開起來就沒有像前兩輛車那麼充滿戲劇性。按照The Grand Tour的主持人Jeremy Clarkson的理論,一輛能讓人充滿笑容的車才是好車,那麼北汽EV160可能就有點達不到要求了。

不過這些都不重要了,如果今天的這條片子看完沒能讓你笑出聲來,那可能就說明了連我們的聰明才智都無法讓這三輛車挽回那一點點“車”的尊嚴。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

過年買車套路深…怎樣才能用合理的價錢買到合適的車?

免息≠免手續費什麼“一年免息”、“無息貸款2年愛車開回家”這些廣告你們真敢信。等你帶好了身份證、銀行流水甚至房產證去店裡準備簽字畫押的時候,你就會明白即使減免或優惠了所謂的利息,但手續費、服務費等等費會讓你瞬間感覺上當,不過此時的你騎虎難下,買還是不買。

眼瞅着公曆新年正式開始,同時也意味着“購置稅5折優惠大酬賓”活動徹底結束,不知道大家有沒有趕在2017年之前買到自己喜愛的車呢?

叫獸在這裏對已經提了新車的朋友們說聲恭喜,你們算是“撿到了大便宜”。還沒買的也不要着急,畢竟今年購置稅還可以享受“75折優惠”(1.6L及以下排量乘用車)。更重要的是,如果您恰好在買車之前看到今天這篇文章保你買車不被坑,可以少花冤枉錢,這同樣是“真金白銀”一樣的福利,快收下吧。

叫獸是個實誠人,就單刀直入告訴大家我們在買車的時候會遇到哪些常見的套路。

一.網上低價陷阱

如今網絡信息這麼發達,大部分人買車之前都會先在網上做好功課。除了查詢車輛的配置差異外,還會通過媒體試駕評測(比如時刻關注叫獸)以及網友評價等消息對車輛做一個初步的了解和判斷,而報價更是大家的關注的重點。

來到敏感的價格部分,這時候就需要擦亮你的雙眼。網上都常會以特別顯眼(尤其是紅色)的顏色展示出一個非常有誘惑力的價格,不懂行的“小白”們會以為撿着大便宜了,趕緊點進鏈接一看,通常會遇到幾種情況:

1.需要在店內上牌、辦理保險甚至購買裝飾(所謂的優惠也就形同虛設)

2.大幅優惠只限於頂配車型(並且這個優惠通常來說一樣也辦不到),要買低配?不是沒車就是做不了那樣的優本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

除了家用致炫還能這麼玩,看完包你流口水…

5L車型前往午飯目的地,人生地不熟的在原廠車載導航的指引下,只用半個多小時便順利到達午飯地點——濱江大公館惠食佳。飲飽食醉后,馬不停蹄前往場地挑戰地點,媒體老師們駕馭着致炫,各顯神通、爭奪積分,無論是短小狹窄的緊湊型賽道,抑或是蜿蜒小徑,致炫都能游刃有餘,樂趣不言而喻。

常言道:“唯汽車與美食不可辜負”,每天沉浸在汽車與美食帶來的歡悅中,樂此不疲。無獨有偶,廣汽豐田邀請參与廠商悉心舉辦的“食貨大玩咖的美食探尋之旅”,正中下懷。

兵馬未動,糧草先行,首個行程便是前往遠近馳名的“食在廣州第一家”——廣州酒家,品味嶺南“一盅兩件”的早茶文化。

苦澀中夾雜一絲甘甜,被貪婪的口腔霸佔着,茶的蒸汽朦朧了雙眼,勾勒出朦朧的回憶,但記憶卻不再朦朧。(好了,這逼裝不下去了)

早茶過後,便駕馭豐田致炫1.5L車型前往午飯目的地,人生地不熟的在原廠車載導航的指引下,只用半個多小時便順利到達午飯地點——濱江大公館惠食佳。

飲飽食醉后,馬不停蹄前往場地挑戰地點,媒體老師們駕馭着致炫,各顯神通、爭奪積分,無論是短小狹窄的緊湊型賽道,抑或是蜿蜒小徑,致炫都能游刃有餘,樂趣不言而喻。

這歸功於致炫搭載S-CVT智能無級變速器(模擬8速),優化了動力系統的效率和加速響應性,使車輛的動力輸出更平順,燃油經濟性理想,只可惜變速箱並未配備S擋。

來到位於佛山北滘的晚飯地點——蚝專家,自不然要一嘗新鮮地道的海鮮盛宴。

這次致炫讓我們收穫了廣佛兩城的獨特味道,在快節奏的城市生活中與致炫偷得一絲閑逸,體驗新款致炫的多項升級。未來,致炫將憑藉其獨有的產品魅力,俘獲更多年輕人。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

明明買了奔馳寶馬卻被人嘲笑 我到底做錯了什麼

雖然面對他們的嘲笑我表示不解,但是車還是要買,聽說買了奔馳,妹子就會自動上車。於是我又買了輛最新款的SUV。結果還是被他們嘲笑。後來我又換了寶馬。路虎。但是結果都是一樣,我覺得不要買那麼好的車了,太容易買到假貨,車要那麼好的品牌幹嘛,能夠遮風擋雨代步就夠了,所以下定決心買一輛吉利熊貓,結果買回去他們卻說,我買車的這段時間,他們的臉上都長肌肉了。

眼看年關將至,為了過年回家的時候能在親戚朋友面前,展示一下自己一年的成績,決定去提輛車。一直都有聽朋友說本田的超跑不錯,還可以爆VTEC,所以決定買輛GK5。

不知怎麼買回來就被朋友取笑,難道在他們眼中Type R才是本田的超跑嗎?後來想了想。

於是我決定換個選擇。雖然超跑很刺激,但是過年車多路滑,安全也很重要,聽說奧迪的quattro四驅很厲害,所以決定去買輛奧迪的SUV。

結果買回來又被他們取笑,難道現在的奧迪是quattro嗎?

雖然面對他們的嘲笑我表示不解,但是車還是要買,聽說買了奔馳,妹子就會自動上車。於是我又買了輛最新款的SUV。

結果還是被他們嘲笑。

後來我又換了寶馬。

路虎。

但是結果都是一樣,我覺得不要買那麼好的車了,太容易買到假貨,車要那麼好的品牌幹嘛,能夠遮風擋雨代步就夠了,所以下定決心買一輛吉利熊貓,結果買回去他們卻說,我買車的這段時間,他們的臉上都長肌肉了。

我的人生觀彷彿在這一刻崩塌。

其實並不是笑話這些車是假車,段子聽聽就完了,畢竟他們除了換了個Logo之外知道自己是老年代步車,並沒有說自己是汽車,不想其他品牌。

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

Flutter 中由 BuildContext 引發的血案

今天和各位分享一個博主在實際開發中遇到的問題,以及解決方法。廢話不多說,我們先來看需求:
我們要做一個iOS風格的底部菜單彈出組件,具體涉及showCupertinoModalPopup()方法,該方法被執行后,會出現如下圖類似所示的菜單彈出視圖:

相信這個彈出菜單視圖都有見過吧?下面重點來了:在本次的項目需求中,該視圖的選項文字是由Server端返回的。也就是說,這些選項的內容和個數都不固定,因此不能將其在代碼中寫固定值。
為了簡化代碼以突出重點,下面放上我在一開始的實現方案:

  openActionSheet() {
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) {
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () {
          Navigator.pop(context);
          debugPrint("操作$element被執行“);
        },
        isDefaultAction: true,
      ));
    });

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) {
          return CupertinoActionSheet(
              title: Text('測試菜單'),
              message: Text('點擊菜單項試試吧!'),
              actions: menuWidgets);
        });
  }

如上述代碼所示,openActionSheet()是显示該組件的方法。其中,showCupertinoModalPopup()為Flutter SDK內置方法,其作用即显示這個組件;再其上面的循環以及List聲明、賦值等操作實際上就是在動態添加菜單項。menuItems類型是List<String>。
通過對代碼的解釋,相信大家能夠一目瞭然地看出,當某個菜單項被點擊時,整個菜單組件消失,並打印Debug Log(對應為真實項目要執行的操作)。
大家覺得上述代碼有問題嗎?如果有問題,問題在哪兒呢?
現在公布答案:這段代碼有問題!
上述代碼執行時,當用戶點擊菜單項后,其運行結果並非如我們預想的那樣:菜單組消失並輸出Log,而變成了:整個頁面被Pop,菜單組保留,並輸出Log!
這是什麼原因呢?
實際上,罪魁禍首就在我們循環遍歷賦值操作時的這條語句:

Navigator.pop(context);

這裏的context是整個頁面的BuildContext,而非菜單組的。這裏我們要明確一個概念——我們想Pop誰,一定要用誰的BuildContext對象。
在這裏,正確的BuildContext對象是誰呢?它在這裏:

showCupertinoModalPopup(
    context: context,
    builder: (buildContext) {
      return CupertinoActionSheet(
          title: Text('測試菜單'),
          message: Text('點擊菜單項試試吧!'),
          actions: menuWidgets);
    }
);

注意到了嗎?上面第三行括號里的buildContext才是我們真正要用的對象。因此,正確的做法是什麼呢?

  openActionSheet() {
    BuildContext tempContext;
    List<Widget> menuWidgets = new List();
    menuItems.forEach((element) {
      menuWidgets.add(CupertinoActionSheetAction(
        child: Text(element),
        onPressed: () {
          Navigator.pop(tempContext);
          debugPrint("操作$element被執行");
        },
        isDefaultAction: true,
      ));
    });

    showCupertinoModalPopup(
        context: context,
        builder: (buildContext) {
          tempContext = buildContext;
          return CupertinoActionSheet(
              title: Text('測試菜單'),
              message: Text('點擊菜單項試試吧!'),
              actions: menuWidgets);
        });
  }

如上所示,我們只需將正確的對象“帶”到其作用域外面就可以了。
好了,這就是本篇文章的全部內容,希望能夠對你有所幫助!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Zookeeper分佈式鎖

分佈式解決方案源碼,請幫我點個star哦!
原文地址為https://www.cnblogs.com/haixiang/p/13112710.html,轉載請註明出處!

zookeeper客戶端選型

  • 原生zookeeper客戶端,有watcher一次性、無超時重連機制等一系列問題
  • ZkClient,解決了原生客戶端一些問題,一些存量老系統中還在使用
  • curator,提供了各種應用場景(封裝了分佈式鎖,計數器等),新項目首選

分佈式鎖使用場景

在單體項目中jvm中的鎖即可完成需要,但是微服務、分佈式環境下,同一個服務可能部署在多台服務器上,多個jvm之間無法通過常用的jvm鎖來完成同步操作,需要借用分佈式鎖來完成上鎖、釋放鎖。例如在訂單服務中,我們需要根據日期來生成訂單號流水,就有可能產生相同的時間日期,從而出現重複訂單號。(jdk8使用LocalDateTime線程安全,不會存在這樣的問題)

zookeeper分佈式鎖實現原理

  • zookeeper中規定,在同一時刻,不能有多個客戶端創建同一個節點,我們可以利用這個特性實現分佈式鎖。zookeeper臨時節點只在session生命周期存在,session一結束會自動銷毀。
  • watcher機制,在代表鎖資源的節點被刪除,即可以觸發watcher解除阻塞重新去獲取鎖,這也是zookeeper分佈式鎖較其他分佈式鎖方案的一大優勢。

基於臨時節點方案

第一種方案實現較為簡單,邏輯就是誰創建成功該節點,誰就持有鎖,創建失敗的自己進行阻塞,A線程先持有鎖,B線程獲取失敗就會阻塞,同時對/lockPath設置監聽,A線程執行完操作后刪除節點,觸發監聽器,B線程此時解除阻塞,重新去獲取鎖。

我們模仿原生jdk的lock接口設計,採用模板方法設計模式來編寫分佈式鎖,這樣的好處是擴展性強,我們可以快速切換到redis分佈式鎖、數據庫分佈式鎖等實現方式。

創建Lock接口

public interface Lock {
    /**
     * 獲取鎖
     */
    void getLock() throws Exception;

    /**
     * 釋放鎖
     */
    void unlock() throws Exception;
}

AbstractTemplateLock抽象類

public abstract class AbstractTemplateLock implements Lock {
    @Override
    public void getLock() {
        if (tryLock()) {
            System.out.println(Thread.currentThread().getName() + "獲取鎖成功");
        } else {
            //等待
            waitLock();//事件監聽 如果節點被刪除則可以重新獲取
            //重新獲取
            getLock();
        }
    }
    protected abstract void waitLock();
    protected abstract boolean tryLock();
    protected abstract void releaseLock();
    @Override
    public void unlock() {
        releaseLock();
    }
}

zookeeper分佈式鎖邏輯

@Slf4j
public class ZkTemplateLock extends AbstractTemplateLock {
    private static final String zkServers = "127.0.0.1:2181";
    private static final int sessionTimeout = 8000;
    private static final int connectionTimeout = 5000;

    private static final String lockPath = "/lockPath";


    private ZkClient client;

    public ZkTemplateLock() {
        client = new ZkClient(zkServers, sessionTimeout, connectionTimeout);
        log.info("zk client 連接成功:{}",zkServers);
    }

    @Override
    protected void waitLock() {
        CountDownLatch latch = new CountDownLatch(1);

        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                System.out.println("監聽到節點被刪除");
                latch.countDown();
            }
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {}
        };
        //完成 watcher 註冊
        client.subscribeDataChanges(lockPath, listener);

        //阻塞自己
        if (client.exists(lockPath)) {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //取消watcher註冊
        client.unsubscribeDataChanges(lockPath, listener);
    }

    @Override
    protected boolean tryLock() {
        try {
            client.createEphemeral(lockPath);
            System.out.println(Thread.currentThread().getName()+"獲取到鎖");
        } catch (Exception e) {
            log.error("創建失敗");
            return false;
        }
        return true;
    }

    @Override
    public void releaseLock() {
       client.delete(this.lockPath);
    }
}

缺點

每次去競爭鎖,都只會有一個線程拿到鎖,當線程數龐大時會發生“驚群”現象,zookeeper節點可能會運行緩慢甚至宕機。這是因為其他線程沒獲取到鎖時都會監聽/lockPath節點,當A線程釋放完畢,海量的線程都同時停止阻塞,去爭搶鎖,這種操作十分耗費資源,且性能大打折扣。

基於臨時順序節點方案

臨時順序節點與臨時節點不同的是產生的節點是有序的,我們可以利用這一特點,只讓當前線程監聽上一序號的線程,每次獲取鎖的時候判斷自己的序號是否為最小,最小即獲取到鎖,執行完畢就刪除當前節點繼續判斷誰為最小序號的節點。

臨時順序節點操作源碼

@Slf4j
public class ZkSequenTemplateLock extends AbstractTemplateLock {
    private static final String zkServers = "127.0.0.1:2181";
    private static final int sessionTimeout = 8000;
    private static final int connectionTimeout = 5000;
    private static final String lockPath = "/lockPath";
    private String beforePath;
    private String currentPath;
    private ZkClient client;

    public ZkSequenTemplateLock() {
        client = new ZkClient(zkServers);
        if (!client.exists(lockPath)) {
            client.createPersistent(lockPath);

        }
        log.info("zk client 連接成功:{}",zkServers);

    }

    @Override
    protected void waitLock() {
        CountDownLatch latch = new CountDownLatch(1);
        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                System.out.println("監聽到節點被刪除");
                latch.countDown();
            }
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {}
        };
        //給排在前面的節點增加數據刪除的watcher,本質是啟動另一個線程去監聽上一個節點
        client.subscribeDataChanges(beforePath, listener);

        //阻塞自己
        if (client.exists(beforePath)) {
            try {
                System.out.println("阻塞"+currentPath);
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //取消watcher註冊
        client.unsubscribeDataChanges(beforePath, listener);
    }

    @Override
    protected boolean tryLock() {
        if (currentPath == null) {
            //創建一個臨時順序節點
            currentPath = client.createEphemeralSequential(lockPath + "/", "lock-data");
            System.out.println("current:" + currentPath);
        }

        //獲得所有的子節點並排序。臨時節點名稱為自增長的字符串
        List<String> childrens = client.getChildren(lockPath);
        //排序list,按自然順序排序
        Collections.sort(childrens);
        if (currentPath.equals(lockPath + "/" + childrens.get(0))) {
            return true;
        } else {
            //如果當前節點不是排第一,則獲取前面一個節點信息,賦值給beforePath
            int curIndex = childrens.indexOf(currentPath.substring(lockPath.length() + 1));
            beforePath = lockPath + "/" + childrens.get(curIndex - 1);
        }
        System.out.println("beforePath"+beforePath);
        return false;
    }

    @Override
    public void releaseLock() {
        System.out.println("delete:" + currentPath);
        client.delete(currentPath);
    }
}

Curator分佈式鎖工具

curator提供了以下種類的鎖:

  • 共享可重入鎖(Shared Reentrant Lock):全局同步鎖,同一時間不會有兩個客戶端持有一個鎖
  • 共享鎖:與共享可重入鎖類似,但是不可重入(有時候會因為這個原因造成死鎖)
  • 共享可重入讀寫鎖
  • 共享信號量
  • Multi Shared Lock:管理多種鎖的容器實體

我們採用第一種Shared Reentrant Lock中的InterProcessMutex來完成上鎖、釋放鎖的的操作

public class ZkLockWithCuratorTemplate implements Lock {
    // zk host地址
    private String host = "localhost";

    // zk自增存儲node
    private String lockPath = "/curatorLock";

    // 重試休眠時間
    private static final int SLEEP_TIME_MS = 1000;
    // 最大重試1000次
    private static final int MAX_RETRIES = 1000;
    //會話超時時間
    private static final int SESSION_TIMEOUT = 30 * 1000;
    //連接超時時間
    private static final int CONNECTION_TIMEOUT = 3 * 1000;
		//curator核心操作類
    private CuratorFramework curatorFramework;

    InterProcessMutex lock;

   public ZkLockWithCuratorTemplate() {
       curatorFramework = CuratorFrameworkFactory.builder()
               .connectString(host)
               .connectionTimeoutMs(CONNECTION_TIMEOUT)
               .sessionTimeoutMs(SESSION_TIMEOUT)
               .retryPolicy(new ExponentialBackoffRetry(SLEEP_TIME_MS, MAX_RETRIES))
               .build();
       curatorFramework.start();
       lock = new InterProcessMutex (curatorFramework, lockPath);
    }

    @Override
    public void getLock() throws Exception {
        //5s后超時釋放鎖
         lock.acquire(5, TimeUnit.SECONDS);
    }

    @Override
    public void unlock() throws Exception {
        lock.release();
    }
}

源碼以及測試類地址

https://github.com/Motianshi/distribute-tool

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!