15萬買合資SUV必看!這款車居然是歐洲最暢銷車型之一!_貨運

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

而且作為一款SUV它的離地間隙達到了將近20cm這也保證了它具備一定實力的通過性,可以適合歐洲主要以丘陵為主的道路,以及中國多路況的局面,這也是為什麼逍客的銷量在中國銷量也不差的原因。逍客配備的是一款2。0L自然吸氣發動機,最大馬力達到150ps,以及一台模擬七速的CVT變速箱,動力總成的賬面參數並不突出,但是對於家用來說,動力輸出已經完全可以滿足日常的用車需求。



歐洲,世界汽車工業誕生的搖籃,孕育出了寶馬、奔馳、大眾等等世界知名的汽車品牌,但是來自日產的逍客,居然成為了歐洲最暢銷的SUV車型之一,而且逍客在國內的銷量也不低,平均月銷量都過萬台,這車究竟為什麼會如此受歡迎?

外觀:其貌不揚卻迎合觀眾

熟悉歐洲車的朋友都知道,除了法系車以外,以德系車為代表的汽車外觀設計都是普遍偏向保守,這其實也跟歐洲人普遍的審美還是偏向保守嚴謹有關。

逍客的外觀其實沒有什麼突出的亮點,日產家族式的前臉,用整體性更強的幾何板塊營造出一個不過不失,中庸圓潤的車輛外形,這也十分符合一台家用SUV應有的定位。

逍客的車主年齡層次涵蓋十分廣泛,從65后的中年人到85后的年輕人,購買逍客的不在少數,正式這種其貌不揚但是老少通殺的外觀設計才能做到真正迎合消費者的普遍審美。

操控:駕控靈活,可適應路況眾多

作為一台緊湊型SUV,逍客的軸距並不算短,達到了2645mm,但是車身的前後懸較短,營造出了一個較為短小精悍的外觀,

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

而且由於逍客的操控特性建立得十分靈敏,導致逍客的操控感受十分靈敏輕便。談不上什麼運動感和駕駛樂趣,但是逍客卻可以成為一家人所有會開車的人士都比較好上手的一台SUV。

而且作為一款SUV它的離地間隙達到了將近20cm這也保證了它具備一定實力的通過性,可以適合歐洲主要以丘陵為主的道路,以及中國多路況的局面,這也是為什麼逍客的銷量在中國銷量也不差的原因。

逍客配備的是一款2.0L自然吸氣發動機,最大馬力達到150ps,以及一台模擬七速的CVT變速箱,動力總成的賬面參數並不突出,但是對於家用來說,動力輸出已經完全可以滿足日常的用車需求。

內飾:樸實無華,舒適至上

日產的汽車在內飾層面不會體現過多的設計感,走量的車型普遍都是以很樸素的樣式進行設計,中控功能區的按鍵布局合理簡潔,內飾的溝通感做得不錯,但是方向盤尺寸偏大而且握感很細,在駕駛的時候或許需要駕駛員做更多的適應。

逍客的乘坐空間布局不算很大,後排空間顯得比較局促,但是勝在座椅的貼合程度很高,所以乘坐起來很舒適,各種布局都體現了逍客是一款適合家用的SUV。

全文總結:什麼車是一台好車,相信不少人眼裡都會有不同的答案。有些人注重的是品牌情懷,有人注重的是各種突出的性能,而更多人可能選擇是一款足夠實用,簡單,而且不過不失的車型,SUV作為國人家庭接受度程度比較高的車型,日產的逍客或許沒有什麼吸引的品牌效應,甚至有不少排斥日系的朋友會對其不屑一顧,但就事論事的說,逍客作為15萬級的合資家用SUV來說,可以作為一款不錯的選擇。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

能杜絕95%的變道追尾事故!這幾款9.98萬起的車型神了!_網頁設計公司

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

5T 手動尊貴型配備了併線輔助系統,對於如此碩大的SUV來說,盲區更加值得關注,加上DX7的車窗設計得不算大,A柱也不小,因此開DX7真的要多注意注意,這個帶併線輔助系統的東南DX7 2017款 1。5T 手動尊貴型售價12。99萬,使用1。

日產軒逸

指導價:9.98-15.90萬

軒逸是我們熟悉的國民家轎之一,每個月的銷量都十分高,省油安靜平順是我們對軒逸的最大印象,當然軒逸的尺寸也不小,4631*1760*1503mm的車身尺寸,2700mm的軸距都意味着這是一台大緊湊型轎車,因此它的車內空間表現也十分不錯,不過1.6L的發動機的版本動力不怎麼強,這也是它的短板所在。

軒逸 2016款 1.6XV CVT智尊版上就裝備了併線輔助系統,而且不止併線輔助,這款指導價14.68萬的1.6XV CVT智尊版還配備了車道偏離預警系統和主動剎車系統,因此這款車的安全性裝備也是十分豐富,LED近光燈、ESp等裝備也有,配置上這款車型十分豐富。

東南DX7

指導價:9.69-13.99萬

東南DX7算是大家比較熟悉的SUV了,外觀設計是DX7最大的亮點,

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

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

意大利設計大師操刀設計的造型看起來十分有氣場,加上4530*1900*1700mm的車身尺寸,2700mm的軸距營造出來的大車感十分充足,喜歡開大車的朋友一定不能錯過這車。

東南DX7 2017款 1.5T 手動尊貴型配備了併線輔助系統,對於如此碩大的SUV來說,盲區更加值得關注,加上DX7的車窗設計得不算大,A柱也不小,因此開DX7真的要多注意注意,這個帶併線輔助系統的東南DX7 2017款 1.5T 手動尊貴型售價12.99萬,使用1.5T發動機(156馬力/215牛米)搭配6擋手動變速箱,性價比還是很不錯的。分區空調/感應雨刷/GpS系統/車內氛圍燈/電動主駕駛座椅/前後雷達/全景影像也都有配備。

標緻308

指導價:9.97-15.97萬

如果說老款標緻308長得很帥的話,我想沒幾個人會贊同,但我說這一代308很帥的話,我想沒幾個人會反對,的確這代標緻308的造型相比老款發生了翻天覆地的變化,4590*1820*1488mm的車身尺寸不算小,尤其是1820mm的寬度使得整車看起來更加寬大,頗有幾分寬體轎跑既視感。

標緻308 2016款 1.6T 自動尊貴版指導價15.97萬,這款車採用了1.6T發動機加6擋手自一體變速箱的動力組合,這款車配備了併線輔助系統,搭配上法系車紮實的底盤,1.6T發動機動力強勁,因此標緻308十分適合跑高速。

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

海峽兩岸學者講述家文化_網頁設計公司

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

《回家》 薛仁明 白劍峰著 紅旗出版社

