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

百公里加速不到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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

都拖家帶口了…有哪些車適合80后?

能堅強地走到今天,已經是世界性的奇迹。對於這樣一群堅韌不拔的人,努力奮鬥奮鬥,過兩年開上保時捷911還真不是痴人說夢。

總結

俗話說80后是“被坑的一代”,沒趕上改革開放發財致富的機會,卻遇上了金融危機跟樓市瘋長的節點。能堅強地走到今天,已經是世界性的奇迹。對於這樣一群堅韌不拔的人,努力奮鬥奮鬥,過兩年開上保時捷911還真不是痴人說夢!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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

css3中的@font-face你真的了解嗎

css3中的自定義字體方法@font-face

@font-face屬性可以讓我們自定義網站字體屬性,然後引用到想要應用該字體的元素上。

基本語法:

@font-face {
  font-family: <font-name>;
  src: local( <family-name> ) | <url> [format("formatName")][,<url> [format("formatName")]]*;
  unicode-range: <unicode-range>;
  font-variant: <font-variant>;
  font-feature-settings: <font-feature-settings>;
  font-variation-settings: <font-variation-settings>;
  font-stretch: <font-stretch>;
  font-weight: <font-weight>;
  font-style: <font-style>;
  font-display: <font-display>;
}

屬性規則說明

font-family

給你引入的字體起一個專屬的字體名字,font-name,然後他會在元素font-family:中使用,如div{font-family:font-name}

src

用於指定加載字體文件的路徑或者加載本地字體

local

加載一個本地字體,font-name表示本地的字體名稱,比如Microsoft YaHei | 微軟雅黑;如果本地有應用此字體显示文本。

示例:

/* 加載一個本地字體 */
@font-face{
  font-family: myFont;
  src: local('Microsoft YaHei');
}
/* 加載多個本地字體 */
@font-face{
  font-family: myFont;
  src:  local(黑體), local("Microsoft YaHei"), local("HelveticaNeue-Light"), local("Helvetica Neue Light"),  local("PingFang SC"), local(sans-serif);
}
/* 應用自定義字體 */
.box{
  font-family: myFont;
}

在上邊代碼中看到,可以使用一個或多個local,多個之間用逗號分開,括號中的字體名稱可以使用單引號或者雙引號括起來,也可以不帶引號直接寫字體名稱,有空格的必須添加引號,但是只能寫一個字體名稱
上邊的寫法讓我們在定義字體的時候變得方便很多,我們只需要定義好自定義名稱然後直接引用該字體等同於下邊代碼:

.box{
  font-family: 黑體, "Microsoft YaHei", "HelveticaNeue-Light", "Helvetica Neue Light", "PingFang SC", sans-serif;
}

url

表示服務器端提供的字體地址,這個也是可以使用多個,多個之間用逗號隔開,一般寫多個是為了瀏覽器兼容加載不同格式的字體。目前web可以加載六種格式的字體:

  1. EOT:全拼:Embedded_OpenType,是由微軟開發的字體格式規範,所以只適用於IE瀏覽器。詳細介紹

兼容:

兼容詳情

  1. TTF:全拼:TrueType,是一種輪廓字體標準,最早是由蘋果公司研發,後來成為Mac OSMicrosoft Windows系統中最常用的字體格式。詳細介紹

兼容:

兼容詳情
3. OTF:全拼:OpenType,是可縮放計算機字體的格式,是由微軟和Adobe公司聯合開發。詳細介紹

兼容:

兼容詳情
4. WOFF:全拼:Web Open Font Formatweb網絡開放字體格式,他是專為網絡設計的一種字體格式,WOFF是把OpenTypeTrueType字體進行了封裝,並進行了壓縮優化,它使用了廣泛應用的zlib壓縮,並添加了XML元數據,這種字體格式體積更小,適用於網絡傳輸,可以使用戶體驗做到更好。詳細介紹

兼容:

兼容詳情
5. WOFF2:它是WOFF的升級版,它使用Brotli進行字節級壓縮,比WOFF體積更小

兼容:

兼容詳情

  1. SVG:全拼:Scalable Vector Graphics可縮放矢量圖形,是一種基於可擴展標記語言(XML)的矢量圖像格式,用於二維圖形,並支持交互性和動畫,字體中就是使用svg技術來呈現文字樣式。我測試只有蘋果Safari支持; 詳細介紹

兼容:

兼容詳情

format

可選值,表示給加載的外部字體指定字體格式,用來告訴瀏覽器讓瀏覽器能夠識別所使用的字體格式,可用的類型有 embedded-opentype, truetype, opentype, woff, woff2, svg。分別對應上邊我們介紹的字體格式。

語法:

/* 加載一種字體格式 */
@font-face{
  font-family: "myFontName";
  src:  url('font.woff') format('woff');
}

