湖底海綿感染疾病 貝加爾湖水質恐遭污染

摘錄自2020年7月23日公視報導

有最美湖泊之稱的俄羅斯「貝加爾湖」,向來具有豐富的生物多樣性,有超過3600種動植物,在這裡棲息。但是,近年來湖底部分地區的海綿,卻因為感染不知名的疾病,發生變色、垂死的狀況。讓研究人員憂心已產生生態危機。

過去5年來,研究員針對貝加爾湖的海底生態進行觀察,發現感染不明疾病的海綿會喪失原有的顏色,讓貝加爾湖泊流域中靠近「利斯特維揚卡」海灣地區的湖底,海床遭到垂死的有機物質覆蓋,海綿的種類逐漸減少,數量急速的降低。

「湖沼研究所」認為,目前政府應該要採取嚴厲措施,暫時禁止遊客進入「利斯特維揚卡」海灣,防止污染加重,讓大自然有時間可以自我療癒。

生物多樣性
國際新聞
俄羅斯
貝加爾湖
海綿
水文

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

日導演評《福島50英雄》:「引發災難的責任者,突然變成正義的英雄」

文:宋瑞文(媽媽監督核電廠聯盟特約撰述)

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

又一款德系轎跑SUV即將面世 先睹為快

0T V6汽油發動機和4。0T V8的柴油發動機。競品車型:寶馬X6指導價格:83。80萬-184。80萬奔馳GLE 轎跑SUV指導價格:89。54-149。8萬(僅限轎跑SUV)總結:雖然Q8僅僅只是概念曝光的程度,但是作為跨界SUV暫時尚缺的奧迪來說,推出一款劍指寶馬X6的車型也是屬於正常的事情,而最終售價,我們也不指望它能有多親民,不過未來的汽車市場多出一台高顏值的跨界SUV,也是一件可以過過眼癮的快事。



奧迪SUV我們已經很熟悉了,“Q字輩”的Q3、Q5、Q7在各自的細分市場都有着不錯的銷量,正當人們期待着售價可能會更親民的Q2儘快曝光的時候,一款定位更旗艦的Q8卻有了新的消息。

奧迪官方透露出了Q8概念車的預告圖,據悉,奧迪Q8將會在明年一月份開幕的北美車展上正式發布。

儘管是猶抱琵琶半遮面,僅僅透露出車頭一角,但可以看到前臉造型相當具有視覺侵略感,粗壯的下包圍,跑車化的輪前通風口,囂張的進氣格柵尺寸,以及造型凌厲,盡顯科幻前衛感的大燈造型,我們不妨可以猜測,這是一款運動風格非常濃郁的SUV車型。

奧迪Q8的對標車型會是寶馬X6以及GLE Coupe,由此可知它將會是一款主打跨界的運動型SUV,從車身側麵線條來看,長車頭短車尾的溜背造型,頗有一種跑車的調調。

作為定位旗艦的SUV,奧迪Q8將會有傳動燃油動力和新能源兩種版本,畢竟新能源汽車的普及就目前來說也是汽車工業國際性進程一個非常熱門的選擇。燃油動力版本不出意外將會搭載3.0T V6汽油發動機和4.0T V8的柴油發動機。

競品車型:

寶馬X6

指導價格:83.80萬-184.80萬

奔馳GLE 轎跑SUV

指導價格:89.54-149.8萬(僅限轎跑SUV)

總結:雖然Q8僅僅只是概念曝光的程度,但是作為跨界SUV暫時尚缺的奧迪來說,推出一款劍指寶馬X6的車型也是屬於正常的事情,而最終售價,我們也不指望它能有多親民,不過未來的汽車市場多出一台高顏值的跨界SUV,也是一件可以過過眼癮的快事。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

這款月銷量排第八的小型SUV,帥氣又舒適

底盤的厚實感不錯,過爛路和坑窪時的濾震吸收很好,韌性充足。轉向恐怕是紳寶X35的弱項,不僅指向有點虛,回正力還來得不自然,會在回到中間時有所停頓。這個轉向恐怕也看出來紳寶X35並不是一名“運動健將”。隔音良好紳寶X35的隔音還是有很大的提升空間,主要是風噪較為明顯,而且無論前後門都有漏風的嫌疑。

前言

北汽紳寶在小型SUV市場中布局了3輛車,分別是X25、X35和X55。之前和大家聊完了X25,現在來說說比它稍大一點的X35,畢竟這款車瞄準的市場還是很火熱的。

北京汽車-紳寶X35

指導價:6.58-8.88萬

外觀很飽滿

紳寶X35的車身尺寸為4300*1815*1640mm,軸距為2570mm。從前臉來看,整個設計很飽滿陽剛,鍍鉻裝飾條的使用也恰到好處,給人一點點高傲感。

側面來看,整個車身較為平緩,輪圈的樣式也很特別。

尾部來看較為凹凸有致,不過後擋風玻璃的尺寸偏小,同時尾燈也不大。不過,中間的鍍鉻裝飾條裝飾得恰到好處。

內飾有格調

內飾採用了雙色設計,更顯亮麗。按鈕的布局很整齊劃一,阻尼手感也不錯。空調出風口的樣式很有特色,只是中控大屏會稍顯突兀。

動力總成

紳寶X35隻有一副1.5L自然吸氣發動機,最大輸出116馬力和148牛米,與之匹配的是5擋手動變速箱和4擋自動變速箱。

這副4AT是採購自愛信公司的,也是一個靠譜之選。實際表現來看,降擋很积極,稍微深踩一點油門,變速箱就馬上降擋來響應,導致行駛時的噪音略高。而在高速120km/h巡航時的轉速維持在2800轉左右,不算很高。

底盤表現

雖然紳寶X35的后懸用的是扭力梁式非獨立懸架,但是實際表現卻帶來一點驚喜。底盤的厚實感不錯,過爛路和坑窪時的濾震吸收很好,韌性充足。

轉向恐怕是紳寶X35的弱項,不僅指向有點虛,回正力還來得不自然,會在回到中間時有所停頓。這個轉向恐怕也看出來紳寶X35並不是一名“運動健將”。

隔音良好

紳寶X35的隔音還是有很大的提升空間,主要是風噪較為明顯,而且無論前後門都有漏風的嫌疑。這也從側面看出,紳寶X35的裝配工藝與一線品牌還是有差距。