近日,《回家》一書由紅旗出版社出版,作者為台灣作家薛仁明和人民日報高級記者白劍峰。這是海峽兩岸學者首次聯袂出版的“家文化”專著。本書從中華傳統文化的視角解讀了“回家”的內涵和意義。《回家》以家庭為切入點,從飲食男女到四時祭祀,從鄉愁鄉思到祠堂家譜,從居住空間到民俗風情,從家風家教到家國情懷,涵蓋了家庭的方方面面,將中華傳統文化融入日常生活的細節之中,剖析了中國人的文化基因,闡述了中國人的家國情懷。本書分為家居、家祭、家鄉、家庭、家風、家教、家國七章,站在中華幾千年文明的高度,涉及儒釋道及諸子百家,體現了儒家“修身、齊家、治國、平天下”的人生理想。書中有美食的味道,有節氣的韻律,有建築的光影,有祭祀的虔敬,有生死的感悟,有血脈的綿續,有鄉愁的記憶,有夫妻的恩愛,有教子的良方,既是一本中華傳統文化的“濃縮讀本”,也是一本家庭生活的“百科全書”。

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

2020年全國鄉村春晚聯動“百縣萬村”_台中搬家公司

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

2019年12月26日,2020年全國鄉村春晚集中展示活動暨麗水鄉村春晚建設成果新聞發布會在北京召開。活動由文化和旅遊部公共服務司、全國公共文化發展中心、浙江省文化和旅遊廳和浙江省麗水市政府主辦。2020年全國鄉村春晚集中展示活動啟動儀式將於2020年1月8日在浙江麗水舉辦。屆時,以麗水為主會場,安徽省、四川省、廣東省、河南省等8省區同步啟動鄉村春晚活動。

此次活動將通過線下鄉村春晚年俗展示、線上互聯網科技互動形式,拉開2020年全國鄉村春晚“百縣萬村”區域聯動大幕。此外,全國鄉村“鬧”春晚展演展示活動也將在全國篩選最具區域特色的節目,到麗水主會場以斗藝和吆喝的形式,集中展示全國鄉村藝術普及成果。

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

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

在麗水,活動主要以鄉村春晚品牌建設為主導,同步舉辦“麗水味道”年味集市、“麗水山耕”年貨集市、“僑鄉中國年”等十大特色活動,形成文、農、旅大融合,通過網絡直播、主會場與分會場聯動、線上線下互動,全面展現社會發展成就。

據介紹,鄉村春晚起源於1981年由麗水市慶元縣舉水鄉月山村农民自編、自導、自演的“月山春晚”,比央視春晚還早兩年,至今已有30多年的歷史。經過幾十年的發展,麗水鄉村春晚點面結合、遍地開花,成為當地重要的公共文化品牌。(記者 杜潔芳)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

讀懂時代,從讀懂語言開始_網頁設計公司

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

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

時節如流,歲月不居。年終歲末,不同機構相繼發布年度漢字、漢語盤點,國家語言資源監測與研究中心發布了中國媒體十大流行語:我和我的祖國、金色十年、學習強國、中美經貿磋商、最美奮鬥者、硬核、垃圾分類、先行示範區、基層減負年、我太難了。《咬文嚼字》編輯部、《語言文字周報》等機構公布的年度流行語與之有重合也有不同。

年度流行語盤點,既是語言文字研究領域的一件盛事,也是整個文化界的一樁“雅事”,而且被大眾看成是觀察中國社會、洞悉世道人心的一扇窗口。因此,年復一年,如期而至,廣受關注。今年的年度流行語形態各異,既有“我和我的祖國”“最美奮鬥者”“硬核”等與時代大局大勢同頻共振的“鐘鼓之音”,也有諸如“我太難了”“好嗨呦”之類,反映社會風尚、大眾心態、百姓心聲的“網言網語”。“大珠小珠落玉盤”,盡顯漢語言文字的豐富性。

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

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

過去的2019年,我們為共和國70年的輝煌成就喝彩,愛國主義情感讓我們熱淚盈眶,“我和我的祖國”在大江南北唱響。我們感動於千千萬萬奮鬥者“擼起袖子加油干”的蓬勃朝氣,也為基層幹部切實減輕了負擔鬆了一口氣;我們為偉大祖國的欣欣向榮而驕傲,也每每因工作的壓力、生活的不易感到焦慮……不論社會如何變化,總有一些與每個人息息相關的國家大事、社會大潮引領着我們的注意力,總有一些標誌性的人和事讓我們產生情感的交集;總有一些共同的情感把我們的心凝聚在一起。也因此,在人口基數如此龐大的中國,每年總有一些被普遍認同的年度流行語。

過去的2019年,我們的社交方式、信息獲取方式乃至生活方式,都在發生更深刻改變。人們在為奮鬥者喝彩的同時,也對所謂的“996工作制”產生質疑;既熱衷於以“X千萬條,Y第一條”造句,也在類似“我不要你覺得,我要我覺得”的表達中展現着自我意識的增強、個體個性的鮮明;盤他、雨女無瓜、檸檬精、斷舍離等等流行語,讓很多人感到摸不着頭腦,一些人卻高度認同、非常默契,這提示我們,分眾化的社交趨勢更加明顯,不同社交圈的人往往有着全然不同的文化生態和話語體系。如果把這種巨大的差異性放在70年歲月變遷的歷史長河中去觀察,我們就會看到,這種千差萬別、百花齊放、形態各異,展現的正是生活選擇的自由、社會文化的多元、人的個性的舒展。“和而不同,各美其美”是社會繁榮進步的體現。

語言是時代的風向標,讀懂時代,當從讀懂語言開始。十多年來,每至歲末,一串串閃現在語言文字大潮中的流行語被“打撈”出來。這種“打撈”,實際上是對國家大勢、世界風雲、民生實事、社會熱點的“打撈”,是對時代變化、社會變遷的“打撈”。一個個時代流行語鋪展在面前,嘴角輕揚,看似波瀾不驚地咂摸間,往往蘊含着人們對自身生活、社會變革與人類發展的深長思考,往往寄予着擁抱更美好生活、更美好時代的蓬勃進取心。“只爭朝夕,不負韶華”,新年已至,讓我們整裝出發。(作者:李思輝,系華中科技大學新聞評論研究中心特聘研究員)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

恢復古城風貌 傳承傳統文化_租車

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

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

本報石家莊1月8日電  (記者張志鋒)“登得上城樓,望得見古塔,記得住鄉愁。”新年伊始,河北正定東城門、府文廟、府城隍廟和正定博物館正式開放。歷時三年,正定縣24項古城風貌恢復提升工程全部完成。據了解,2019年正定縣旅遊接待預計突破1471萬人次,同比增長逾16.7%。