/* 加載多個字體格式,兼容更多瀏覽器 */
@font-face{
  font-family: "myFontName";
  src: url('font.eot'); /* IE9*/
  src: url('font.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('font.woff2') format('woff2'),
  url('font.woff') format('woff'), /* chrome、firefox */
  url('font.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
  url('font.svg#Alibaba-PuHuiTi-Regular') format('svg'); /* iOS 4.1- */
}

從上邊語法來看我們可以加載一個格式的字體文件,也可以加載多個格式字體,之間用逗號分開,瀏覽器會優先讀取寫在前面的字體格式並且檢測是否支持,如果支持就使用該格式的字體文件。

font-weight

表示自定義字體規則的字重程度,我們可以給一個字體指定不同的粗細規則引用不同規格的字體文件。

語法:

/* Single values */
font-weight: normal;
font-weight: bold;
font-weight: 400;

/* Multiple Values */
font-weight: normal bold;
font-weight: 300 500;

取值說明:

  1. normal:默認值,表示該字體規則是在默認情況下的字體,也就是在應用改字體的元素中不規定字體的粗細情況或者font-weight: 400 | normal下應用該字體;
  2. bold:粗體,表示元素設置font-weight: bold | 700,或者使用<b><strong>元素的時候應用該字體。
  3. 400:也可以設置成數值,在CSS Fonts Level 4之前的版本只能去100-900的100倍數值,之後的數值可以去1-1000的任意數值。
  4. normal bold:可以使用多個關鍵字來定義此字體規則,多個關鍵字之間用逗號分開,表示元素字重設置為此關鍵字中的其中一個值時應用該字體。
  5. 300 500:也可以使用多個數值來定義此字體規則。

取數值情況下應該對應的每個字體:

value 對應的字體的自重名稱
100 Thin (Hairline)
200 Extra Light (Ultra Light)
300 Light
400 Normal
500 Medium
600 Semi Bold (Demi Bold)
700 Bold
800 Extra Bold (Ultra Bold)
900 Black (Heavy)

代碼示例:因為字體有版權限制,這裏我們使用阿里的免費商用字體來演示

https://codepen.io/qwguo88/full/jObgQYG

從上邊的案例我們可以看出,先自定義了一個名為FW的字體,並且使用font-weight定義不同字重使用不同的字體。在上邊的案例中定義了5中字重樣式,分別是bold阿里巴巴-普惠體-Heavy100楊任東竹石體-Bold200站酷高端黑300 600龐門正道標題體2900思源黑體-粗
然後給div設置font-family:FW;最後我們分別給這個div下的每個段落設置不同的font-weight,段落的字體就會根據不同的字重來應用不同的字體。
我們可以把自定義字體看成我們平常使用系統內置字體一樣,當我們設置字體為微軟雅黑,並且設置不同的字重他會在系統中尋找每個自重對應的字體,然後來显示。

font-style

表示自定義字體規則的樣式表現形式,我們可以給一個字體指定不同的樣式規則引用不同規格的字體文件。

語法:

font-style: normal | italic | oblique <angle>{0,2}

取值說明:

  1. normal:默認字樣式使用的字體規則,當我們不設置或者設置成此值時的字體。
  2. italic:表示字樣式設置成斜體的時候使用的字體規則。
  3. oblique:表示字樣式設置成斜體的時候使用的字體規則。

當我們同時定義italicoblique規則的字體時,寫在後邊的生效所設置的斜體字體显示。

代碼示例: https://codepen.io/qwguo88/full/RwWXONo

unicode-range

表示自定義字體規則的unicode字符範圍

語法:

/* unicode-range 取值規則 */
unicode-range: U+26;                /* 單個值 */
unicode-range: U+0-7F;              /* 字符編碼區間*/
unicode-range: U+0025-00FF;        /* 字符編碼區間 */
unicode-range: U+4??;              /* 通配符區間 */
unicode-range: U+0025-00FF, U+4??; /* 可以寫多個值,多個值之間用逗號分開 */

取值說明:
取值規則:前邊是U+後邊跟上字符的charCode

  1. 可以是單個值,表示文本中只有該字符的字應用該字體。
  2. 可以使用一個字符區間,表示文本中如果有在此區間的文字將應用改字體規則。
  3. 也可以使用通配符來設置一個區間規則其中?表示一個16進制0-F的之間的值U+4??表示 U+400U+4FF區間的字符編碼。
  4. 也可以使用多個值,多個值之間使用逗號分開。

案例:https://codepen.io/qwguo88/full/XWXWqmP

從上邊案例可以看出,unicode-range是用來規定應用當前字體規則的文字unicode碼在規則內的將以此字體規則显示字體。
他能讓我們來控制一個段落中的個別字的显示效果,一般要显示的字體規則排在最前面,將優先显示。

font-display

設置自定義字體在沒有加載完的显示方式取值如下:

語法:

font-display: auto | block | swap | fallback | optional
  1. auto:字體显示策略由用戶代理定義。
  2. block:為字體提供一個短暫的阻塞周期和無限的交換周期。也就是說等字體加載完以後字體显示效果會自動更新成改字體
  3. swap:為字體提供一個非常小的阻塞周期和無限的交換周期。也就是說等字體加載完以後字體显示效果會自動更新成改字體
  4. fallback:為字體提供一個非常小的阻塞周期和短暫的交換周期。也就是說等字體加載在過了一定的交互周期后加載完字體將不進行更新显示
  5. optional:為字體提供一個非常小的阻塞周期,並且沒有交換周期。也就是說等字體加載不進行更新显示

參考網站

  1. https://webplatform.github.io/docs/tutorials/typography/font-face/
  2. https://developer.mozilla.org/zh-CN/docs/Web/CSS/@font-face/font-display
  3. https://www.zhangxinxu.com/wordpress/2016/11/css-unicode-range-character-font-face/
  4. https://www.w3cplus.com/css/font-display-masses.html

字體下載格式轉換網站

  1. https://www.fontke.com/tool/fontface/
  2. http://www.fonts.net.cn/
  3. https://fonts.google.com/

字體壓縮工具

  1. http://www.fonts.net.cn/ 字體天下
  2. http://www.ziticq.com/ 字體傳奇
  3. https://www.hellofont.cn/ 字由
  4. http://fontstore.baidu.com/static/editor/index.html 百度在線字體編輯器
  5. https://efe.baidu.com/ 百度字體處理
  6. https://www.fontsquirrel.com/tools/webfont-generator 字體格式換
  7. https://www.fontke.com/tool/fontface/ 字體轉換

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

Python 圖像處理 OpenCV (9):圖像處理形態學開運算、閉運算以及梯度運算

前文傳送門:

「Python 圖像處理 OpenCV (1):入門」

「Python 圖像處理 OpenCV (2):像素處理與 Numpy 操作以及 Matplotlib 显示圖像」

「Python 圖像處理 OpenCV (3):圖像屬性、圖像感興趣 ROI 區域及通道處理」

「Python 圖像處理 OpenCV (4):圖像算數運算以及修改顏色空間」

「Python 圖像處理 OpenCV (5):圖像的幾何變換」

「Python 圖像處理 OpenCV (6):圖像的閾值處理」

「Python 圖像處理 OpenCV (7):圖像平滑(濾波)處理」

「Python 圖像處理 OpenCV (8):圖像腐蝕與圖像膨脹」

引言

前面介紹了圖像形態學的兩種基礎算法,圖像腐蝕和圖像膨脹,本篇接着介紹圖像形態學中的開運算、閉運算以及梯度運算。

由於內容的連貫性,請先閱讀前文「Python 圖像處理 OpenCV (8):圖像腐蝕與圖像膨脹」,了解清楚圖像的腐蝕與膨脹基礎原理。

不然真的沒辦法理解開運算和閉運算。

第一件事情還是給圖像增加噪聲,思路沿用之前加噪聲的思路,使用 Numpy 給圖片添加黑白兩種噪聲點,代碼如下:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
img = cv.imread("demo.png", cv.IMREAD_UNCHANGED)
source = cv.cvtColor(img, cv.COLOR_BGR2RGB)
rows, cols, chn = source.shape

# 加噪聲-白點噪聲
for i in range(500):
    x = np.random.randint(0, rows)
    y = np.random.randint(0, cols)
    source[x, y, :] = 255

# 圖像保存 白點噪聲圖像
cv.imwrite("demo_noise_white.jpg", source)
print("白點噪聲添加完成")

# 重新讀取圖像
img1 = cv.imread("demo.png", cv.IMREAD_UNCHANGED)
source1 = cv.cvtColor(img1, cv.COLOR_BGR2RGB)

# 加噪聲-黑點噪聲
for i in range(1000):
    x = np.random.randint(0, rows)
    y = np.random.randint(0, cols)
    source1[x, y, :] = 0

# 圖像保存 黑點噪聲圖像
cv.imwrite("demo_noise_black.jpg", source1)
print("黑點噪聲添加完成")

# 显示結果
titles = ['White Img','Black Img']
images = [source, source1]

# matplotlib 繪圖
for i in range(2):
   plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

形態學開運算

圖像開運算實際上是一個組合運算,開運算是圖像先進行腐蝕,再進行膨脹的運算。

圖像被腐蝕后,去除了噪聲,但是也壓縮了圖像;接着對腐蝕過的圖像進行膨脹處理,使得剛才在腐蝕過程中被壓縮的圖像得以恢復原狀。

下面是一個圖像開運算的流程圖:

開運算的一些特性:

  • 開運算能夠除去孤立的小點,毛刺和小橋,而總的位置和形狀不便。
  • 開運算是一個基於幾何運算的濾波器。
  • 結構元素大小的不同將導致濾波效果的不同。
  • 不同的結構元素的選擇導致了不同的分割,即提取出不同的特徵。

我們先不管開運算 OpenCV 為我們提供的函數是什麼,先使用前面介紹過的圖像腐蝕與膨脹處理看下結果:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
source = cv.imread("demo_noise_white.jpg", cv.IMREAD_GRAYSCALE)

# 設置卷積核
kernel = np.ones((5, 5),np.uint8)

# 圖像腐蝕
erode_img = cv.erode(source, kernel)

# 圖像膨脹
dilate_result = cv.dilate(erode_img, kernel)

# 显示結果
titles = ['Source Img','Erode Img','Dilate Img']
images = [source, erode_img, dilate_result]

# matplotlib 繪圖
for i in range(3):
   plt.subplot(1, 3, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

可以看到降噪的效果還是不錯的。

接着看 OpenCV 為開運算提供的函數。

圖像開運算主要使用到的函數是 morphologyEx() 它是形態學擴展的一組函數,而其中的 cv.MORPH_OPEN 對應的是開運算。

使用時語法如下:

dst = cv.morphologyEx(src, cv.MORPH_OPEN, kernel)
  • src: 原圖形
  • cv2.MORPH_OPEN: 表示開運算
  • kernel: 卷積核

我們再使用 morphologyEx() 函數去重新實現下剛才的圖像開運算,看下和之前的結果有啥區別:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
source = cv.imread("demo_noise_white.jpg", cv.IMREAD_GRAYSCALE)

# 設置卷積核
kernel = np.ones((5, 5),np.uint8)

#圖像開運算
dst = cv.morphologyEx(source, cv.MORPH_OPEN, kernel)

# 显示結果
titles = ['Source Img','Dst Img']
images = [source, dst]

# matplotlib 繪圖
for i in range(2):
   plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

至少從肉眼的角度上看不出來和之前的方式有啥區別,實際上也沒啥區別。

形態學閉運算

與開運算相反的是閉運算,閉運算是圖像先膨脹,后腐蝕,它有助於關閉前景物體內部的小孔,或物體上的小黑點。

先看下圖像閉運算的流程圖:

閉運算的一些特性:

  • 閉運算能夠填平小湖(即小孔),彌合小裂縫,而總的位置和形狀不變。
  • 閉運算是通過填充圖像的凹角來濾波圖像的。
  • 結構元素大小的不同將導致濾波效果的不同。
  • 不同結構元素的選擇導致了不同的分割。

首先還是用 dilate()erode() 函數實現一下圖像閉運算,代碼如下:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
source = cv.imread("demo_noise_black.jpg", cv.IMREAD_GRAYSCALE)

# 設置卷積核
kernel = np.ones((5, 5),np.uint8)

# 圖像膨脹
dilate_result = cv.dilate(source, kernel)

# 圖像腐蝕
erode_img = cv.erode(dilate_result, kernel)

# 显示結果
titles = ['Source Img','Dilate Img','Erode Img']
images = [source, dilate_result, erode_img]

# matplotlib 繪圖
for i in range(3):
   plt.subplot(1, 3, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

如果想要使用形態學擴展的函數 morphologyEx() 則需要把裏面的參數換成 MORPH_CLOSE ,同樣,既然是形態學擴展函數,那麼圖像腐蝕和圖像膨脹也有對應的參數:

  • 圖像腐蝕: MORPH_ERODE
  • 圖像膨脹: MORPH_DILATE

接着還是使用 MORPH_CLOSE 參數來實現下圖像的閉運算:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
source = cv.imread("demo_noise_black.jpg", cv.IMREAD_GRAYSCALE)

# 設置卷積核
kernel = np.ones((5, 5),np.uint8)

# 圖像閉運算
dst = cv.morphologyEx(source, cv.MORPH_CLOSE, kernel)

# 显示結果
titles = ['Source Img','Dst Img']
images = [source, dst]

# matplotlib 繪圖
for i in range(2):
   plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

形態學梯度運算

圖像形態學的梯度運算和前面的開運算閉運算是一樣的,都是組合函數。

梯度運算實際上是圖像膨脹減去圖像腐蝕后的結果,最終我們得到的是一個類似於圖像輪廓的圖形。

梯度運算在 morphologyEx() 函數中的參數是 MORPH_GRADIENT ,示例代碼如下:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

# 讀取圖片
source = cv.imread("demo.png", cv.IMREAD_GRAYSCALE)

# 設置卷積核
kernel = np.ones((5, 5), np.uint8)

# 圖像梯度運算
dst = cv.morphologyEx(source, cv.MORPH_GRADIENT, kernel)

# 显示結果
titles = ['Source Img','Dst Img']
images = [source, dst]

# matplotlib 繪圖
for i in range(2):
   plt.subplot(1, 2, i+1), plt.imshow(images[i],'gray')
   plt.title(titles[i])
   plt.xticks([]),plt.yticks([])

plt.show()

示例代碼

如果有需要獲取源碼的同學可以在公眾號回復「OpenCV」進行獲取。

參考

http://www.woshicver.com/

https://blog.csdn.net/Eastmount/article/details/83651172

https://blog.csdn.net/hanshanbuleng/article/details/80657148

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

【其他文章推薦】

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

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

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

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

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

通過Nginx、Consul、Upsync實現動態負載均衡和服務平滑發布

前提

前段時間順利地把整個服務集群和中間件全部從UCloud遷移到阿里雲,筆者擔任了架構和半個運維的角色。這裏詳細記錄一下通過NginxConsulUpsync實現動態負載均衡和服務平滑發布的核心知識點和操作步驟,整個體系已經在生產環境中平穩運行。編寫本文使用的虛擬機系統為CentOS7.x,虛擬機的內網IP192.168.56.200

動態負載均衡的基本原理

一般會通過upstream配置Nginx的反向代理池:

http {
    
    upstream upstream_server{
        server 127.0.0.1:8081;
        server 127.0.0.1:8082;
    }

    server {
        listen       80;
        server_name localhost;

        location / {
            proxy_pass http://upstream_server;
        }
    }
}

現在假如8081端口的服務實例掛了需要剔除,那麼需要修改upstream為:

upstream upstream_server{
    # 添加down標記該端口的服務實例不參与負載
    server 127.0.0.1:8081 down;
    server 127.0.0.1:8082;
}

並且通過nginx -s reload重新加載配置,該upstream配置才會生效。我們知道,服務發布時候重啟過程中是處於不可用狀態,正確的服務發布過程應該是:

  • 把該服務從對應的upstream剔除,一般是置為down,告知Nginx服務upstream配置變更,需要通過nginx -s reload進行重載。
  • 服務構建、部署和重啟。
  • 通過探活腳本感知服務對應的端口能夠訪問,把該服務從對應的upstream中拉起,一般是把down去掉,告知Nginx服務upstream配置變更,需要通過nginx -s reload進行重載。

上面的步驟一則涉及到upstream配置,二則需要Nginx重新加載配置(nginx -s reload),顯得比較笨重,在高負載的情況下重新啟動Nginx並重新加載配置會進一步增加系統的負載並可能暫時降低性能。

所以,可以考慮使用分佈式緩存把upstream配置存放在緩存服務中,然後Nginx直接從這個緩存服務中讀取upstream的配置,這樣如果有upstream的配置變更就可以直接修改緩存服務中對應的屬性,而Nginx服務也不需要reload。在實戰中,這裏提到的緩存服務就選用了ConsulNginx讀取緩存中的配置屬性選用了新浪微博提供的NginxC語言模塊nginx-upsync-module。示意圖大致如下:

Consul安裝和集群搭建

ConsulHashicorp公司的一個使用Golang開發的開源項目,它是一個用於服務發現和配置的工具,具備分佈式和高度可用特性,並且具有極高的可伸縮性。Consul主要提供下面的功能:

  • 服務發現。
  • 運行狀況檢查。
  • 服務分塊/服務網格(Service Segmentation/Service Mesh)。
  • 密鑰/值存儲。
  • 多數據中心。

下面是安裝過程:

mkdir /data/consul
cd /data/consul
wget https://releases.hashicorp.com/consul/1.7.3/consul_1.7.3_linux_amd64.zip
# 注意解壓后只有一個consul執行文件
unzip consul_1.7.3_linux_amd64.zip

解壓完成后,使用命令nohup /data/consul/consul agent -server -data-dir=/tmp/consul -bootstrap -ui -advertise=192.168.56.200 -client=192.168.56.200 > /dev/null 2>&1 &即可後台啟動單機的Consul服務。啟動Consul實例后,訪問http://192.168.56.200:8500/即可打開其後台管理UI

下面基於單台虛擬機搭建一個偽集群,關於集群的一些配置屬性的含義和命令參數的解釋暫時不進行展開

# 創建集群數據目錄
mkdir /data/consul/node1 /data/consul/node2 /data/consul/node3
# 創建集群日誌目錄
mkdir /data/consul/node1/logs /data/consul/node2/logs /data/consul/node3/logs

/data/consul/node1目錄添加consul_conf.json文件,內容如下:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node1",
  "log_file": "/data/consul/node1/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node1",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8510,
    "dns": 8610,
    "server": 8310,
    "serf_lan": 8311,
    "serf_wan": 8312
    }
}

/data/consul/node2目錄添加consul_conf.json文件,內容如下:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node2",
  "log_file": "/data/consul/node2/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node2",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8520,
    "dns": 8620,
    "server": 8320,
    "serf_lan": 8321,
    "serf_wan": 8322
    }
}