空間表現

紳寶X35的後排腿部空間充足,但頭部空間略有不足。同時,坐墊的長度不夠,無法完全承托住大腿。中部的凸起不高,對乘坐影響不大,後排中央亦配有頭枕。

油耗表現

多位車主反映紳寶X35手動擋車型的百公里綜合油耗為8.1L,而自動擋車型為8.9L。

配置分析

手動擋車型有3個版本,我會推薦買中配指導價為6.98萬的手動精英版,其比低配貴了4000元,但是多出了日間行車燈、前霧燈和后視鏡加熱,還有下圖所示的配置。

這堆配置中,ESp無疑是很重要的,而胎壓監測裝置算是同級罕見的配置。

自動擋車型也有三個版本,我會推薦買最低配指導價為7.88萬的自動精英版,這個版本的配置比手動加精英多了定速巡航,其餘配置並無差別。

編者總結:

可以看出,紳寶X35是靠其亮麗的外觀、漂亮的內飾、舒適的底盤和豐富的配置來搶佔這個市場。雖然在動力和隔音上的表現不太令人滿意,但是這個價位的車也不能要求太多。總的來說,紳寶X35算是一台值得考慮的家用車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

既能裝又不丟面,這台7萬起的MPV車主這樣評價

價格也比較實惠,開出去一點也不像幾萬塊錢的車子。配置很豐富,主副駕氣囊必不可少,還有前排側氣囊、ESp、上坡輔助、天窗、真皮方向盤、仿皮座椅、中控大屏等等,性價比很高。不滿意的:開了這麼久發動機有一絲咕嚕聲,其它沒問題,不過這應該也是小問題,其他車主沒有遇到我這樣的問題,回頭有空了開去4S點給它檢查一下,治治病。

最近身邊一個朋友想買一台七座的MpV,預算在7萬到10萬,問我選擇什麼比較好,我毫不猶豫的推薦了寶駿730。

他問我為什麼那麼直接就推薦了730,我答道:因為車主們對730很滿意。

上汽通用五菱-寶駿730

指導價:6.08-9.28萬

車主一:小羅

購買車型:2016款 1.5L 手動超值型 7座

裸車價格:5.98萬

車主點評:外形大氣,很多購買730的車友們就是看中他大氣的外觀,而且很有商務氣息,特別是車尾,感覺有那麼一絲GL8的影子。引擎蓋比較長,看上去協調不少,再加上是前置前驅,噪音比后驅車小!

不滿意的:我的車型是超值型,可能是價格已經做的夠低了,所以沒有ESp,這樣讓我跑高速的時候心有餘悸。希望730可以全系標配ESp。

車主一:隔壁老王

購買車型:2016款 1.5L 手動豪華型 7座

裸車價格:7.86萬

車主點評:最滿意的就是空間非常大,既能載人又能拉貨,外觀高端大氣上檔次,到了裏面乘坐很舒服,特別是中間兩個座椅最舒服。價格也比較實惠,開出去一點也不像幾萬塊錢的車子。配置很豐富,主副駕氣囊必不可少,還有前排側氣囊、ESp、上坡輔助、天窗、真皮方向盤、仿皮座椅、中控大屏等等,性價比很高。

不滿意的:開了這麼久發動機有一絲咕嚕聲,其它沒問題,不過這應該也是小問題,其他車主沒有遇到我這樣的問題,回頭有空了開去4S點給它檢查一下,治治病。

車主三:小喇叭

購買車型:2016款 1.5T 手動豪華型 7座

裸車價格:8.38萬

車主點評:當時試駕的是1.5L車型,感覺不是很給力,我是個急性子,受不了較弱的動力。正好730推出了1.5T車型,1.5T豪華型和1.5L豪華型指導價就差2000元,其它配置基本一樣,相當於多花2000塊買了一個渦輪發動機。絕對很值得。動力充足,爆發力強,外觀大氣,空間巨大,配置很高,口碑很好,絕對值得入手,

不滿意的:A柱盲區有點大,左轉受影響較大,1擋不是很好掛,中控台防水能力較差,做工比較一般。

總結:在這個價位,730的性價比可以說是比價高的,無論外觀還是內飾、配置、舒適性都有着很好的表現,確實是一個家用好幫手。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

來勢兇猛 看T60如何演繹皮卡新時代

在傳統思維上,也許你覺得智能化和皮卡根本毫無瓜葛。T60打破了這種陳建,首創在皮卡上開發了Normal/Eco/power三種駕駛模式。在輕載或行駛在平坦路面時,選擇ECO模式,以達到更好的燃油經濟性。在山路、工地等複雜路況或重載時,選擇power模式,帶來更強勁的動力。

在中國,皮卡一直以邊緣車型處在一個尷尬境地,各類歧視性政策更是讓實用價值很高的皮卡被人們敬而遠之,和國外濃厚的皮卡文化相比,我們仍然處於一個嬰兒般的市場狀態,如何培育,如何增長,如何推廣,都成了皮卡的廠商一個大難題,而且中國皮卡造型多以中庸的外觀出現,講究皮實耐用,與國內80后、90后追求外觀、個性的性格有些格格不入,所以,皮卡車企如何適應當前社會潮流,推出適合當下年輕人喜歡的中高端皮卡,將是刺激當前皮卡市場的一劑強心劑,也是一個對於廠商來說一個最大的突破口。

縱觀國內皮卡市場,雖然每年有着40萬輛左右的銷量,但產品的各方面整體較落後,市場上缺少一款性能卓越,讓用戶滿意的產品。在此背景下,上汽T60的誕生就是為了引領並推動國內皮卡市場。

12月21日,上汽大通邀經銷商夥伴聚集海南博鰲,回顧輝煌業績,共贏未來。回望2016年,在今年的廣州車展上,上汽大通推出了“划時代皮卡”——T60,當時只是公布了售價區間為9.98-19.98萬元。

與會期間,上汽大通為大家揭開了最後的謎底,官方公布T60各款車型的價格

T60柴油兩驅低底盤價格表

T60柴油兩驅高底盤價格表

T60柴油四驅高底盤價格表

突破傳統,時尚觀感遇見智能互聯