正定是國家歷史文化名城,有1600多年的建城史,縣城有“九樓四塔八大寺,二十四座金牌坊”,全縣有全國重點文物保護單位9處、河北省文物保護單位6處,各類館藏文物7672件。2017年1月,當地啟動24項古城風貌恢復提升工程。

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

提升工程主要包括:修復加固南城門系統和南部城牆5800米;在北城門建成遺址公園,修建東城門主城及瓮城,恢復護城河;原址復建“鎮府巨觀”陽和樓,展示正定傳統文化等。

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

專家學者聚焦古民居保護傳承:像對老人一樣善待古民居_包裝設計

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

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

城市化進程加快,使得自然村落加速消散。如何保護傳統古民居,促進建築遺產良性有序傳承,成為文化收藏界深思的話題。

在彙集了眾多徽派古民居的聞道園,業界專家、收藏者11日舉行了一個小型沙龍,共同聚焦古民居的文化傳承及活態保護。

同濟大學國家歷史文化名城研究中心特約研究員居平表示,古民居是中國人的傳統住宅,體現了獨特的中華文化,其中蘊含的文化藝術可謂國粹。古民居體現了中國人的生活方式、人際交往,以獨特的方式呈現中國之美。由此,留住古民居也是留住了“中國文化的根”。

知名建築保護專家阮儀三在接受採訪時認為,注重真實性、整體性、可讀性、永續性、獨特性是老房子收藏保護的最高境界。他亦坦言,實在非拆不可,只能異地保護,花功夫拆,給每一片瓦、每一塊磚都編上號。收藏學專家祝君波也認為,異地收藏和保護並非上策,但比之於“毀”,是一種妥協的辦法。

坐落在上海寶山羅店鎮的聞道園,規劃面積600餘畝,被業界譽為“古建築異地保護的典範”。沙龍採訪之餘,記者探訪了這座洋溢着古典之美的園林。行走其間,記者看到,園內錯落有致地佇立了數十座古色古香的徽州老宅、古橋、古亭、戲台……庭院里、房間里懸挂、擺放着匾額、傢具等“老物件”。清代咸豐九年的永濟橋、道光八年的狀元樓、乾隆年間的雕花樓……在這裏靜靜地佇立着,散發著厚重的歷史氣息,青石砌成的門牆洋溢着濃濃的古意,石磨盤連成的小路蜿蜒神相遠處,置身寧靜古樸的飛檐古宅群之間,在垂柳、碧波映襯下,彷彿時空倒轉,現代人與崇尚自然的古人有了神交。

聞道園創始人王衛告訴記者,目前園中仍有包括明代大夫第、明代金絲楠木翰林院、清代大祠堂等在內的百餘棟古民居原材料還靜待搭建。

談及收藏這些古民居的初衷,王衛十分感慨:收藏古宅的初衷不僅源於對徽式古建築的喜愛,更出於對老宅飄零凋敝現狀的痛心。20多年前,他與朋友在安徽鄉間遊歷,黛瓦、粉壁、馬頭牆的徽派古民居建築之美深深吸引着他們。承載着數百年歷史滄桑的飛檐、雕花韻味雋永,讓人回味無窮。可是當時在安徽的一些農村,修復一幢稍有規模的古宅所需費用,憑當地人的年收入,根本無力承受。日久天長,一些古宅或受潮霉爛,或坍塌廢棄。不少精美木雕被住戶丟棄。

王衛說,眼睜睜看着不少古宅被用作豬圈、養蠶場,心中不僅是痛楚,還有對傳統文化失落的擔憂。於是,他將早年做生意所得積蓄投入古民居的收藏。為了盡可能多地保留下這些古民居,王衛曾一度賣掉房產,來收藏別人眼中的“爛木頭”。

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

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

王衛告訴記者,很多古民居在被收藏的時候已然破敗不堪,甚至已經倒塌。不少古民居或被白蟻蛀蝕、或被風雨侵蝕,毀損嚴重,脆弱飄搖地存在於世間,如同風燭殘年的老者。有些古民居只剩下主要構件。在王衛看來,每一棟古民居都是不可再生的財富,不僅記載着歷史變遷,更飽含文化傳承的價值。王衛將這些當地人眼中的“廢物”,當做寶貝,一件不落地運回上海。或者按原貌維修重建,或者進行防腐、防蛀處理后暫時保存起來,等待日後逐一恢復原貌。

在收集古民居的20餘年裡,一百多棟古民居建材被王衛用卡車分300多次運到上海。王衛說,遇到特別粗大的冬瓜梁,因為鄉村道路設施限制,無法用吊機搬動,所以只能靠人力靠人力搬運。一根冬瓜梁常需要20多個壯漢共同搬動。

據了解,一些古民居的部件,因為年代久遠,經多年風霜侵蝕,無法再使用,所以在建設過程中,王衛和工作人員按照原部件的材質和形態,四處尋覓最為相似的部件。他說,這是一個艱辛的而糾結的過程。

如今,20多棟徽派古民居已經建好,成為上海“水泥鋼筋”森林中,別具特色的徽派古民居觀賞地。王衛賦予每一棟重煥新生的古民居不同的“職責”。有的成為傳播傳統文化的文創中心,有的作為人們修心的禪修堂,還有的變身書院、畫院和大師工作室。聞道園也成為書畫家、文人墨客青睞的創作、展示作品場所。王衛說,重生的古民居只有在現代人的使用中才能得到最好的保護。

談及未來,王衛期待一棟棟古民居逐漸恢復修建,古建築藝術重煥光彩。他希望這些古民居成為傳統文化的彰顯之地。

不過,如今,待建的百餘棟古民居原材料暴露在自然環境中,這令王衛十分着急。古民居全部重建需要時間,而原本就已經十分破敗凋敝的老木料雖經修復,但已經不起風霜烈日的摧殘。王衛說,如果在自然環境中再次霉爛,這些古民居終將難逃毀滅的厄運。

作為文化研究學者,居平坦言,在現實中,古民居的保護與當地居民實際需求,以及與相關機構、個人利益產生衝突。這對古民居保護確實不利。她呼籲,要像善待老人一樣善待、愛護古民居。

談及還未恢復建造的古民居原材料,阮儀三指出,應給這些寶貴的原材料提供相對穩定的存儲環境。對古民居的保護不僅要動用高科技手段,還要進行活態保護。(陳靜)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

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

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

甘肅將推動長城國家文化公園建設_台中搬家

※台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

記者11日從甘肅省文物局獲悉,中國長城資源大省甘肅今年將推動長城國家文化公園建設,提升全省長城保護利用整體水平。同時啟動河西走廊國家遺產線路保護利用行動計劃,帶動文物保護利用區域協同發展。

甘肅境內長城修建始於戰國秦,其後歷代多有修築,現存遺迹主要是戰國秦、漢、明三代,三代長城的西端起點均在甘肅境內。全省長城共分佈在11個市州,總長度為3654千米,居全國第二。明長城1738千米,長度為全國之首。