/data/consul/node3目錄添加consul_conf.json文件,內容如下:

{
  "datacenter": "es8-dc",
  "data_dir": "/data/consul/node3",
  "log_file": "/data/consul/node3/consul.log",
  "log_level": "INFO",
  "server": true,
  "node_name": "node3",
  "ui": true,
  "bind_addr": "192.168.56.200",
  "client_addr": "192.168.56.200",
  "advertise_addr": "192.168.56.200",
  "bootstrap_expect": 3,
  "ports":{
    "http": 8530,
    "dns": 8630,
    "server": 8330,
    "serf_lan": 8331,
    "serf_wan": 8332
    }
}

新建一個集群啟動腳本:

cd /data/consul
touch service.sh
# /data/consul/service.sh內容如下:
nohup /data/consul/consul agent -config-file=/data/consul/node1/consul_conf.json > /dev/null 2>&1 &
sleep 10
nohup /data/consul/consul agent -config-file=/data/consul/node2/consul_conf.json -retry-join=192.168.56.200:8311 > /dev/null 2>&1 &
sleep 10
nohup /data/consul/consul agent -config-file=/data/consul/node3/consul_conf.json -retry-join=192.168.56.200:8311 > /dev/null 2>&1 &

如果集群啟動成功,觀察節點1中的日誌如下:

通過節點1的HTTP端點訪問後台管理頁面如下(可見當前的節點1被標記了一顆紅色的星星,說明當前節點1是Leader節點):

至此,Consul單機偽集群搭建完成(其實分佈式集群的搭建大同小異,注意集群節點所在的機器需要開放使用到的端口的訪問權限),由於Consul使用Raft作為共識算法,該算法是強領導者模型,也就是只有Leader節點可以進行寫操作,因此接下來的操作都需要使用節點1的HTTP端點,就是192.168.56.200:8510

重點筆記:如果Consul集群重啟或者重新選舉,Leader節點有可能發生更變,外部使用的時候建議把Leader節點的HTTP端點抽離到可動態更新的配置項中或者動態獲取Leader節點的IP和端口。

Nginx編譯安裝

直接從官網下載二進制的安裝包並且解壓:

mkdir /data/nginx
cd /data/nginx
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar -zxvf nginx-1.18.0.tar.gz

解壓后的所有源文件在/data/nginx/nginx-1.18.0目錄下,編譯之前需要安裝pcre-develzlib-devel依賴:

yum -y install pcre-devel
yum install -y zlib-devel

編譯命令如下:

cd /data/nginx/nginx-1.18.0
./configure --prefix=/data/nginx

如果./configure執行過程不出現問題,那麼結果如下:

接着執行make

cd /data/nginx/nginx-1.18.0
make

如果make執行過程不出現問題,那麼結果如下:

最後,如果是首次安裝,可以執行make install進行安裝(實際上只是拷貝編譯好的文件到--prefix指定的路徑下):

cd /data/nginx/nginx-1.18.0
make install

make install執行完畢后,/data/nginx目錄下新增了數個文件夾:

其中,Nginx啟動程序在sbin目錄下,logs是其日誌目錄,conf是其配置文件所在的目錄。嘗試啟動一下Nginx

/data/nginx/sbin/nginx

然後訪問虛擬機的80端口,從而驗證Nginx已經正常啟動:

通過nginx-upsync-module和nginx_upstream_check_module模塊進行編譯

上面做了一個Nginx極簡的編譯過程,實際上,在做動態負載均衡的時候需要添加nginx-upsync-modulenginx_upstream_check_module兩個模塊,兩個模塊必須提前下載源碼,並且在編譯Nginx過程中需要指定兩個模塊的物理路徑:

mkdir /data/nginx/modules
cd /data/nginx/modules
# 這裡是Github的資源,不能用wget下載,具體是:
nginx-upsync-module需要下載release裏面的最新版本:v2.1.2
nginx_upstream_check_module需要下載整個項目的源碼,主要用到靠近當前版本的補丁,使用patch命令進行補丁升級