T60在外觀造型上有了重大突破,在我們之前的印象中,皮卡一向講求“內修”只要實用,空間大,力量足就行,但是T60確實是做到了“內外兼修”摒棄此前皮卡車過於單調、落後的特點,用迎合年輕一代用車群體的需求作為設計出發點,這款T60皮卡有着眾多獨特個性與亮點,稜角分明的前臉造型線條十分豐富,直瀑式進氣格柵突出了皮卡車型硬朗風格的同時也融入現代SUV的時尚元素。多邊形前大燈組造型,近光燈帶有透鏡結構,保險杠底部的霧燈則顯得十分秀氣。直觀的感受,T60帶來一款讓人耳目一新的皮卡。

在內飾設計上,T60整體造型顯得十分簡潔,全車黑色搭配銀色鍍鉻飾條,T形對稱式中控檯布局,大尺寸的液晶显示屏搭載了上汽與阿里共同開發的YunOS車聯網系統,提供智能車管家、在線互聯、遠程控制、語音識別等智能功能,這些科技的配置完全不輸一台B級車的配置了,而且皮卡上配備了pEpS無鑰匙進入一鍵啟動功能,這樣的皮卡配置確實有些無敵了。

作為一款皮卡,動力方面是其必須具備的,畢竟它的主要角色擔當是裝載及運輸,所以對於動力的要求會很高,那T60搭載了來自意大利VM的2.8T柴油發動機,其最大功率為110千瓦,最大扭矩360牛米,這樣的動力是足夠消費者輕鬆應對各種繁重任務了。

值得一提的,T60首次在皮卡上配備了6AT自動變速箱,這讓其在彎道或變速方面更加應付自如。

在傳統思維上,也許你覺得智能化和皮卡根本毫無瓜葛。T60打破了這種陳建,首創在皮卡上開發了Normal/Eco/power三種駕駛模式。在輕載或行駛在平坦路面時,選擇ECO模式,以達到更好的燃油經濟性;在山路、工地等複雜路況或重載時,選擇power模式,帶來更強勁的動力。這種改變,大大滿足了越來越多的年輕用戶希望有更輕鬆的駕駛體驗的需求。

最後,不得不提是T60在安全配置方面的專家級表現,T60以澳洲A-NCAp五星碰撞安全標準進行設計,首次將熱成型技術運用於皮卡,並通過激光焊接,大幅提升車身強度;T60還率先採用6安全氣囊,為駕乘人員提供全方位的保護。

T60首次在皮卡上配備,AFS隨動轉向LED大燈,根據車輛轉向,自動調節大燈照射角度,為用戶夜晚行車,提供更寬闊的照明視野。

在面對雨雪泥濘、崎嶇山路行駛時出現甩尾、側滑等險情時, T60採用BOSCH 9.0,ESp車身穩定控制系統,給皮卡用戶在各種工況下,帶來更好的行車穩定性。並把360環視影像、LDW車道偏離預警、疲勞駕駛提醒,等多項安全配置,首次應用在皮卡上,提供全方位的安全保障。

在美國,皮卡代表了張揚、自由與個性,涵蓋了美國英雄主義的西部文化,駕駛皮卡犹如之前的騎馬一樣輕鬆自然,而在中國恰恰缺少美國一樣的皮卡文化。

隨着皮卡市場的春天來臨,大家對皮卡的關注與討論與日俱增。同時,上汽大通T60更是憑藉行業首創的C2B理念,提供最全面的產品型譜和組合,上汽大通T60作為一輛外觀時尚、性能卓越、注重安全的皮卡,將會引領皮卡市場走向宜商宜家的高端路線。這樣一款划時代的皮卡,讓我們更加期待其精彩的市場表現。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

車輪上的家 上汽大通發布全新房車RV80

98萬的價格。而且RV80型譜包含B型和C型房車,擁有短軸/長軸/加長軸,低頂/中頂/高頂多種車身形式,用戶可通過C2B線上交互平台,可直接在線訂製房車,OTD高效透明,消費者可參与試駕、試住等全體驗式互動,享受真正的價值和樂趣。

買一輛房車,開到郊外,給自己一個遠離城市堵車和污染的樂土,傍晚一邊欣賞湖邊那一抹落日一邊和家人烹制一頓可口的晚餐;晚上透過大大的天窗看沒有污染的星空;亦或把車停在海邊,換上泳衣下海衝浪,回來后在寬敞的洗澡間沖個熱水澡這份嚮往其實不僅僅是一輛車,而是一種生活方式更是一種房車生活的文化,這才是一種說走就走的旅行,因為不用規劃路線,不用訂酒店,風景在那,走到那,家在路上。

近日,一場融新款房車上市發布、房車露營大會、草地音樂狂歡節、美酒佳肴於一體的“全體驗式”上市發布會登陸海南博鰲,由“中國房車第一品牌”上汽大通打造的2016“新旅程·心享受”房車之夜暨房車新品上市發布會亮相南海之濱,全新房車RV80在萬眾期待中揭開神秘面紗。

當晚,配合現場炫目的燈光秀,國內首款滿足國五排放標準的高端房車——上汽大通全新房車RV80正式發布。

新款房車憑藉六大產品優勢,定義房車行業新標杆,其搭載源自歐洲的VM柴油發動機,最高功率110KW,綜合油耗百公里僅為8.3L;全系國5排放標準;在國內房車行業中率先使用AMT技術;而且全車座椅是達乘用級安全標準。

消費者最為關心的價格也在上市會上正式揭曉,售:26.98萬至51.98萬的價格。

而且RV80型譜包含B型和C型房車,擁有短軸/長軸/加長軸,低頂/中頂/高頂多種車身形式,用戶可通過C2B線上交互平台,可直接在線訂製房車,OTD高效透明,消費者可參与試駕、試住等全體驗式互動,享受真正的價值和樂趣。

雖然國內房車起步晚,房車少、營地少,在心往來說,移動露營來說比較難實現,但隨着房車市場近幾年的發展,房車露營地的逐漸增多,露營地的規劃與擬建逐漸完善,移動露營的實現,在今天已經變的越來越便利了。

房車,又稱“車輪上的家”,兼具“房”與“車”兩大功能,但其屬性還是車,相比普通汽車,房車上的居家設施有爐具、冰箱、櫥櫃、沙發、餐桌椅、盥洗設施、空調、電視、音響等傢具和電器等,隨心而行,隨景而歇,這是一種房車的生活狀態。