根據甘肅長城國家文化公園建設規劃,結合甘肅長城資源稟賦和基礎條件,初步規劃“338”的總體布局,即:建設敦煌漢長城、嘉峪關明長城、臨洮戰國秦長城3個綜合示範區,漢長城、明長城、戰國秦長城3個集中展示帶,臨澤、永昌、民勤、古浪、天祝、景泰、環縣、華池8個重點展示區段。

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

甘肅省文物局黨組書記、局長馬玉萍表示,有關市縣要有計劃地加強全省長城保護利用,使更多的長城文物達到開放服務的標準,使長城大省真正能夠讓世界看得見、叫得響。要重視加強日常管護,全面排查長城本體安全隱患,實施搶救性保護工程。同時有計劃地解決長城保護範圍內的違建、耕種等問題,全面改善長城環境風貌。

河西走廊在中華文明史、中外交流史上佔有特殊而重要的價值地位,“穿越千年、歷久彌新”。2020年,甘肅還將實施好河西走廊國家遺產線路保護利用行動計劃,帶動文物保護利用區域協同發展。

馬玉萍介紹說,甘肅省文物部門認真梳理河西走廊文化要素,提出全國首個國家遺產線路項目,並編製了保護利用行動計劃,涉及酒泉、嘉峪關、張掖、金昌、武威、白銀6個市,通過探索實踐,為全國創建國家遺產線路保護利用示範體系,該項目已被甘肅省政府和國家文物局攜手計劃於今年啟動實施。(馮志軍)

本站聲明:網站內容來http://www.societynews.cn/html/wh/fq/,如有侵權,請聯繫我們,我們將及時處理

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

曹工說mini-dubbo(2)–分析eureka client源碼,想辦法把我們的服務提供者註冊到eureka server(上)_網頁設計

※台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

前言

eureka是spring cloud Netflix技術體系中的重要組件,主要完成服務註冊和發現的功能;那現在有個問題,我們自己寫的rpc服務,如果為了保證足夠的開放性和功能完善性,那肯定要支持各種註冊中心。目前我們只支持redis註冊中心,即服務提供者,在啟動的時候,將自身的ip+端口信息寫入到redis,那,我們是否註冊到 eureka中呢?

這個想法可行嗎?可行。eureka client 和eureka server間,無非是網絡通信,既然是網絡通信,那就有網絡協議,那我們的應用,只要遵照eureka server的協議來,就可以接入。

另外,eureka server沒有採用spring mvc來實現,而是採用了jersey框架,這個框架啥意思呢,可以理解為對Restful的實現。我從網上找了一段(https://www.jianshu.com/p/88f97b90963c):

SpringMVC在開發REST應用時,是不支持JSR311/JSR339標準的。如果想要按照標準行事,最常用的實現了這兩個標準的框架就是Jersey和CxF了。但是,因為Jersey是最早的實現,也是JSR311參考的主要對象,所以,可以說Jersey就是事實上的標準(類似Hibernate是JPA的事實上的標準),也是現在使用最為廣泛的REST開發框架之一。

因為eureka server採用了jersey,所以eureka client最終也是使用了配套的jersey client來和服務端通信。

所以,eureka client,裏面其實依賴了一堆jersey的包:

注意,上面的jersey-client、jersey-core等包,其group id都是這樣的:

    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-client</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

但是,不知道為啥,eureka client中,最終並沒有完全使用jersey-client,而是使用了

    <dependency>
      <groupId>com.sun.jersey.contribs</groupId>
      <artifactId>jersey-apache-client4</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

這個包,內部引入了:

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
        </dependency>

這個包,你可以簡單理解為,jersey-client變成了一個接口,jersey-apache-client4是它的一個實現,實現里,用了httpClient去實現。

httpClient,沒有幾個java同學不知道吧?這麼做是可行的,因為你最終通信,還是http,不管你服務端框架,是jersey、還是spring mvc、甚至以前的struts,這都不重要。

所以,大家在下面的源碼中看到jersey的時候,腦海里可以有這麼一張圖。從上層到底層的接口,分別是:

CloudEurekaClient
       ...
DiscoveryClient  
	...
EurekaClient
	...
JerseyClient
	...
HttpClient	

在此之前,我們還是先分析下eureka client 註冊到eureka server的源碼。

源碼環境

minidubbo代碼和相關博文在:
曹工說mini-dubbo(1)–為了實踐動態代理,我寫了個簡單的rpc框架
https://gitee.com/ckl111/mini-dubbo

代碼很簡單,不過還是給個代碼鏈接吧:

https://gitee.com/ckl111/all-simple-demo-in-work-1/tree/master/eureka-client

主要就是在pom.xml中,引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然後啟動類:

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

源碼分析

spring.factory支持自動配置

因為前面的pom,引入了如下jar包:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-eureka-client</artifactId>
		</dependency>

該jar包的META-INF\spring.factories中,有如下幾行:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

我們看到,key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是逗號分割的列表,這裏面都是需要被自動裝配的配置類,其中,我們看第三行的:

org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration。

這個類,是自動裝配的配置類,我們可以簡單一覽:

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
		CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
public class EurekaClientAutoConfiguration {

裏面一堆@ConditionalOn***,主要是看該配置類是否生效。

我們不管,這裏條件是滿足的,所以,看具體java文件里有什麼要裝配的內容,裏面內容較多,我們關注我們需要關注的:

		@Bean(destroyMethod = "shutdown")
		@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
		@Lazy
		public EurekaClient eurekaClient(ApplicationInfoManager manager,
				EurekaClientConfig config, EurekaInstanceConfig instance,
				@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
			ApplicationInfoManager appManager;
			if (AopUtils.isAopProxy(manager)) {
				appManager = ProxyUtils.getTargetObject(manager);
			}
			else {
				appManager = manager;
			}
            // 1
			CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
					config, this.optionalArgs, this.context);
			cloudEurekaClient.registerHealthCheck(healthCheckHandler);
			return cloudEurekaClient;
		}

這裡會自動裝配一個EurekaClient類型的bean,(從返回值可以看出來),而具體的類型呢,從上面的1處,可以看出,具體類型是CloudEurekaClient。

所以,我們開始看1處,這個CloudEurekaClient是怎麼new出來的。

CloudEurekaClient的創建

先看看其繼承結構:

我們這個CloudEurekaClient,位於spring-cloud-netflix-eureka-client-2.1.5.RELEASE包。

而其父類DiscoveryClient和接口EurekaClient,位於eureka-client-1.9.13包

大致能分析出,CloudEurekaClient的底層實現是eureka,其本身,是一個膠水,集成 spring 和 Netflix。

CloudEurekaClient的構造函數

	public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
			EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args,
			ApplicationEventPublisher publisher) {
        // 1
		super(applicationInfoManager, config, args);
        // 2
		this.applicationInfoManager = applicationInfoManager;
		this.publisher = publisher;
		this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,
				"eurekaTransport");
		ReflectionUtils.makeAccessible(this.eurekaTransportField);
	}