下載完成後分別(解壓)放在/data/nginx/modules目錄下:

ll /data/nginx/modules
drwxr-xr-x. 6 root root   4096 Nov  3  2019 nginx_upstream_check_module-master
drwxrwxr-x. 5 root root     93 Dec 18 00:56 nginx-upsync-module-2.1.2

編譯前,還要先安裝一些前置依賴組件:

yum -y install libpcre3 libpcre3-dev ruby zlib1g-dev patch

接下來開始編譯安裝Nginx

cd /data/nginx/nginx-1.18.0
patch -p1 < /data/nginx/modules/nginx_upstream_check_module-master/check_1.16.1+.patch
./configure --prefix=/data/nginx --add-module=/data/nginx/modules/nginx_upstream_check_module-master --add-module=/data/nginx/modules/nginx-upsync-module-2.1.2
make
make install

上面的編譯和安裝過程無論怎麼調整,都會出現部分依賴缺失導致make異常,估計是這兩個模塊並不支持太高版本的Nginx。(生產上用了一個版本比較低的OpenResty,這裏想復原一下使用相對新版本Nginx的踩坑過程)於是嘗試降級進行編譯,下面是參考多個Issue后得到的相對比較新的可用版本組合:

  • nginx-1.14.2.tar.gz
  • xiaokai-wang/nginx_upstream_check_module,使用補丁check_1.12.1+.patch
  • nginx-upsync-module:release:v2.1.2
# 提前把/data/nginx下除了之前下載過的modules目錄外的所有文件刪除
cd /data/nginx
wget http://nginx.org/download/nginx-1.14.2.tar.gz
tar -zxvf nginx-1.14.2.tar.gz

開始編譯安裝:

cd /data/nginx/nginx-1.14.2
patch -p1 < /data/nginx/modules/nginx_upstream_check_module-master/check_1.12.1+.patch
./configure --prefix=/data/nginx --add-module=/data/nginx/modules/nginx_upstream_check_module-master --add-module=/data/nginx/modules/nginx-upsync-module-2.1.2
make && make install

安裝完成后通過/data/nginx/sbin/nginx命令啟動即可。

啟用動態負載均和健康檢查

首先編寫一個簡易的HTTP服務,因為Java比較重量級,這裏選用Golang,代碼如下:

package main

import (
	"flag"
	"fmt"
	"net/http"
)

func main() {
    var host string
    var port int
    flag.StringVar(&host, "h", "127.0.0.1", "IP地址")
    flag.IntVar(&port, "p", 9000, "端口")
    flag.Parse()
    address := fmt.Sprintf("%s:%d", host, port)
    http.HandleFunc("/ping", func(writer http.ResponseWriter, request *http.Request) {
        _, _ = fmt.Fprintln(writer, fmt.Sprintf("%s by %s", "pong", address))
    })
    http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
        _, _ = fmt.Fprintln(writer, fmt.Sprintf("%s by %s", "hello world", address))
    })
    err := http.ListenAndServe(address, nil)
    if nil != err {
        panic(err)
    }
}

編譯:

cd src
set GOARCH=amd64
set GOOS=linux
go build -o ../bin/app app.go

這樣子在項目的bin目錄下就得到一個Linux下可執行的二進制文件app,分別在端口90009001啟動兩個服務實例:

# 記得先給app文件的執行權限chmod 773 app
nohup ./app -p 9000 >/dev/null 2>&1 &
nohup ./app -p 9001 >/dev/null 2>&1 &

修改一下Nginx的配置,添加upstream

# /data/nginx/conf/nginx.conf部分片段
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    upstream app {
       # 這裡是consul的leader節點的HTTP端點
       upsync 192.168.56.200:8510/v1/kv/upstreams/app/ upsync_timeout=6m upsync_interval=500ms upsync_type=consul strong_dependency=off;
       # consul訪問不了的時候的備用配置
       upsync_dump_path /data/nginx/app.conf;
       # 這裡是為了兼容Nginx的語法檢查
       include /data/nginx/app.conf;
       # 下面三個配置是健康檢查的配置
       check interval=1000 rise=2 fall=2 timeout=3000 type=http default_down=false;
       check_http_send "HEAD / HTTP/1.0\r\n\r\n";
       check_http_expect_alive http_2xx http_3xx;
    }

    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://app;
        }
        # 健康檢查 - 查看負載均衡的列表
        location /upstream_list {
            upstream_show;
        }
        # 健康檢查 - 查看負載均衡的狀態
        location /upstream_status {
            check_status;
            access_log off;
        }
    }
}

# /data/nginx/app.conf
server 127.0.0.1:9000 weight=1 fail_timeout=10 max_fails=3;
server 127.0.0.1:9001 weight=1 fail_timeout=10 max_fails=3;

手動添加兩個HTTP服務進去Consul中:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000
curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9001

最後重新加載Nginx的配置即可。

動態負載均衡測試

前置工作準備好,現在嘗試動態負載均衡,先從Consul下線9000端口的服務實例:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":1}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000

可見負載均衡的列表中,9000端口的服務實例已經置為down,此時瘋狂請求http://192.168.56.200,只輸出hello world by 127.0.0.1:9001,可見9000端口的服務實例已經不再參与負載。重新上線9000端口的服務實例:

curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":0}' http://192.168.56.200:8510/v1/kv/upstreams/app/127.0.0.1:9000

再瘋狂請求http://192.168.56.200,發現hello world by 127.0.0.1:9000hello world by 127.0.0.1:9001交替輸出。到此可以驗證動態負載均衡是成功的。此時再測試一下服務健康監測,通過kill -9隨機殺掉其中一個服務實例,然後觀察/upstream_status端點:

瘋狂請求http://192.168.56.200,只輸出hello world by 127.0.0.1:9001,可見9000端口的服務實例已經不再參与負載,但是查看Consul9000端口的服務實例的配置,並沒有標記為down,可見是nginx_upstream_check_module為我們過濾了異常的節點,讓這些節點不再參与負載。

總的來說,這個相對完善的動態負載均衡功能需要nginx_upstream_check_modulenginx-upsync-module共同協作才能完成。

服務平滑發布

服務平滑發布依賴於前面花大量時間分析的動態負載均衡功能。筆者所在的團隊比較小,所以選用了阿里雲的雲效作為產研管理平台,通過裏面的流水線功能實現了服務平滑發布,下面是其中一個服務的生產環境部署的流水線:

其實平滑發布和平台的關係不大,整體的步驟大概如下:

步驟比較多,並且涉及到大量的shell腳本,這裏不把詳細的腳本內容列出,簡單列出一下每一步的操作(注意某些步驟之間可以插入合理的sleep n保證前一步執行完畢):

  • 代碼掃描、單元測試等等。
  • 代碼構建,生成構建后的壓縮包。
  • 壓縮包上傳到服務器X中,解壓到對應的目錄。
  • Consul發送指令,把當前發布的X_IP:PORT的負載配置更新為down=1
  • stop服務X_IP:PORT
  • start服務X_IP:PORT
  • 檢查服務X_IP:PORT的健康狀態(可以設定一個時間周期例如120秒內每10秒檢查一次),如果啟動失敗,則直接中斷返回,確保還有另一個正常的舊節點參与負載,並且人工介入處理。
  • Consul發送指令,把當前發布的X_IP:PORT的負載配置更新為down=0

上面的流程是通過hard code完成,對於不同的服務器,只需要添加一個發布流程節點並且改動一個IP的佔位符即可,不需要對Nginx進行配置重新加載。筆者所在的平台流量不大,目前每個服務部署兩個節點就能滿足生產需要,試想一下,如果要實現動態擴容,應該怎麼構建流水線?

小結

服務平滑發布是CI/CD中比較重要的一個環節,而動態負載均衡則是服務平滑發布的基礎。雖然現在很多雲平台都提供了十分便捷的持續集成工具,但是在使用這些工具和配置流程的時候,最好能夠理解背後的基本原理,這樣才能在工具不適用的時候或者出現問題的時時候,迅速地作出判斷和響應。

參考資料:

  • nginx-upsync-module
  • Nginx docs
  • Consul docs

(本文完 c-7-d e-a-20200613 感謝廣州某金融科技公司運維大佬昊哥提供的支持)

技術公眾號(《Throwable文摘》),不定期推送筆者原創技術文章(絕不抄襲或者轉載):