購買房車的理由可以很多,可能是為了帶着父母去旅行,也可能是為了滿足夫妻雙方共同的旅行夢想,也可以一群朋友同事用懷揣探索的心踏上未知的旅途,無論是兩個人駕駛房車出行,要過一次久違的二人世界或帶着一家人遠離都市的喧鬧,這都是我們嚮往的生活,而房車是對於這種嚮往的一個載體,一個最適合的載體。

國內消費者對待房車的消費觀念正在發生轉變,讓目前的房車市場顯得更為火爆,現在很多人的旅遊觀念都在轉變,更加傾向於自駕出遊,而非報團旅行。而自駕的長途旅行,使得房車成為了旅行者的首選。

但相應的配套設施卻略顯單薄,最大的制約就是目前國內房車露營地較少,無法為房車提供停放場所。畢竟在房車內生活,做飯、取暖、照明等都需要電力和水源,雖然房車裡自帶有蓄電池和水源儲蓄,但容量的限制最多只能保證一兩個晚上的生活需求,這也是制約房車發展最重要的一個條件。

對於現在房車的各方面使用性來說,可能有些人覺的買了回來,使用率比較低,上汽大通也在2017年投入超過1200輛房車,試水房車租賃市場,與運營夥伴在全國鋪開租賃網絡。

讓用戶輕鬆體驗到大通房車落地自駕的樂趣;開發了“房車生活家”平台,為房車用戶和潛客用戶提供“@房車”、“@生活”、“@家”三個方面的服務,構建多個使用和體驗場景,實現在線房車自駕游線路推薦和預訂、營地推薦和預訂,閱讀攻略遊記、預約試駕體驗、參加自駕活動等功能。

也可掃二維碼了解更多房車生活及房車租賃信息,而且還有很多優惠信息喔!

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

別唧唧歪歪了!年輕人第一台SUV就該選TA!

但相信2765mm的軸距將會給用戶一個滿意的乘坐空間。後排無論在橫向還是縱向空間都是足夠大而舒適,頭部空間則因車頂後排頭頂位置為內凹設計,也是很讓人滿意。“低”價格,高配置啟辰T90的內飾給人的感覺跟外觀還是有些不一樣,整體的風格偏向於穩重,但穩重中也沒有忽略運動感的塑造。

在目前的國內市場,SUV尤其的火爆,可以這麼說,當人們有需求的時候,市場就會自動做出調整生產符合消費者要求的產品。在這種情況下,很多廠家也紛紛都將矛頭指向了緊湊型的SUV,以及二胎政策捧紅的7座中型SUV。然而,啟辰獨辟蹊徑,瞄準了85后消費群體,打造了溜背式的中型SUV啟辰T90,憑藉媲美合資車的生產品質、顛覆傳統的驚艷設計、充滿競爭力的價格,勢必成為中型SUV現有市場格局的全新標杆。

12月25日,啟辰全新中型SUV啟辰T90正式上市,售價區間為10.98—15.48萬元,共推出6款車型。

個性時尚的外觀

啟辰T90最大的亮點可以說是外觀造型,無論是前臉還是性感的“背影”,開這麼一款SUV在路上,回頭率都會是極高的。其採用了全新的家族式前臉和溜背式SUV的風雕美學設計。不是華麗的簡單堆砌,而是經過設計師無數次對細節完美追求的結果。總體來看,不管是在研發設計還是產品定位上,啟辰T90的設計感並不亞於一些合資品牌。

寬敞舒適的空間

說到這裏,也許有人會問,採用溜背造型的設計,其後排空間會不會就因此受到影響,要說完全沒有,這個有點不現實。但相信2765mm的軸距將會給用戶一個滿意的乘坐空間。後排無論在橫向還是縱向空間都是足夠大而舒適,頭部空間則因車頂後排頭頂位置為內凹設計,也是很讓人滿意。

“低”價格,高配置

啟辰T90的內飾給人的感覺跟外觀還是有些不一樣,整體的風格偏向於穩重,但穩重中也沒有忽略運動感的塑造。值得一提的還有,中控台上12.3英寸的大屏,從布局上就可以看得出設計的用心,其呈環抱式位置分佈,主駕駛席無需起身即可完成所有操作;真皮座椅,搭配着藍色的氛圍燈,精密的雙縫線,各處接縫的高度契合,使內飾整體更有質感,是現代年輕一代受眾所青睞的。

除了以上提到的,還有智能互聯、高科技配置和安全配置等方面,啟辰T90也展示出非凡的實力,可以說是智超所值。IT-COMMANDER旋鈕式信息控制系統堪稱同級SUV罕有的“鼠標”式車載控制系統,集便利性、簡易性、精美性於一體,觸控隨心所欲;全彩3D平視信息显示系統以駕駛者為中心,操控簡潔。啟辰T90還具備駕駛提醒、胎壓監測、泊車輔助等功能,在白色高亮質感照明和多功能方向盤按鍵控制下,能夠帶來更清晰、更科技、更好操作的显示效果。

總結:如今中型SUV市場,早已是一片紅海,啟辰T90無論是獨樹一幟的外觀,還是空間、配置,以及親民的價格,都可以說,將會讓它在這片紅海中,逆襲上位、脫穎而出,成為業內的一個新標杆。期待在未來,啟辰T90成為下一个中型SUV爆款。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

存活1億年!南太平洋海底發現恐龍時代的微生物

摘錄自2020年7月29日自由時報報導

日本海洋研究開發機構和高知大學所組成的團隊,在南太平洋海底下約有1億年至430萬年的地層從發現微生物,這些微生物並未變成化石,而是在提供營養後,竟然可以從長期休眠中復甦。

據日本《共同社》報導,日本研究團隊在2010年於紐西蘭以東的海域進行挖掘研究,他們從3700至5700公尺深的海底中挖掘7處地層,發現了像是被封閉在細微粒子組成的粘土之中的微生物,為了確認微生物是否仍然存活還是變成化石,團隊開始提供氧氣和糖等餌食進行了觀察。