我們看1處,調用了父類的構造函數;2處下面的幾行,主要是對本類中的幾個field進行賦值,這幾個字段,我們不關心,所以,直接看父類的構造函數吧。

DiscoveryClient的構造函數

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
        this(applicationInfoManager, config, args, ResolverUtils::randomize);
    }

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, EndpointRandomizer randomizer) {
        // 1
        this(applicationInfoManager, config, args, null, randomizer);
    }

上面兩個,都是重載。1處調用的,我們接下來會重點分析。

步驟1:一堆field賦值

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                    Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
    	// 0	
        if (args != null) {
            this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
            this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
            this.eventListeners.addAll(args.getEventListeners());
            this.preRegistrationHandler = args.preRegistrationHandler;
        } else {
            this.healthCheckCallbackProvider = null;
            this.healthCheckHandlerProvider = null;
            this.preRegistrationHandler = null;
        }
        
        this.applicationInfoManager = applicationInfoManager;
        InstanceInfo myInfo = applicationInfoManager.getInfo();
		// 1
        clientConfig = config;
        staticClientConfig = clientConfig;
        transportConfig = config.getTransportConfig();
        instanceInfo = myInfo;
        if (myInfo != null) {
            appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
        } else {
            logger.warn("Setting instanceInfo to a passed in null value");
        }
		...

這一堆都是根據入數,來對類中的field進行賦值。比如

0處,主要是一些健康檢查的東西;1處,config類型為 com.netflix.discovery.EurekaClientConfig,這裏主要是eureka client的一些配置,比如我們在yml中配置了eureka.client.*之類的,就會到這裏。

步驟2:判斷是否要獲取eureka server中的服務提供者信息

	// 1
	if (config.shouldFetchRegistry()) {
            this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

1處,可以看出來,是根據config中的shouldFetchRegistry進行判斷,是否要去獲取eureka server。

然後進行了一些監控指標的初始化。

步驟3:判斷是否要註冊到eureka server

    	// 1    
		if (config.shouldRegisterWithEureka()) {
            this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

同上。

步驟4:如果既不註冊,也不獲取,則處理基本結束

		// 1
		if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
            logger.info("Client configured to neither register nor query for data.");
            scheduler = null;
            heartbeatExecutor = null;
            cacheRefreshExecutor = null;
            eurekaTransport = null;
            instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion());

            DiscoveryManager.getInstance().setDiscoveryClient(this);
            DiscoveryManager.getInstance().setEurekaClientConfig(config);
			// 2
            return;  // no need to setup up an network tasks and we are done
        }
  • 1處,既不註冊,也不從eureka server獲取
  • 2處,直接結束

步驟5:定義三個線程池

            //1 default size of 2 - 1 each for heartbeat and cacheRefresh
            scheduler = Executors.newScheduledThreadPool(2,
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-%d")
                            .setDaemon(true)
                            .build());
			// 2
            heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
			// 3 
			cacheRefreshExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
  • 1處,定義一個用於服務提供者信息的緩存刷新的定時線程池
  • 2處,定義一個心跳線程池
  • 3處,這個看起來也是用於緩存刷新的

步驟6:創建eurekaTransport對象

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
eurekaTransport = new EurekaTransport();
// 2
scheduleServerEndpointTask(eurekaTransport, args);
  • 1處,eurekaTransport是一個field,該類主要封裝了幾個後續通信要使用的底層client。

    
        private static final class EurekaTransport {
            private ClosableResolver bootstrapResolver;
            private TransportClientFactory transportClientFactory;
    		// 1.1
            private EurekaHttpClient registrationClient;
            private EurekaHttpClientFactory registrationClientFactory;
    		// 1.2
            private EurekaHttpClient queryClient;
            private EurekaHttpClientFactory queryClientFactory;
    

    1.1處,這個應該是註冊用的,也是我們需要的;

    1.2處,應該是查詢信息用的。

  • 調用了當前類的方法scheduleServerEndpointTask,且把eurekaTransport傳入了

步驟7:schedule周期任務

創建抽象工廠

因我們只是new了eurekaTransport,沒有對其field進行任何賦值,所以,這個scheduleServerEndpointTask總,有個地方對其field進行賦值。

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
TransportClientFactories transportClientFactories =new Jersey1TransportClientFactories();

// 2
eurekaTransport.transportClientFactory = transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo(), sslContext, hostnameVerifier)

  • 1處,就是new了一個抽象工廠,抽象工廠,我個人理解是工廠的工廠,其產出的東西,不是直接的最終對象,而是另一種工廠。

    TransportClientFactories 是一個接口,主要包含了如下方法:

    ※推薦評價好的iphone維修中心

    擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

    	public TransportClientFactory newTransportClientFactory(
            	final EurekaClientConfig clientConfig,
            	final Collection<F> additionalFilters,
                final InstanceInfo myInstanceInfo,
                final Optional<SSLContext> sslContext,
                final Optional<HostnameVerifier> hostnameVerifier);
    

    主要5個參數,排除掉最後的倒數2個,可選參數,剩3個。分別是:eurekaClient的配置bean,額外的filter集合,當前實例信息。

具體工廠的職責

  • 2處,就是利用1處創建的抽象工廠,來生成我們需要的工廠。

    這裏,我們可以先看看,最終我們需要的工廠,是什麼樣的。

    /**
     * A low level client factory interface. Not advised to be used by top level consumers.
     *
     * @author David Liu
     */
    public interface TransportClientFactory {
    
        EurekaHttpClient newClient(EurekaEndpoint serviceUrl);
    
        void shutdown();
    
    }
    

    newClient這個方法,聽名字,就是一個創建客戶端的,創建客戶端,需要什麼參數呢?總得知道要連接到哪個eureka server服務器吧,服務器地址是啥吧?沒錯,參數EurekaEndpoint serviceUrl可以給我們提供需要的這些:

    package com.netflix.discovery.shared.resolver;
    
    public interface EurekaEndpoint extends Comparable<Object> {
    	// 1
        String getServiceUrl();
    	// 2
        String getNetworkAddress();
    	// 3
        int getPort();
    
        boolean isSecure();
    
        String getRelativeUri();
    
    }
    
    
    • 1處,獲取url
    • 2處,獲取網絡地址
    • 3處,獲取端口

    基本對於我們一個客戶端來說,需要的參數就這些。

    說完了newClient的參數,再來看看響應:

    
    /**
     * Low level Eureka HTTP client API.
     *
     * @author Tomasz Bak
     */
    public interface EurekaHttpClient {
    
        EurekaHttpResponse<Void> register(InstanceInfo info);
    
        EurekaHttpResponse<Void> cancel(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus);
    
        EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info);
    
        EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info);
    
        EurekaHttpResponse<Applications> getApplications(String... regions);
    
        EurekaHttpResponse<Applications> getDelta(String... regions);
    
        EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String id);
    
        void shutdown();
    }
    

    看到了嗎,各種註冊、取消、發送心跳、狀態更新啥的,這幾本涵蓋了eureka client的所有操作了,沒錯,我們就是需要這麼個東西。