娛樂公眾號(《天天沙雕》),甄選奇趣沙雕圖文和視頻不定期推送,緩解生活工作壓力:

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

(數據科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題

本文示例代碼、數據已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes

1 簡介

  在進行數據可視化時我們常常需要在可視化作品上進行一些文字標註,譬如對散點圖我們可以將每個散點對應的屬性信息標註在每個散點旁邊,但隨着散點量的增多,或圖像上的某個區域聚集了較多的散點時,疊加上的文字標註會擠在一起相互疊置,出現如圖1所示的情況:

圖1

  出現這種情況非常影響數據可視化作品的呈現效果,而我們下面要介紹的adjustText是一個輔助matplotlib所繪製的圖像自動調整文字位置以緩解遮擋現象的庫,其靈感來源於R中非常著名的輔助ggplot2解決文字遮擋問題的ggrepel

圖2

  它通過算法迭代,在一輪輪的迭代過程中逐漸消除文字遮擋現象:

圖3

  下面我們就來學習如何使用adjustText解決matplotlib圖像文字遮擋問題。

2 使用adjustText解決文字遮擋問題

2.1 從一個簡單的例子出發

  使用pip install adjustTextconda install -c conda-forge adjusttext 來安裝adjustText。安裝成功之後,首先生成隨機示例數據以方便之後的演示:

import matplotlib.pyplot as plt
from adjustText import adjust_text
import numpy as np

#解決中文显示問題
plt.rcParams['font.sans-serif'] = ['SimHei']

seed = np.random.RandomState(42) # 固定隨機數水平
x, y = seed.uniform(0, 1, [2, 100]) # 產生固定的均勻分佈隨機數
texts = [f'文字{i}' for i in range(x.__len__())]

  接着我們先不使用adjustText調整圖像,直接繪製出原始的散點+文字標籤

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 繪製所有點對應的文字標籤
for x_, y_, text in zip(x, y, texts):
    plt.text(x_, y_, text, fontsize=12)

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('圖4.png', dpi=300, bbox_inches='tight', pad_inches=0) # 保存圖像

圖4

  可以看到,在通常的情況下,散點聚集的區域內文字標籤非常容易重疊在一起,接下來我們使用adjustText的基礎功能來消除文字重疊現象:

圖5

  這時可以看到與圖4相比,圖5中的所有文字都沒有出現彼此重疊現象,adjustText幫助我們自動微調了文字的擺放位置,並且距離原始散點偏移較大的文字還貼心的加上了連接線,至此,我們就初探了adjustText的強大功能,接下來我們來學習adjustText的更多功能。

2.2 adjust_text的用法

  adjustText中的核心功能都通過調用函數adjust_text來實現,其核心參數如下:

texts:List型,每個元素都是表示單個文字標籤對應的matplotlib.text.Text對象

ax:繪製文字標籤的目標axe對象,默認為最近一次的axe對象

lim:int型,控制迭代調整文本標籤位置的次數,默認為500次

precision:float型,用於決定迭代停止的精度,默認為0.01,即所有標籤相互遮擋部分的長和寬占所有標籤自身長寬之和的比例,addjust_text會在精度達到precision和迭代次數超過lim這兩個條件中至少有一個滿足時停止迭代

only_move:字典型,用於指定文本標籤與不同對象發生遮擋時的位移策略,鍵有'points''text''objects',對應的值可選'xy''x''y',分別代表豎直和水平方向均調整、只調整水平方向以及只調整豎直方向

arrowprops:字典型,用於設置偏移后的文字標籤與原始位置之間的連線樣式,下文會作具體演示

save_steps:bool型,用於決定是否保存記錄迭代過程中各輪的幀圖像,默認為False

save_prefix:str型,當save_steps設置為True時,用於指定中間幀保存的路徑,默認為”,即當前工作路徑

  下面我們來演示一下這些參數的使用效果,首先我們來看看only_move參數的效果,在圖6的基礎上,我們設置only_move={'text': 'x'},即當文字出現遮擋時,只在水平方向上進行偏移,這裏將save_steps設置為True以直觀地查看偏移過程:

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 使用adjustText修正文字重疊現象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            only_move={'text': 'x'},
            arrowprops=dict(arrowstyle='-', color='grey'),
            save_steps=True)

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

圖6

  可以看到在整個迭代微調的過程中,每個標籤只在水平方向發生位移,你可以根據自己作圖的實際需要靈活調整這裏的平移策略。接下來我們來看看arrowprops對可視化結果的影響,在之前的例子里我們設置了arrowprops={arrowstyle='-', color='grey'},其中arrowstyle用於設定連線的線型,color不用多說,接下來我們添加參數lw用於控制線的寬度,並對線型與顏色進行修改:

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 使用adjustText修正文字重疊現象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            arrowprops=dict(arrowstyle='->', 
                            color='red',
                            lw=1))

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('圖7.png', dpi=300, bbox_inches='tight', pad_inches=0) # 保存圖像

  這時連線隨着我們自定義的設置改變到相應的樣式:

圖7

  有關adjustText的更多參數設置信息和示例可以去官方文檔(https://adjusttext.readthedocs.io/en/latest/ )查看。

  以上就是本文的全部內容,如有疑問歡迎在評論區與我們討論。

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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