實驗啟動3週後,微生物竟然復甦開始進食,約2個月後最大增至1萬倍以上。細胞分裂平均從喂餌5天後開始,微生物平均復甦率為77%,而年代最久遠的1億年地層,存活率更是高達99.1%。

海洋
國際新聞
南太平洋

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

【其他文章推薦】

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

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

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

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

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

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

akka-typed(6) – cluster:group router, cluster-load-balancing

先談談akka-typed的router actor。route 分pool router, group router兩類。我們先看看pool-router的使用示範:

      val pool = Routers.pool(poolSize = 4)( // make sure the workers are restarted if they fail
 Behaviors.supervise(WorkerRoutee()).onFailure[Exception](SupervisorStrategy.restart)) val router = ctx.spawn(pool, "worker-pool") (0 to 10).foreach { n => router ! WorkerRoutee.DoLog(s"msg $n")
      }

上面例子里的pool是個pool-router,意思是一個有4個routees的routee池。每個routee都是通過WorkerRoutee()構建的,意味着routee池中只有一個種類的actor。pool-router是通過工廠方法直接在本地(JVM)構建(spawn)所有的routee。也就是說所有routee都是router的子actor。

再看看group-router的使用例子:

val serviceKey = ServiceKey[Worker.Command]("log-worker") // this would likely happen elsewhere - if we create it locally we // can just as well use a pool
      val workerRoutee = ctx.spawn(WorkerRoutee(), "worker-route") ctx.system.receptionist ! Receptionist.Register(serviceKey, workerRoutee) val group = Routers.group(serviceKey) val router = ctx.spawn(group, "worker-group") // the group router will stash messages until it sees the first listing of registered // services from the receptionist, so it is safe to send messages right away
      (0 to 10).foreach { n => router ! WorkerRoutee.DoLog(s"msg $n") }

group-router與pool-router有較多分別:

1、routee是在router之外構建的,router是用一個key通過Receptionist獲取同key的actor清單作為routee group的

2、Receptionist是集群全局的。任何節點上的actor都可以發送註冊消息在Receptionist上登記

3、沒有size限制,任何actor一旦在Receptionist上登記即變成routee,接受router管理

應該說如果想把運算任務分配在集群里的各節點上并行運算實現load-balance效果,group-router是最合適的選擇。不過對不同的運算任務需要多少routee則需要用戶自行決定,不像以前akka-classic里通過cluster-metrics根據節點負載情況自動增減routee實例那麼方便。

Receptionist: 既然說到,那麼就再深入一點介紹Receptionist的應用:上面提到,Receptionist是集群全局的。就是說任何節點上的actor都可以在Receptonist上註冊形成一個生存在集群中不同節點的actor清單。如果Receptionist把這個清單提供給一個用戶,那麼這個用戶就可以把運算任務配置到各節點上,實現某種意義上的分佈式運算模式。Receptionist的使用方式是:通過向本節點的Receptionist發送消息去登記ActorRef,然後通過Receptionist發布的登記變化消息即可獲取最新的ActorRef清單:

  val WorkerServiceKey = ServiceKey[Worker.TransformText]("Worker") ctx.system.receptionist ! Receptionist.Register(WorkerServiceKey, ctx.self) ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter)

Receptionist的登記和清單獲取是以ServiceKey作為關聯的。那麼獲取的清單內應該全部是一種類型的actor,只不過它們的地址可能是跨節點的,但它們只能進行同一種運算。從另一個角度說,一項任務是分佈在不同節點的actor并行進行運算的。

在上篇討論里提過:如果發布-訂閱機制是在兩個actor之間進行的,那麼這兩個actor也需要在規定的信息交流協議框架下作業:我們必須注意消息類型,提供必要的消息類型轉換機制。下面是一個Receptionist登記示範:

object Worker { val WorkerServiceKey = ServiceKey[Worker.TransformText]("Worker") sealed trait Command final case class TransformText(text: String, replyTo: ActorRef[TextTransformed]) extends Command with CborSerializable final case class TextTransformed(text: String) extends CborSerializable def apply(): Behavior[Command] = Behaviors.setup { ctx =>
      // each worker registers themselves with the receptionist
      ctx.log.info("Registering myself with receptionist") ctx.system.receptionist ! Receptionist.Register(WorkerServiceKey, ctx.self) Behaviors.receiveMessage { case TransformText(text, replyTo) => replyTo ! TextTransformed(text.toUpperCase) Behaviors.same } } }

Receptionist登記比較直接:登記者不需要Receptionist返回消息,所以隨便用ctx.self作為消息的sender。注意TransformText的replyTo: ActorRef[TextTransformed],代表sender是個可以處理TextTransformed消息類型的actor。實際上,在sender方是通過ctx.ask提供了TextTransformed的類型轉換。

Receptionist.Subscribe需要Receptionist返回一個actor清單,所以是個request/response模式。那麼發送給Receptionist消息中的replyTo必須是發送者能處理的類型,如下:

  def apply(): Behavior[Event] = Behaviors.setup { ctx => Behaviors.withTimers { timers =>
      // subscribe to available workers
      val subscriptionAdapter = ctx.messageAdapter[Receptionist.Listing] { case Worker.WorkerServiceKey.Listing(workers) => WorkersUpdated(workers) } ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter) ... } 

ctx.messageAdapter進行了一個從Receptionist.Listing返回類型到WorkersUpdated類型的轉換機制登記:從Receptionist回復的List類型會被轉換成WorkersUpdated類型,如下:

... Behaviors.receiveMessage { case WorkersUpdated(newWorkers) => ctx.log.info("List of services registered with the receptionist changed: {}", newWorkers) ...

另外,上面提過的TextTransformed轉換如下:

 ctx.ask[Worker.TransformText,Worker.TextTransformed](selectedWorker, Worker.TransformText(text, _)) { case Success(transformedText) => TransformCompleted(transformedText.text, text) case Failure(ex) => JobFailed("Processing timed out", text) }

ctx.ask將TextTransformed轉換成TransformCompleted。完整的Behavior定義如下:

object Frontend { sealed trait Event private case object Tick extends Event private final case class WorkersUpdated(newWorkers: Set[ActorRef[Worker.TransformText]]) extends Event private final case class TransformCompleted(originalText: String, transformedText: String) extends Event private final case class JobFailed(why: String, text: String) extends Event def apply(): Behavior[Event] = Behaviors.setup { ctx => Behaviors.withTimers { timers =>
      // subscribe to available workers
      val subscriptionAdapter = ctx.messageAdapter[Receptionist.Listing] { case Worker.WorkerServiceKey.Listing(workers) => WorkersUpdated(workers) } ctx.system.receptionist ! Receptionist.Subscribe(Worker.WorkerServiceKey, subscriptionAdapter) timers.startTimerWithFixedDelay(Tick, Tick, 2.seconds) running(ctx, IndexedSeq.empty, jobCounter = 0) } } private def running(ctx: ActorContext[Event], workers: IndexedSeq[ActorRef[Worker.TransformText]], jobCounter: Int): Behavior[Event] = Behaviors.receiveMessage { case WorkersUpdated(newWorkers) => ctx.log.info("List of services registered with the receptionist changed: {}", newWorkers) running(ctx, newWorkers.toIndexedSeq, jobCounter) case Tick =>
        if (workers.isEmpty) { ctx.log.warn("Got tick request but no workers available, not sending any work") Behaviors.same } else { // how much time can pass before we consider a request failed
          implicit val timeout: Timeout = 5.seconds val selectedWorker = workers(jobCounter % workers.size) ctx.log.info("Sending work for processing to {}", selectedWorker) val text = s"hello-$jobCounter" ctx.ask[Worker.TransformText,Worker.TextTransformed](selectedWorker, Worker.TransformText(text, _)) { case Success(transformedText) => TransformCompleted(transformedText.text, text) case Failure(ex) => JobFailed("Processing timed out", text) } running(ctx, workers, jobCounter + 1) } case TransformCompleted(originalText, transformedText) => ctx.log.info("Got completed transform of {}: {}", originalText, transformedText) Behaviors.same case JobFailed(why, text) => ctx.log.warn("Transformation of text {} failed. Because: {}", text, why) Behaviors.same }

現在我們可以示範用group-router來實現某種跨節點的分佈式運算。因為group-router是通過Receptionist來實現對routees管理的,而Receptionist是集群全局的,意味着如果我們在各節點上構建routee,然後向Receptionist登記,就會形成一個跨節點的routee ActorRef清單。如果把任務分配到這個清單上的routee上去運算,應該能實現集群節點負載均衡的效果。下面我們就示範這個loadbalancer。流程很簡單:在一個接入點 (serviceActor)中構建workersRouter,然後3個workerRoutee並向Receptionist登記,把接到的任務分解成子任務逐個發送給workersRouter。每個workerRoutee完成任務后將結果發送給一個聚合器Aggregator,Aggregator在核對完成接收所有workerRoutee返回的結果后再把匯總結果返回serverActor。先看看這個serverActor:

object Service { val routerServiceKey = ServiceKey[WorkerRoutee.Command]("workers-router") sealed trait Command extends CborSerializable case class ProcessText(text: String) extends Command { require(text.nonEmpty) } case class WrappedResult(res: Aggregator.Response) extends Command def serviceBehavior(workersRouter: ActorRef[WorkerRoutee.Command]): Behavior[Command] = Behaviors.setup[Command] { ctx => val aggregator = ctx.spawn(Aggregator(), "aggregator") val aggregatorRef: ActorRef[Aggregator.Response] = ctx.messageAdapter(WrappedResult) Behaviors.receiveMessage { case ProcessText(text) => ctx.log.info("******************** received ProcessText command: {} ****************",text) val words = text.split(' ').toIndexedSeq aggregator ! Aggregator.CountText(words.size, aggregatorRef) words.foreach { word => workersRouter ! WorkerRoutee.Count(word, aggregator) } Behaviors.same case WrappedResult(msg) => msg match { case Aggregator.Result(res) => ctx.log.info("************** mean length of words = {} **********", res) } Behaviors.same } } def singletonService(ctx: ActorContext[Command], workersRouter: ActorRef[WorkerRoutee.Command]) = { val singletonSettings = ClusterSingletonSettings(ctx.system) .withRole("front") SingletonActor( Behaviors.supervise( serviceBehavior(workersRouter) ).onFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ) , "singletonActor" ).withSettings(singletonSettings) } def apply(): Behavior[Command] = Behaviors.setup[Command] { ctx => val cluster = Cluster(ctx.system) val workersRouter = ctx.spawn( Routers.group(routerServiceKey) .withRoundRobinRouting(), "workersRouter" ) (0 until 3).foreach { n => val routee = ctx.spawn(WorkerRoutee(cluster.selfMember.address.toString), s"work-routee-$n") ctx.system.receptionist ! Receptionist.register(routerServiceKey, routee) } val singletonActor = ClusterSingleton(ctx.system).init(singletonService(ctx, workersRouter)) Behaviors.receiveMessage { case job@ProcessText(text) => singletonActor ! job Behaviors.same } } }

整體goup-router和routee的構建是在apply()里,並把接到的任務轉發給singletonActor。singletonActor是以serviceBehavior為核心的一個actor。在servceBehavior里把收到的任務分解並分別發送給workersRouter。值得注意的是:serviceBehavior期望接收從Aggregator的回應,它們之間存在request/response模式信息交流,所以需要Aggregator.Response到WrappedResult的類型轉換機制。還有:子任務是通過workersRoute發送給個workerRoutee的,我們需要各workerRoutee把運算結果返給給Aggregator,所以發送給workersRouter的消息包含了Aggregator的ActorRef,如:workersRouter ! WorkerRoutee.Count(cnt,aggregatorRef)。

Aggregator是個persistentActor, 如下:

 

object Aggregator { sealed trait Command sealed trait Event extends CborSerializable sealed trait Response case class CountText(cnt: Int, replyTo: ActorRef[Response]) extends Command case class MarkLength(word: String, len: Int) extends Command case class TextCounted(cnt: Int) extends Event case class LengthMarked(word: String, len: Int) extends Event case class Result(meanWordLength: Double) extends Response case class State(expectedNum: Int = 0, lens: List[Int] = Nil) var replyTo: ActorRef[Response] = _ def commandHandler: (State,Command) => Effect[Event,State] = (st,cmd) => { cmd match { case CountText(cnt,ref) => replyTo = ref Effect.persist(TextCounted(cnt)) case MarkLength(word,len) => Effect.persist(LengthMarked(word,len)) } } def eventHandler: (State, Event) => State = (st,ev) => { ev match { case TextCounted(cnt) => st.copy(expectedNum = cnt, lens = Nil) case LengthMarked(word,len) => val state = st.copy(lens = len :: st.lens) if (state.lens.size >= state.expectedNum) { val meanWordLength = state.lens.sum.toDouble / state.lens.size replyTo ! Result(meanWordLength) State() } else state } } val takeSnapShot: (State,Event,Long) => Boolean = (st,ev,seq) => { if (st.lens.isEmpty) { if (ev.isInstanceOf[LengthMarked]) true
          else
            false } else
         false } def apply(): Behavior[Command] = Behaviors.supervise( Behaviors.setup[Command] { ctx => EventSourcedBehavior( persistenceId = PersistenceId("33","2333"), emptyState = State(), commandHandler = commandHandler, eventHandler = eventHandler ).onPersistFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ).receiveSignal { case (state, RecoveryCompleted) => ctx.log.info("**************Recovery Completed with state: {}***************",state) case (state, SnapshotCompleted(meta))  => ctx.log.info("**************Snapshot Completed with state: {},id({},{})***************",state,meta.persistenceId, meta.sequenceNr) case (state,RecoveryFailed(err)) => ctx.log.error("*************recovery failed with: {}***************",err.getMessage) case (state,SnapshotFailed(meta,err)) => ctx.log.error("***************snapshoting failed with: {}*************",err.getMessage) }.snapshotWhen(takeSnapShot) } ).onFailure( SupervisorStrategy .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1) .withMaxRestarts(3) .withResetBackoffAfter(10.seconds) ) }