創建具體工廠

看完了我們需要的工廠的功能,我們馬上來看看這麼厲害的工廠怎麼創建出來?

com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories#newTransportClientFactory(...)
        
	@Override
    public TransportClientFactory newTransportClientFactory(
        	EurekaClientConfig clientConfig,
            Collection<ClientFilter> additionalFilters,
        	InstanceInfo myInstanceInfo,
        	Optional<SSLContext> sslContext,
        	Optional<HostnameVerifier> hostnameVerifier) {
    	// 2.1
        final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
                clientConfig,
                additionalFilters,
                myInstanceInfo,
                new EurekaClientIdentity(myInstanceInfo.getIPAddr()),
                sslContext,
                hostnameVerifier
        );
        // 2.2
        final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);
		// 2.3
        return new TransportClientFactory() {
            @Override
            public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
                return metricsFactory.newClient(serviceUrl);
            }

            @Override
            public void shutdown() {
                metricsFactory.shutdown();
                jerseyFactory.shutdown();
            }
        };
    }
  • 2.1處,調用JerseyEurekaHttpClientFactory的create 靜態方法,生成了一個工廠
  • 2.2處,對生成的工廠,進行了包裝,看名稱,應該是包裝了統計相關信息。
  • 2.3處,對2.2處生成的工廠,用匿名內部類進行了包裝,調用匿名內部類的newClient時,直接代理給了metricsFactory;而shutdown方法,則主要是關閉 metricsFactory 和 jerseyFactory 工廠。

所以,我們現在要看看,2.1處,是怎麼創建工廠的。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory#create
    
    public static JerseyEurekaHttpClientFactory create(
    	EurekaClientConfig clientConfig,
        Collection<ClientFilter> additionalFilters,
    	InstanceInfo myInstanceInfo,                                                       		 AbstractEurekaIdentity clientIdentity) {
        // 1
    	boolean useExperimental = "true".equals(clientConfig.getExperimental("JerseyEurekaHttpClientFactory.useNewBuilder"));
		// 2
        JerseyEurekaHttpClientFactoryBuilder clientBuilder = (useExperimental ? experimentalBuilder() : newBuilder())
                .withAdditionalFilters(additionalFilters)
                .withMyInstanceInfo(myInstanceInfo)
                .withUserAgent("Java-EurekaClient")
                .withClientConfig(clientConfig)
                .withClientIdentity(clientIdentity);
    	// 3
        clientBuilder.withClientName("DiscoveryClient-HTTPClient");
		// 4
        return clientBuilder.build();
    }
  • 1處,砍斷是否要使用實驗性的builder
  • 2處,創建對應的builder,並把我們的參數,通過with*方法,設置進去
  • 3處,設置客戶端名稱
  • 4處,生成客戶端工廠
com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#build

    
        @Override
        public JerseyEurekaHttpClientFactory build() {
    		// 1
            Map<String, String> additionalHeaders = new HashMap<>();
    		// 2
            if (allowRedirect) {
                additionalHeaders.put(HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true");
            }
            if (EurekaAccept.compact == eurekaAccept) {
                additionalHeaders.put(EurekaAccept.HTTP_X_EUREKA_ACCEPT, eurekaAccept.name());
            }
			
            // 3
            return buildLegacy(additionalHeaders, systemSSL);
        }

這裏就是弄了個hashmap,設置了幾個header進去,然後3處,調用buildLegacy。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#buildLegacy
    
        private JerseyEurekaHttpClientFactory buildLegacy(Map<String, String> additionalHeaders, boolean systemSSL) {
    		// 1
            EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientBuilder()
                    .withClientName(clientName)
                    .withUserAgent("Java-EurekaClient")
                    .withConnectionTimeout(connectionTimeout)
                    .withReadTimeout(readTimeout)
                    .withMaxConnectionsPerHost(maxConnectionsPerHost)
                    .withMaxTotalConnections(maxTotalConnections)
                    .withConnectionIdleTimeout((int) connectionIdleTimeout)
                    .withEncoderWrapper(encoderWrapper)
                    .withDecoderWrapper(decoderWrapper);
			...
            
			// 2
            EurekaJerseyClient jerseyClient = clientBuilder.build();
    		// 3
            ApacheHttpClient4 discoveryApacheClient = jerseyClient.getClient();
            addFilters(discoveryApacheClient);
			// 4
            return new JerseyEurekaHttpClientFactory(jerseyClient, additionalHeaders);
        }
  • 1處,通過我們傳入的一些參數,以及該類自身的一些field,比如connectionTimeout、readTimeout、maxTotalConnections、maxConnectionsPerHost這些,構造一個builder。

    這些參數,已經看出來,是網絡通信所需要的東西了

  • 2處,通過1處的builder,調用build,拿到了EurekaJerseyClient類型的對象,可以說,這裏其實是已經把客戶端構造好了。也就是說,在構造這個工廠的過程中,其實已經在生成對應的產品了

  • 3處,對2處拿到的客戶端,做一些處理

  • 4處,將2處拿到的客戶端,封裝到了工廠的一些field中,後續調用工廠生產產品的時候,直接從field中取就行了。

        public JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient, Map<String, String> additionalHeaders) {
            this(jerseyClient, null, -1, additionalHeaders);
        }
    	private JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient,
                                              ApacheHttpClient4 apacheClient,
                                              long connectionIdleTimeout,
                                              Map<String, String> additionalHeaders) {
            this.jerseyClient = jerseyClient;
            this.apacheClient = jerseyClient != null ? jerseyClient.getClient() : apacheClient;
            this.additionalHeaders = additionalHeaders;
        }
    

所以,我們的重點,要放在2處的build身上。

	com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl.EurekaJerseyClientBuilder#build
	public EurekaJerseyClient build() {
            MyDefaultApacheHttpClient4Config config = new MyDefaultApacheHttpClient4Config();
            try {
                // 1
                return new EurekaJerseyClientImpl(connectionTimeout, readTimeout, connectionIdleTimeout, config);
            } catch (Throwable e) {
                throw new RuntimeException("Cannot create Jersey client ", e);
            }
        }

接下來看1處:

public EurekaJerseyClientImpl(int connectionTimeout, int readTimeout, final int connectionIdleTimeout,ClientConfig clientConfig) {
        try {
            jerseyClientConfig = clientConfig;
            // 1
            apacheHttpClient = ApacheHttpClient4.create(jerseyClientConfig);
            // 2
            HttpParams params = apacheHttpClient.getClientHandler().getHttpClient().getParams();

            HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
            HttpConnectionParams.setSoTimeout(params, readTimeout);
			
        } catch (Throwable e) {
            throw new RuntimeException("Cannot create Jersey client", e);
        }
    }
  • 1處,創建com.sun.jersey.client.apache4.ApacheHttpClient4類型的對象

    該類型,就位於:

        <dependency>
          <groupId>com.sun.jersey.contribs</groupId>
          <artifactId>jersey-apache-client4</artifactId>
          <version>1.19.1</version>
          <scope>runtime</scope>
        </dependency>
    
    
        public static ApacheHttpClient4 create(final ClientConfig cc) {
            return new ApacheHttpClient4(createDefaultClientHandler(cc), cc);
        }
    

    這裏的createDefaultClientHandler(cc),裏面會去創建HttpClient。

    private static ApacheHttpClient4Handler createDefaultClientHandler(final ClientConfig cc) {
    		...
    
    		// 1
            final DefaultHttpClient client = new DefaultHttpClient(
                    (ClientConnectionManager)connectionManager,
                    (HttpParams)httpParams
            );
    
            ...
    		
            return new ApacheHttpClient4Handler(client, cookieStore, preemptiveBasicAuth);
        }
    

    這裏面細節省略了部分,主要就是1處,創建了HttpClient,這個就是平時我們用來發http請求的那個。

  • 2處,設置一些參數,這裏的HttpParams,從哪兒取出來的?apacheHttpClient.getClientHandler().getHttpClient()。這裏取到的,已經是HttpClient了。

    到此為止,我們可以看看httpParams中有哪些header:

在具體工廠基礎上,對註冊用的工廠進行封裝

        com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
        // 1    
		if (clientConfig.shouldRegisterWithEureka()) {
            EurekaHttpClientFactory newRegistrationClientFactory = null;
            EurekaHttpClient newRegistrationClient = null;
            // 2
            newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                transportConfig
            );
            // 3
            newRegistrationClient = newRegistrationClientFactory.newClient();
            // 4
            eurekaTransport.registrationClientFactory = newRegistrationClientFactory;
            eurekaTransport.registrationClient = newRegistrationClient;
        }

我們前面的n步,已經把通信用的客戶端,及對應的工廠,都已經創建出來了,為啥這裏又要創建什麼工廠。

簡單來說,前面的工廠,造出來的客戶端,通信是沒問題了;但是,你通信失敗了,要重試嗎,重試的話,換哪一台呢?你每次通信是成功,還是失敗,還是超時,需要統計嗎?一個生產級的框架,是要有這些功能的。

所以,這裏主要是進行一些上層的封裝。

ok,繼續分析上面的代碼。

  • 1處,判斷是否要註冊到eureka
  • 2處,生成一個工廠,該工廠負責生產:註冊用的客戶端
  • 3處,使用2處拿到的工廠,創建註冊用的客戶端
  • 4處,把3處拿到的客戶端,存儲到eurekaTransport的field中。

繼續深入2處。

    com.netflix.discovery.shared.transport.EurekaHttpClients#canonicalClientFactory
	static EurekaHttpClientFactory canonicalClientFactory(
        final String name,
        final EurekaTransportConfig transportConfig,
        final ClusterResolver<EurekaEndpoint> clusterResolver,
        final TransportClientFactory transportClientFactory) {
		// 1
        return new EurekaHttpClientFactory() {
            // 2
            @Override
            public EurekaHttpClient newClient() {
                // 3
                return new SessionedEurekaHttpClient(
                        name,
                        RetryableEurekaHttpClient.createFactory(...),
                        transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000
                );
            }

            @Override
            public void shutdown() {
                wrapClosable(clusterResolver).shutdown();
            }
        };
    }
  • 1處,返回了一個工廠對象
  • 2處,工廠里重寫了newClient
  • 3處,返回了一個包裝過的EurekaClient。

可以看下這裏返回的SessionedEurekaHttpClient類。

這裏就是裝飾器模式,對enreka進行了層層封裝,和 java 的 io 流那樣理解就對了。

在具體工廠基礎上,對查詢用的工廠進行封裝

		// 1
		if (clientConfig.shouldFetchRegistry()) {
            EurekaHttpClientFactory newQueryClientFactory = null;
            EurekaHttpClient newQueryClient = null;
            // 2
            newQueryClientFactory = EurekaHttpClients.queryClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                clientConfig,
                transportConfig,
                applicationInfoManager.getInfo(),
                applicationsSource,
                endpointRandomizer
            );
            // 3
            newQueryClient = newQueryClientFactory.newClient();
            eurekaTransport.queryClientFactory = newQueryClientFactory;
            eurekaTransport.queryClient = newQueryClient;
        }

這裏的代碼,和上面基本相似。只不過,這裡是給查詢用的,所謂查詢,就是去eureka server獲取信息,比如服務提供者列表啥的。

  • 1處,判斷是否要去eureka server獲取
  • 2處,創建查詢用的工廠
  • 3處,利用2處拿到的工廠,創建查詢客戶端

步驟8:去eureka server獲取服務提供者信息

我們終於把步驟7講完了,實在有點長。

com.netflix.discovery.DiscoveryClient#DiscoveryClient(...)
    
// 1    
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
    // 2
    fetchRegistryFromBackup();
}

這裏1處,就是判斷要不要去獲取,如果要的話,就調用fetchRegistry(false)。

2處,如果1處沒取到,則要從backup地方去取。這塊可以自己定製backup策略。

註冊到eureka server

        if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
            // 1
            if (!register() ) {
                throw new IllegalStateException("Registration error at startup. Invalid server response.");
            }
        }

這裡會判斷是否要註冊,是否要在初始化的時候註冊,如果要的話,進入1處,進行註冊。

初始化周期執行的任務

        // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
        initScheduledTasks();

看這裏註釋,初始化的任務包括:集群解析、心跳、實例信息註冊、周期從eureka server獲取信息等。

周期任務:獲取服務提供者信息

if (clientConfig.shouldFetchRegistry()) {
            // registry cache refresh timer
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ),
                    registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }

默認30s一次。

周期任務:定時發心跳,向eureka server進行renew

            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);

這個也是30s。

心跳包,基本就是個put請求,裏面攜帶了2個參數。

@Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {
        String urlPath = "apps/" + appName + '/' + id;
        ClientResponse response = null;
        try {
            WebResource webResource = jerseyClient.resource(serviceUrl)
                    .path(urlPath)
                    .queryParam("status", info.getStatus().toString())
                    .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());

周期任務:InstanceInfoReplicator

這個任務,默認也是30s執行一次。

            instanceInfoReplicator = new InstanceInfoReplicator(
                    this,
                    instanceInfo,
                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                    2); // burstSize

這個任務,其實現了runnable,註釋如下:


/**
 * A task for updating and replicating the local instanceinfo to the remote server. Properties of this task are:
 * - 1 configured with a single update thread to guarantee sequential update to the remote server
 * - 2 update tasks can be scheduled on-demand via onDemandUpdate()
 * - 3 task processing is rate limited by burstSize
 * - 4 a new update task is always scheduled automatically after an earlier update task.  However if an on-demand task is started, the scheduled automatic update task is discarded (and a new one will be scheduled after the new
 *   on-demand update).
 *
 *   @author dliu
 */
class InstanceInfoReplicator implements Runnable 
  • 1處,配置了一個單線程,保證向遠程eureka server,順序更新
  • 2處,通過本類的onDemandUpdate,可以強行插入一個任務,而無需通過定時執行
  • 3處,限流相關
  • 4處,執行完一個周期任務后,馬上會給自己安排下一個周期任務

其run方法:

    public void run() {
        try {
            // 1
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                // 2
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        }finally {
            // 3
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }
  • 1處,刷新實例信息
  • 2處,如果有需要的話,向eureka server進行註冊
  • 3處,調度下一次任務

初始化結束

基本,這個CloudEurekaClient構造就結束了,後續就依靠其開啟的一堆定時任務去進行工作。

總結

eureka client的初始化就講了這麼多,註冊還沒講,留帶下一講吧。

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

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

Python裝飾器的一點解讀_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

  版權申明:本文為博主窗戶(Colin Cai)原創,歡迎轉帖。如要轉貼,必須註明原文網址

  http://www.cnblogs.com/Colin-Cai/p/12977127.html 

  作者:窗戶

  QQ/微信:6679072

  E-mail:6679072@qq.com

  理論上,函數是一等公民(first class function)的語言都可以使用函數式編程,從而利用算子(高階函數)來做裝飾器。

  裝飾器一般是這樣一個算子,它接受一個函數作為參數,返回另外一個函數。裝飾器,顧名思義,就是把一個函數“裝飾”一下,得到另外一個函數。為何要裝飾一下呢?目的一般是可能設計上需要對函數做一些改裝,比如原函數輸出結果需要再加工加工,或者原函數的輸入參數傳入不一樣,或者兩者兼有之,等等。

 

  迭代是編程中常用的手段,它的計算方式表現為狀態的不斷變換,且狀態的變換具有唯一性。

  比如我們使用Scheme來表示迭代。

;stat代表當前狀態,next代表狀態改變函數,final?代表判斷狀態是否終止
(define (iterate-orgin stat next final?)
  (if (final? stat)
      stat
      (iterate-orgin (next stat) next final?)))

;將next函數和final?函數封成一個函數
(define (iterate stat f-stat)
  (iterate-orgin stat (f-stat 'next) (f-stat 'final)))

;最終我們需要的迭代函數
(define (it f-stat)
  (lambda (stat) (iterate stat f-stat)))

  

  以上構造出一個算子it,就是用來“裝飾”迭代的函數。

  我們構造一個對list求和的迭代:

  可以每次把list的前面兩個相加,比如對(1 2 3 4 5)求和,經過以下狀態:

  (1 2 3 4 5)

  (3 3 4 5)

  (6 4 5)

  (10 5)

  (15)

  15

  得到最後結果15。

  代碼可以如下:

(define (make-sum-func sym)
 (if (eq? sym 'next);next函數
  (lambda (lst)
   (if (pair? lst)
    (if (null? (cdr lst))
     (car lst)
     (cons (+ (car lst) (cadr lst)) (cddr lst)))
    lst))
  (if (eq? sym 'final?);final?函數
   (lambda (lst) (not (pair? lst)))
   '())))

 

  然後測試一下((it make-sum-func) ‘(1 2 3 4 5)),得到最後結果15。

  上面兩個函數寫在一起,我們還可以再分離一下。

  

;定義一個打包函數
(define (wrap-next-final next final?)
 (lambda (sym)
  (if (eq? sym 'next)
   next
   (if (eq? sym 'final?)
    final?
    '()))))

;下面next和final?兩個函數可以分開寫
(define make-sum-next
 (lambda (lst)
  (if (pair? lst)
   (if (null? (cdr lst))
    (car lst)
    (cons (+ (car lst) (cadr lst)) (cddr lst)))
   lst)))

(define make-sum-final?
 (lambda (lst) (not (pair? lst))))

;於是函數就可以下面這樣表示
(define make-sum-func (wrap-next-final make-sum-next make-sum-final?))

 

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

  總而言之,裝飾器就是這樣一類算子。

 

  Python也是這樣,只是Python提供了@這樣的語法,實際上是個語法糖,與其說是簡寫,倒是更像是個語法提醒這是一個裝飾器。

  我們這次希望來显示一下mysym,還是用求和。

  先寫一個簡單的mysum函數用於求和: 

def mysum(*args):
    ret = 0
    for i in args:
        ret += i
    return ret

 

  做一個算子,來擴充它的輸入參數:

  這裏需要用來判斷一個對象是否是可迭代對象,

  from collections import Iterable

  然後,如果判斷對象x是否是可迭代對象,只需要:

  isinstance(x, Iterable)

  算子如下:

from collections import Iterable
def args_expan(f):
    def f2(*args):
        lst = []
        for i in args:
            if isinstance(i, Iterable):
                lst.append(f(*i))
            else:
                lst.append(i)
        return f(*lst)
    return f2

 

  然後在mysum前添加裝飾器標誌

@args_expan
def mysum(*args):
    ret = 0
    for i in args:
        ret += i
    return ret

 

  測試如下:

print(mysum(1,2,3,4,5))
print(mysum((1,2,3,4,5)))
print(mysum([1,2,3,4,5]))
print(mysum(range(1,6)))
print(mysum(map(lambda x:x+1, range(5))))
print(mysum(filter(lambda x:x<6, range(10))))

#構造了一個生成器
def gen_range(a, b):
    while a < b:
        yield a
        a += 1

print(mysum(\
    filter(lambda x:x<6, range(10)), \
    6, \
    [7,8], \
    (9, 10), \
    map(lambda x:x+11, range(10)), \
    gen_range(21,101)))

 

  運行得到:

15
15
15
15
15
15
5050

  終於看到,函數功能已被擴充。

  這個裝飾器還可以裝飾別的函數,比如乘積、統計等。

 

  從而,裝飾器就是這樣一個算子,一般用來改造函數的輸入或輸出,避免重複寫代碼。

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

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式