注意這個takeSnapShot函數:這個函數是在EventSourcedBehavior.snapshotWhen(takeSnapShot)調用的。傳入參數是(State,Event,seqenceNr),我們需要對State,Event的當前值進行分析后返回true代表做一次snapshot。

看看一部分显示就知道任務已經分配到幾個節點上的routee:

20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-15] INFO com.learn.akka.WorkerRoutee$ - ************** processing [this] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [text] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.072 [ClusterSystem-akka.actor.default-dispatcher-36] INFO com.learn.akka.WorkerRoutee$ - ************** processing [be] on akka://ClusterSystem@127.0.0.1:51182 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-16] INFO com.learn.akka.WorkerRoutee$ - ************** processing [will] on akka://ClusterSystem@127.0.0.1:51173 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-26] INFO com.learn.akka.WorkerRoutee$ - ************** processing [is] on akka://ClusterSystem@127.0.0.1:25251 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-13] INFO com.learn.akka.WorkerRoutee$ - ************** processing [the] on akka://ClusterSystem@127.0.0.1:51173 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [that] on akka://ClusterSystem@127.0.0.1:25251 ***********
20:06:59.236 [ClusterSystem-akka.actor.default-dispatcher-3] INFO com.learn.akka.WorkerRoutee$ - ************** processing [analyzed] on akka://ClusterSystem@127.0.0.1:25251 ***********

這個例子的源代碼如下:

package com.learn.akka

import akka.actor.typed._
import akka.persistence.typed._
import akka.persistence.typed.scaladsl._
import scala.concurrent.duration._
import akka.actor.typed.receptionist._
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl._
import akka.cluster.typed.Cluster
import akka.cluster.typed.ClusterSingleton
import akka.cluster.typed.ClusterSingletonSettings
import akka.cluster.typed.SingletonActor
import com.typesafe.config.ConfigFactory

object WorkerRoutee {
  sealed trait Command extends CborSerializable
  case class Count(word: String, replyTo: ActorRef[Aggregator.Command]) extends Command

  def apply(nodeAddress: String): Behavior[Command] = Behaviors.setup {ctx =>
    Behaviors.receiveMessage[Command] {
      case Count(word,replyTo) =>
        ctx.log.info("************** processing [{}] on {} ***********",word,nodeAddress)
        replyTo ! Aggregator.MarkLength(word,word.length)
        Behaviors.same
    }
  }
}
object Aggregator {
  sealed trait Command
  sealed trait Event extends  CborSerializable
  sealed trait Response

  case class CountText(cnt: Int, replyTo: ActorRef[Response]) extends Command
  case class MarkLength(word: String, len: Int) extends Command
  case class TextCounted(cnt: Int) extends Event
  case class LengthMarked(word: String, len: Int) extends Event
  case class Result(meanWordLength: Double) extends Response

  case class State(expectedNum: Int = 0, lens: List[Int] = Nil)

  var replyTo: ActorRef[Response] = _

  def commandHandler: (State,Command) => Effect[Event,State] = (st,cmd) => {
    cmd match {
      case CountText(cnt,ref) =>
        replyTo = ref
        Effect.persist(TextCounted(cnt))
      case MarkLength(word,len) =>
        Effect.persist(LengthMarked(word,len))
    }
  }
  def eventHandler: (State, Event) => State = (st,ev) => {
    ev match {
      case TextCounted(cnt) =>
        st.copy(expectedNum = cnt, lens = Nil)
      case LengthMarked(word,len) =>
        val state = st.copy(lens = len :: st.lens)
        if (state.lens.size >= state.expectedNum) {
          val meanWordLength = state.lens.sum.toDouble / state.lens.size
          replyTo ! Result(meanWordLength)
          State()
        } else state
    }
  }
  val takeSnapShot: (State,Event,Long) => Boolean = (st,ev,seq) => {
      if (st.lens.isEmpty) {
          if (ev.isInstanceOf[LengthMarked])
            true
          else
            false
      } else
         false
  }
  def apply(): Behavior[Command] = Behaviors.supervise(
    Behaviors.setup[Command] { ctx =>
      EventSourcedBehavior(
        persistenceId = PersistenceId("33","2333"),
        emptyState = State(),
        commandHandler = commandHandler,
        eventHandler = eventHandler
      ).onPersistFailure(
        SupervisorStrategy
          .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
          .withMaxRestarts(3)
          .withResetBackoffAfter(10.seconds)
      ).receiveSignal {
        case (state, RecoveryCompleted) =>
          ctx.log.info("**************Recovery Completed with state: {}***************",state)
        case (state, SnapshotCompleted(meta))  =>
          ctx.log.info("**************Snapshot Completed with state: {},id({},{})***************",state,meta.persistenceId, meta.sequenceNr)
        case (state,RecoveryFailed(err)) =>
          ctx.log.error("*************recovery failed with: {}***************",err.getMessage)
        case (state,SnapshotFailed(meta,err)) =>
          ctx.log.error("***************snapshoting failed with: {}*************",err.getMessage)
      }.snapshotWhen(takeSnapShot)
    }
  ).onFailure(
    SupervisorStrategy
      .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
      .withMaxRestarts(3)
      .withResetBackoffAfter(10.seconds)
  )
}
object Service {
  val routerServiceKey = ServiceKey[WorkerRoutee.Command]("workers-router")

  sealed trait Command extends CborSerializable

  case class ProcessText(text: String) extends Command {
    require(text.nonEmpty)
  }

  case class WrappedResult(res: Aggregator.Response) extends Command

  def serviceBehavior(workersRouter: ActorRef[WorkerRoutee.Command]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
    val aggregator = ctx.spawn(Aggregator(), "aggregator")
    val aggregatorRef: ActorRef[Aggregator.Response] = ctx.messageAdapter(WrappedResult)
    Behaviors.receiveMessage {
      case ProcessText(text) =>
        ctx.log.info("******************** received ProcessText command: {} ****************",text)
        val words = text.split(' ').toIndexedSeq
        aggregator ! Aggregator.CountText(words.size, aggregatorRef)
        words.foreach { word =>
          workersRouter ! WorkerRoutee.Count(word, aggregator)
        }
        Behaviors.same
      case WrappedResult(msg) =>
        msg match {
          case Aggregator.Result(res) =>
            ctx.log.info("************** mean length of words = {} **********", res)
        }
        Behaviors.same
    }
  }

  def singletonService(ctx: ActorContext[Command], workersRouter: ActorRef[WorkerRoutee.Command]) = {
    val singletonSettings = ClusterSingletonSettings(ctx.system)
      .withRole("front")
    SingletonActor(
      Behaviors.supervise(
        serviceBehavior(workersRouter)
      ).onFailure(
        SupervisorStrategy
          .restartWithBackoff(minBackoff = 10.seconds, maxBackoff = 60.seconds, randomFactor = 0.1)
          .withMaxRestarts(3)
          .withResetBackoffAfter(10.seconds)
      )
      , "singletonActor"
    ).withSettings(singletonSettings)
  }

  def apply(): Behavior[Command] = Behaviors.setup[Command] { ctx =>
    val cluster = Cluster(ctx.system)
    val workersRouter = ctx.spawn(
      Routers.group(routerServiceKey)
        .withRoundRobinRouting(),
      "workersRouter"
    )
    (0 until 3).foreach { n =>
      val routee = ctx.spawn(WorkerRoutee(cluster.selfMember.address.toString), s"work-routee-$n")
      ctx.system.receptionist ! Receptionist.register(routerServiceKey, routee)
    }
    val singletonActor = ClusterSingleton(ctx.system).init(singletonService(ctx, workersRouter))
    Behaviors.receiveMessage {
      case job@ProcessText(text) =>
        singletonActor ! job
        Behaviors.same
    }
  }

}

object LoadBalance {
  def main(args: Array[String]): Unit = {
    if (args.isEmpty) {
      startup("compute", 25251)
      startup("compute", 25252)
      startup("compute", 25253)
      startup("front", 25254)
    } else {
      require(args.size == 2, "Usage: role port")
      startup(args(0), args(1).toInt)
    }
  }

  def startup(role: String, port: Int): Unit = {
    // Override the configuration of the port when specified as program argument
    val config = ConfigFactory
      .parseString(s"""
      akka.remote.artery.canonical.port=$port
      akka.cluster.roles = [$role]
      """)
      .withFallback(ConfigFactory.load("cluster-persistence"))

    val frontEnd = ActorSystem[Service.Command](Service(), "ClusterSystem", config)
    if (role == "front") {
      println("*************** sending ProcessText command  ************")
      frontEnd ! Service.ProcessText("this is the text that will be analyzed")
    }

  }

}

cluster-persistence.conf

akka.actor.allow-java-serialization = on
akka {
  loglevel = INFO
  actor {
    provider = cluster
    serialization-bindings {
      "com.learn.akka.CborSerializable" = jackson-cbor
    }
  }
 remote {
    artery {
      canonical.hostname = "127.0.0.1"
      canonical.port = 0
    }
  }
  cluster {
    seed-nodes = [
      "akka://ClusterSystem@127.0.0.1:25251",
      "akka://ClusterSystem@127.0.0.1:25252"]
  }
  # use Cassandra to store both snapshots and the events of the persistent actors
  persistence {
    journal.plugin = "akka.persistence.cassandra.journal"
    snapshot-store.plugin = "akka.persistence.cassandra.snapshot"
  }
}
akka.persistence.cassandra {
  # don't use autocreate in production
  journal.keyspace = "poc"
  journal.keyspace-autocreate = on
  journal.tables-autocreate = on
  snapshot.keyspace = "poc_snapshot"
  snapshot.keyspace-autocreate = on
  snapshot.tables-autocreate = on
}

datastax-java-driver {
  basic.contact-points = ["192.168.11.189:9042"]
  basic.load-balancing-policy.local-datacenter = "datacenter1"
}

build.sbt

name := "learn-akka-typed"

version := "0.1"

scalaVersion := "2.13.1"
scalacOptions in Compile ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlog-reflective-calls", "-Xlint")
javacOptions in Compile ++= Seq("-Xlint:unchecked", "-Xlint:deprecation")

val AkkaVersion = "2.6.5"
val AkkaPersistenceCassandraVersion = "1.0.0"


libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-cluster-sharding-typed" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-typed" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-query" % AkkaVersion,
  "com.typesafe.akka" %% "akka-serialization-jackson" % AkkaVersion,
  "com.typesafe.akka" %% "akka-persistence-cassandra" % AkkaPersistenceCassandraVersion,
  "com.typesafe.akka" %% "akka-slf4j" % AkkaVersion,
  "ch.qos.logback"     % "logback-classic"             % "1.2.3"
)

 

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

【其他文章推薦】

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

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

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

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

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

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