廿年輝煌 經典再鑄 經典全順入駐上海汽車博物館

不僅如此,福特全順還與2016年G20杭州峰會形成戰略合作關係,成為其指定用車,被授予“G20杭州峰會指定產品”榮譽證書。此次全順榮耀慶典的最大亮點,即是全順系列明星車型—經典全順順利入駐上海汽車博物館,被該館永久收藏。

全順是福特和江鈴汽車合資主打商用車的品牌,在大街上也經常能看到,特別的是改裝款車型如運鈔車、救護車、警車等。在中國市場銷售已經近20年,累計銷售50萬台,一直在輕客市場保持着銷量冠軍,而且這20年中幾乎沒有改款,堪稱經典。

你的名字,經典全順;我的名字,福特新全順。今日,所有的經典終得圓滿,所有的榮耀終被典藏,所有的傳奇將被傳承!12月20日,“福特新全順,榮耀永傳承”2016福特全順榮耀慶典活動在江鈴小藍工廠成功舉辦,經典全順榮耀版順利入駐上海汽車博物館,50萬銷量傳奇銘記成永恆。

福特全順,這款在國內被稱為歐系輕客先驅者的商用車,以經典鑄造經典,給車主創造財富的同時,成為中國經濟騰飛的創造者和見證者。福特新全順,這款專業高效的多功能商用車,以傳承實現超越,提升車主用車價值的同時,推動物流業升級轉型,打造物流新價值。

披荊斬棘,福特全順終得圓滿

自1965年第一輛福特全順從英國蘭利工廠正式下線,歷經半個世紀的品質錘鍊,已經是福特旗下最暢銷的商用車品牌。2013年北美福特毅然用歐洲福特的全順接替了旗下商用車E-series,使全順真正成為了一款福特品牌全球車。憑藉全球熱銷800萬輛的傲人成績,福特全順成為迄今為止銷量最大的商用車品牌,甚至於全順的英文名字“Transit”成了歐洲同類車型的代名詞。

經過不斷磨礪,積澱實力,1976年福特全順獲得首個百萬用戶群,到2005年全順誕生40周年之際,第500萬輛全順從英國南安普頓工廠順利下線。從2010年銷量600萬輛到2013年6月第700萬輛在南昌小藍工廠下線,短短3年間全順銷量增加了100萬輛。之後又經過短短2年,2015年全順全球銷量順利突破800萬輛,成為引領全球的高端商用車領導品牌,全順實力,終得圓滿。

輝煌廿年,經典全順榮耀典藏

傳承歐洲福特純正血統,秉持歐洲品質創新研發的福特全順,進入國內短短的20年間,憑藉堅強品質、出色性能和多功能優勢,從0個車主發展到50萬個車主,實現從質變到量變的轉化,成為輕客市場的銷量“傳奇”。其實,福特全順進入國內20年以來,連續蟬聯歐系輕客銷量冠軍,是國內輕客市場的領軍品牌,收穫無數獎項,斬獲無數榮耀。不僅如此,福特全順還與2016年G20杭州峰會形成戰略合作關係,成為其指定用車,被授予“G20杭州峰會指定產品”榮譽證書。

此次全順榮耀慶典的最大亮點,即是全順系列明星車型—經典全順順利入駐上海汽車博物館,被該館永久收藏。據了解,上海汽車博物館對於展示車輛極其嚴格,收藏的均是具有代表性和貢獻意義的20餘個品牌的近百輛經典古董車。因此,經典全順能夠入駐上海汽車博物館,是對福特品牌和江鈴製造的高度認可,對福特全順的發展之路有着深遠意義。

繼往開來,福特新全順重鑄華本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

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

想買德系SUV的考慮一下是不是把錢留到2017年?

SUV陣營上汽大眾途觀L新款途觀的上市可以說是眾多合資SUV潛在買家“千呼萬喚始出來”的角色,繼全進口的tiguan公布售價之後,合資的國產途觀也緊隨着將在2017年年初上市。新平台,新外觀,跨越級別的車身尺寸是途觀L的最大武器,2791mm的軸距已然超越中型SUV的標杆車型漢蘭達,儘管是僅僅1mm的差距,但也足以看出途觀意圖跨級競爭的決心。

不知從什麼時候開始,“買車就買德系車”,成為了很多家庭和個人的購車第一主觀反應,也不得不承認德系車擁有着相當大的一波粉絲。

轎車陣營

一汽大眾CC

現款大眾CC也確實接近於全新換代的時間臨界點,雖然是每年都有小改動,但都是同一代車型,預計將在明年年中,據悉,一汽大眾CC的全新換代車型將在2017年上半年正式下線。

一汽大眾全新CC將基於MQB平台生產,從國外媒體所透露出來的渲染圖可見,溜背式的轎跑造型依然還是大眾CC的主體語言,全新一台CC設計方面借鑒了很多大眾sport Coupe GTE概念車的元素;根據之前已經得到的消息,新CC將在2017年實現國產。

寶馬1系三廂

三廂版本的國產寶馬1系可以說是寶馬品牌寄予厚望的一跨車型,該車亮相於今年十一月的廣州車展,預計將於2017年年初就正式上市。

1系三廂版本基於寶馬UKL前驅平台生產,儘管是前驅,但它依然是一款強調着運動感和操控性的緊湊型三廂轎車,至於未來價格和實際動態表現究竟如何,着實令人期待。

SUV陣營

上汽大眾途觀L

新款途觀的上市可以說是眾多合資SUV潛在買家“千呼萬喚始出來”的角色,繼全進口的tiguan公布售價之後,合資的國產途觀也緊隨着將在2017年年初上市。

新平台,新外觀,跨越級別的車身尺寸是途觀L的最大武器,2791mm的軸距已然超越中型SUV的標杆車型漢蘭達,儘管是僅僅1mm的差距,但也足以看出途觀意圖跨級競爭的決心。

上汽大眾Teramont

Teramont是上汽大眾進軍中大型SUV市場的強力車型,車身長度超過5米,定位比途觀還要高半個級別,儘管沒有正式發布,但不出意外的話,這款SUV將於明年上半年跟公眾見面。

至於價格,小編猜測這麼大的一台大眾,怎麼著也要四十多萬吧。

斯柯達科迪亞克

儘管斯柯達是一個捷克斯洛伐克的品牌,但畢竟是基於大眾的生產技術,所以把它歸類於德系車也不為過。

科迪亞克是斯柯達的第一台擁有七座的SUV,產品定位與途觀L一樣,生產平台也共享自大眾集團MQB橫置模塊箱平台,由於品牌定位原因,很多人猜測科迪亞克的定價應該會比較親民,承載了相當高關注度的科迪亞克,屆時上市價格希望不會令人失望才好。

本次介紹的車型僅是德系車的一部分在國內可以購買到合資的車型,價格在正式上市之後比全進口的德系車要便宜不少,相信在SUV極度火爆的中國市場,那幾款SUV的正式亮相才是廣大消費者更為關注的新聞,如果在今年尋覓不到心中所想,或許耐心等到明年,這些產品力進一步提升的新款車型上市之後,就可以“找到真愛”啦~本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

虎眼看中國中國SUV新標杆瑞虎7華南試駕會精彩大趴

0DVVT+CVT、1。5T+DCT三種動力組合。1。5T高性能渦輪增壓發動機搭配愛信6速手動變速箱,油耗低至百公里6。3L。瑞虎7后懸採用多連桿獨立懸架+副車架結構,令整車的操控樂趣與駕乘舒適性完美統一。科技配置及安全性方面,瑞虎7搭載了Cloudrive2。

2016年12月18日,奇瑞瑞虎7千人交車儀式暨試駕會在花園城市東莞隆重舉行。近三百輛嶄新的瑞虎7正式交付東莞車主。

12月18日對於奇瑞汽車來說,一個非常特別的日子,1999年12月18日奇瑞汽車第一台整車下線,時至今天—2016年12月18日,奇瑞公司在大家的陪伴下走過了整整19個年頭,以榮獲日內瓦國際車展設計大獎TX概念車為原型的瑞虎7,瑞虎7自9月20日上市以來,上市首月客戶訂單突破2萬台,受到廣大車友的一致追捧,其前瞻性動感造型,出色動力及精準操控,滿足多樣化駕控需求、科技智慧帶來便捷行車生活。瑞虎7的上市熱銷,標志著奇瑞汽車創下中國車企造車水平的又一新高度。

伴隨着動感的音樂,瑞虎7千人交車儀式正式開始,華南大區總經理翟小兵先生為今天活動致辭,一起見證瑞虎7千人交車活動的珍貴瞬間。

瑞虎7不僅是奇瑞歷史上最好的產品,更是中國品牌標杆產品之一。作為奇瑞進入戰略2.0后的首款SUV車型,瑞虎7是在“大奇瑞”國際化團隊協作下,融合捷豹路虎、觀致技術流程開發的專業SUV平台——奇瑞T1X平台開發的首款產品。以未來派前瞻美學的設計風格、自主領先,超越合資的整體性能,實現了品質與品位的完美統一。

在全廣東媒體和現場上百人的見證下,奇瑞汽車廠家領導將瑞虎7車鑰匙交給首批東莞瑞虎7車主,恭喜進300名瑞虎7車主“金”日提車。

瑞虎7上市熱銷,兼具顏值和實力 榮耀之作樹立中國SUV新標杆。

外形方面,轎跑風的“水流”車身設計、側身極速破浪式躍動腰線、超凡奪目的三叉戟式氙氣大燈配虎爪矩陣式LED尾燈,盡顯霸氣與精緻。

動力操控方面,瑞虎7搭載了1.5T+6MT、2.0DVVT+CVT、1.5T+DCT三種動力組合。1.5T高性能渦輪增壓發動機搭配愛信6速手動變速箱,油耗低至百公里6.3L。瑞虎7后懸採用多連桿獨立懸架+副車架結構,令整車的操控樂趣與駕乘舒適性完美統一。

科技配置及安全性方面,瑞虎7搭載了Cloudrive2.0智雲互聯行車系統,採用9英寸超大電容觸摸真彩高清屏,內置4G無線網卡,為全車用戶提供高速流暢的互聯體驗,打造全時在線生活。

瑞虎7採用6D縱梁框架和3R-Body的一體化籠式高強度車身結構,有效提高正側面撞擊的抗擊能力。全系標配ESp电子車身穩定系統、HHC上坡起步輔助系統、TpMS智能胎壓監測系統、前後8探頭雷達,高配車型還裝備了360°全景式影像系統、實時監測車身周邊障礙物,由內而外保護車內乘員的人身安全。

瑞虎7堪稱瑞虎家族“十年磨一劍”的蛻變之作,是一款具有划時代意義的精品SUV。市場對瑞虎7充滿了期待,瑞虎7上市熱銷也給出了完美的交代。瑞虎7在高速增長又競爭激烈的SUV市場中扮演了一名狠角色。已成為中國SUV市場新標杆,征服華南區SUV市場。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

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

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

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

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

【大廠面試06期】談一談你對Redis持久化的理解?

Redis持久化是面試中經常會問到的問題,這裏主要通過對以下幾個問題進行分析,幫助大家了解Redis持久化的實現原理。

1.Redis持久化是什麼?

2.Redis持久化有哪些策略?各自的實現原理是怎麼樣的?

3.Redis的數據恢復策略是怎麼樣的?

4.Redis持久化策略該如何進行選擇?

1.Redis持久化是什麼?

因為Redis是一個內存數據庫,數據保存在內存中,一旦發生關機或者重啟,內存中的數據都會丟失,所以為了能夠重啟時恢複數據,Redis提供了持久化的機制,正常運行期間根據策略生成持久化文件。在機器重啟后,可以根據根據持久化文件恢復內存中的數據。Redis還為我們提供了持久化的機制。(雖然有主從同步,主機掛掉之後,可以讓從節點成為主節點,但是如果整個機房都發生停電,那麼主節點和從節點內存中的數據都會丟失,所以這也是持久化存在的意義。)

2.Redis持久化有哪些策略?

Redis持久化的策略主要有AOF持久化,RDB持久化,混合持久化。這是我自己總結的一個圖:

AOF持久化

執行流程

AOF持久化主要是Redis在修改相關的命令后,將命令添加到aof_buf緩存區的末尾,然後在每次事件循環結束時,

根據appendfsync的配置:

  • appendfsync = always 每條修改命令都會更新到磁盤上的AOF文件, 最多只會丟失當前正在寫入的命令

  • appendfsync = everysec 每秒更新到磁盤上的AOF文件一次, 最多丟失2秒的數據(因為執行fsync命令刷盤也需要時間,下面會解釋)

  • appendfsync = no 不自動更新到磁盤上的AOF文件,由操作系統來決定何時刷盤(linux 貌似大部分默認是 30s)。可能會丟失刷盤之前的寫入數據。

(基於性能考慮一般生產環境的配置都是everysec)

(aof_buf是Redis中的SDS結構,可以理解為是一個字符串,只是對C語言的字符串做了一些優化,每次將新執行的更新命令添加到字符串末尾。)

怎麼防止AOF文件越來越大?

為了防止AOF文件越來越大,可以通過執行BGREWRITEAOF命令,會fork子進程出來,讀取當前數據庫的鍵值對信息,生成所需的寫命令,寫入新的AOF文件。在生成期間,父進程繼續正常處理請求,執行修改命令后,不僅會將命令寫入aof_buf緩衝區,還會寫入重寫aof_buf緩衝區。當新的AOF文件生成完畢后,子進程父進程發送信號,父進程將重寫aof_buf緩衝區的修改命令寫入新的AOF文件,寫入完畢后,對新的AOF文件進行改名,原子地(atomic)地替換舊的AOF文件。

什麼是AOF文件追加阻塞?

修改命令添加到aof_buf之後,如果配置是everysec那麼會每秒執行fsync操作,調用write寫入磁盤一次,但是如果硬盤負載過高,fsync操作可能會超過1s,Redis主線程持續高速向aof_buf寫入命令,硬盤的負載可能會越來越大,IO資源消耗更快,所以Redis的處理邏輯是會對比上次fsync成功的時間,如果超過2s,則主線程阻塞直到fsync同步完成,所以最多可能丟失2s的數據,而不是1s。

RDB持久化

RDB持久化指的是在滿足一定的觸發條件時(在一個的時間間隔內執行修改命令達到一定的數量,或者手動執行SAVE和BGSAVE命令),對這個時間點的數據庫所有鍵值對信息生成一個壓縮文件dump.rdb,然後將舊的刪除,進行替換。

執行流程

實現原理是fork一個子進程,然後對鍵值對進行遍歷,生成rdb文件,在生成過程中,父進程會繼續處理客戶端發送的請求,當父進程要對數據進行修改時,會對相關的內存頁進行拷貝,修改的是拷貝后的數據。(也就是COPY ON WRITE,寫時複製技術,就是當多個調用者同時請求同一個資源,如內存或磁盤上的數據存儲,他們會共用同一個指向資源的指針,指向相同的資源,只有當一個調用者試圖修改資源的內容時,系統才會真正複製一份專用副本給這個調用者,其他調用者還是使用最初的資源,在CopyOnWriteArrayList的實現中,也有用到,添加或者插入一個新元素時過程是,加鎖,對原數組進行複製,然後添加新元素,然後替代舊數組,解鎖)

	//CopyOnWriteArrayList的添加元素的方法
public boolean add(E e) {
  final ReentrantLock lock = this.lock;
  lock.lock();
  try {
    Object[] elements = getArray();
    int len = elements.length;
    Object[] newElements = Arrays.copyOf(elements, len + 1);
    newElements[len] = e;
    setArray(newElements);
    return true;
  } finally {
    lock.unlock();
  }
}

混合持久化(Redis4.0+)

執行流程

混合持久化同樣也是通過bgrewriteaof命令完成的,不同的是當開啟混合持久化時,fork出的子進程先將當前內存中的鍵值對信息全量的以RDB方式寫入aof文件,然後在將重寫緩衝區的增量命令以AOF方式寫入到文件,寫入完成后通知主進程更新統計信息,並將新的含有RDB格式和AOF格式的AOF文件替換舊的的AOF文件。簡單的說:新的AOF文件前半段是RDB格式的全量數據後半段是AOF格式的增量數據,如下圖:

3.Redis的數據恢復策略是怎麼樣的?

1.如果配置了混合持久化,那麼根據混合持久化文件進行恢複數據。(Redis4.0+)

2.只配置 AOF ,重啟時加載 AOF 文件恢複數據。

3.同時配置了 RDB 和 AOF ,啟動時只加載 AOF文件恢複數據,如果AOF文件損壞,那麼根據RDB文件恢複數據。

4.只配置 RDB,啟動時加載RDB持久化文件恢複數據。

4.Redis持久化策略該如何進行選擇?

(因為混合持久化是Redis 4.0之後支持的,目前一般生成環境使用的Redis版本可能都還較低,所以這裏的策略選擇主要是針對AOF持久和RDB持久化進行技術選型。)

以下是幾種持久化方案選擇的場景:

1.不需要考慮數據丟失的情況

那麼不需要考慮持久化。

2.單機實例情況下

可以接受丟失十幾分鐘及更長時間的數據,可以選擇RDB持久化,對性能影響小,如果只能接受秒級的數據丟失,只能選擇AOF持久化。

3.在主從環境下

因為主服務器在執行修改命令后,會將命令發送給從服務器,從服務進行執行后,與主服務器保持數據同步,實現數據熱備份,在master宕掉後繼續提供服務。同時也可以進行讀寫分離,分擔Redis的讀請求。

那麼在從服務器進行數據熱備份的情況下,是否還需要持久化呢?

需要持久化,因為不進行持久化,主服務器,從服務器同時出現故障時,會導致數據丟失。(例如:機房全部機器斷電)。如果系統中有自動拉起機制(即檢測到服務停止后重啟該服務)將master自動重啟,由於沒有持久化文件,那麼master重啟后數據是空的,slave同步數據也變成了空的。應盡量避免“自動拉起機制”和“不做持久化”同時出現。

所以一般可以採用以下方案:

主服務器不開啟持久化,使得主服務器性能更好。

從服務器開啟AOF持久化,關閉RDB持久化,並且定時對AOF文件進行備份,以及在凌晨執行bgaofrewrite命令來進行AOF文件重寫,減小AOF文件大小。(當然如果對數據丟失容忍度高也可以開啟RDB持久化,關閉AOF持久化)

4.異地災備

一般性的故障(停電,關機)不會影響到磁盤,但是一些災難性的故障(地震,洪水)會影響到磁盤,所以需要定時把單機上或從服務器上的AOF文件,RDB文件備份到其他地區的機房。

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

【其他文章推薦】

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

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

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

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

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

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

自動網絡搜索(NAS)在語義分割上的應用(一)

【摘要】本文簡單介紹了NAS的發展現況和在語義分割中的應用,並且詳細解讀了兩篇流行的work:DARTS和Auto-DeepLab。

自動網絡搜索

多數神經網絡結構都是基於一些成熟的backbone,如ResNet, MobileNet,稍作改進構建而成來完成不同任務。正因如此,深度神經網絡總被詬病為black-box,因為hyparameter是基於實驗求得而並非通過嚴謹的數學推導。所以,很多DNN研究人員將大量時間花在修改模型和實驗“調參”上面,而忽略novelty本身。許多教授戲稱這種現象為“graduate student descent”。

近兩年,學術界興起了“自動網絡搜索”取代人工設計網絡結構。2016年,Google Brain公開了他們的研究成果NASNet【1】,這是第一個用自動網絡搜索Neural Architecture Seach (NAS)完成的神經網絡,為深度學習打開了新局面。NASNet是由一系列operation(如depth separable conv, max pooling等)疊加而成。至於怎樣選擇operation,作者用強化學習(RL)的方法,用一個controller網絡隨機組合operations生成模塊,通過評估選擇最優模塊組成網絡結構。Google提供的雲上服務Cloud AutoML正是基於NAS方法,根據用戶上傳的數據自動搜索神經網絡再將結果輸出。迄今為止,國內外很多工業界和學術界的AI Lab都有NAS相關工作:Google Brain提出了NasNet, MNasNet和Nas-FPN,Google提出MobileNetv3,Auto-DeepLab和Dense Prediction Cell(DPC);MIT Han Song團隊提出ProxylessNet;Facebook提出FbNet;Baidu提出SETN;騰訊提出FPNAS;小米提出FairNAS;京東AI提出CAS;華為諾亞有提出P-DARTS等。通過各大實驗室對NAS的研究成果,似乎可以看出自動網絡搜索將會成為趨勢。

接下來我們簡單介紹一下NAS的流程,詳細內容可參考【2】。NAS的架構主要包含三部分:搜索空間Search Space,搜索策略Search Strategy和評估機制Performance Estimation(如下圖)。

  • 首先搜索空間會定義一些模塊(cell),operations(例如dilated conv 3×3),和宏觀網絡結構。cell是由若干個operation組合而成。通常來說,NAS會自動搜索出一些可以重複的cell,將cell按照設定的宏觀網絡結構堆疊起來形成network。Zoph et al.提出了兩種cell,normal cell和reduction cell【3】。Reduction cell有改變spatial resolution的功能。這兩種cell在現有的算法中最為常見。此外還有來自CAS網絡【4】的multi-scale cell,作用和ASPP類似可用作decoder。至於對宏觀網絡結構的設定,早期的工作會採用chained-structured,或者帶分支結構multi-branch例如ResNet和DenseNet帶有skip和shortcut。
  • 搜索策略在NAS中至關重要,因為它決定了下一步要選擇哪一個cell或者operation組成網絡結構。搜索的目的就是讓現有的網絡結構在unseen data上得到最好的效果。如前文提到,第一篇工作NASNet中用到了強化學習作為搜索策略。雖然實驗結果尚可,但強化學習耗時過長並且需要大量GPU資源。NASNet就是用幾百個GPU搜索了幾天才完成,這對於資源少的研究人員來說不可能完成。繼NASNet之後早期的工作多是基於RL完成搜索。隨着其他方法的研究,如random search, Bayesian optimization, Gradient-based, evolution algorithms,搜索時間在下降並且GPU使用量也在減少,使得NAS得以普及。感謝https://github.com/D-X-Y/Awesome-NAS 提供了搜索策略的調研。
  • 由於一個神經網絡通常有幾十甚至上百層,結構也很複雜,一個及時並有效的反饋對於減少搜索時間很有幫助。最簡單的方法是把訓練數據分成兩部分,train data用來訓練網絡結構,validation data用來評估當前搜索到的網絡結構。一般來說,一個好的評估機制要兼具速度和準確度,舉個例子,train data少則網絡結構訓練不到位,train data多則搜索時間過長。來自Freiburg大學的Elsken團隊發表了關於NAS的調研,裏面對於評估方法講的很詳細,請挪步【2】。

自動網絡搜索在語義分割上的應用

相比於目標檢測,語義分割可以更好的解析圖像,因為圖像上的每一個pixel都會被分類。所以語義分割可以完成目標檢測無法完成的任務,如自動駕駛的全景分割,醫療圖像診斷,衛星圖分割,背景摳圖和AR換裝等。過去的五年繼FCN之後,很多神經網絡在公開數據集上都有不錯的成績。隨着AI產品落地,對神經網絡性能的要求更為嚴格:在保證accuracy的前提下,在有限制的硬件資源運行並且提升inference速度。這為設計神經網絡增加了很大難度。所以NAS是一個很好的選擇,它可以避免通過大量調參實驗來決定最優網絡結構。在語義分割之前,NAS在圖像分類和目標檢測上均有成功的應用。我們總結了近兩年比較流行的NAS在語義分割上的工作。多數的語義分割的網絡都是encoder-decoder結構,在這裏我們對比了實際搜索的部分,搜索性能耗時和方法。由於每一個work都在不同實驗環境下進行,所以實驗結果沒有直接進行對比。

(1)第一篇將NAS應用於語義分割的工作來自Google DeepLab團隊,作者是Liang-Chieh Chen等一眾大佬。DeepLabv3+的準確度已經達到一定高度,繼而DeepLab團隊將研究方向轉移到了NAS上面。基於和DeepLab同樣的encoder-decoder結構, 在【5】工作中,作者將encoder結構固定,試圖搜索出一個更小的 ASPP,用到的搜索策略是random search。在Cityscapes達到82.7% mIoU,在PASCAL VOC 12上達到87.9%,結果尚可但搜索時間過慢,用370個GPU搜索一周才得到網絡,這也限制了該方法的普及。

(2)同樣來自DeepLab團隊,Auto-DeepLab【6】相比之前的工作在搜索效率上有顯著的提升。這都歸功於它gradient-based的搜索策略DARTS【9】。與上一篇相反,Auto-DeepLab的目標是搜索encoder,而decoder則採用ASPP。從實驗結果來看,Auto-DeepLab的準確度和DeepLabv3+相差不多,但是FLOPs和參數數量卻是DeepLabv3+的一半。後文我們會具體介紹DARTS和Auto-DeepLab。

(3)Customizable Architecture Search (CAS)【4】是來自京東AI Lab的工作。在搜索空間定義了三種cell,分別是normal cell,reduction cell和新提出的multi-scale cell。其中multi-scale cell是ASPP 的功能類似,所以CAS的搜索區間是針對全部網絡。在搜索策略上依然採用DARTS。值得一提的是,在搜索網絡的時候,CAS不僅考慮accuracy,還考慮了每個模塊的GPU time,CPU time,FLOPs和參數數量。這些屬性對於實時任務至關重要。所以CAS在Cityscapes上的準確度是72.3%,雖然沒有很高,但是在TitanXP GPU的速度fps達到108。

(4)來自Adeleide大學的Vladimir Nekrasov團隊在light weight模型上面做了大量工作,而最近也將研究重心轉到了NAS上面。在【7】中,作者採用RL的搜索策略搜索decoder。眾所周知RL非常耗時,所以作者採用知識蒸餾策略和Polyak Averaging方法結合提升搜索速度,而這正是本文的major contribution。

(5)最後一篇來自商湯發布在Artix的文章,採用圖神經網絡GCN作為搜索策略,試圖尋找cell之間最優的連接方式【8】。和CAS類似,在本文中每個模塊的性能如latency也在搜索評估時考慮進去。在Cityscape上GAS達到73.3%mIoU,在TitanXP GPU的速度是102fps。

簡單總結一下上述工作,我們可以發現NAS在語義分割上的應用還算成功,並且很多團隊已經在NAS上進行研究探索。尤其在DARTS類似的高效搜索策略提出后,個人研究者和小團隊也可以構建自己的NAS網絡,而不受制於GPU資源。

DARTS: Differentiable Architecture Search

通常來說搜索空間是一個離散的空間,在DARTS中【9】,作者將搜索空間定義成一個連續空間,這樣一來搜索到的每一個cell都是可導的,可以用stochastic gradient descent來優化。所以DARTS相比RL並不需要大量GPU資源和搜索時間。在實驗中,DARTS成功用在了CNN模型的圖像分類和RNN模型的language modelling任務上。感恩作者提供了源代碼 https://github.com/quark0/darts ,它可以為我們搭建自己NAS模型提供很好的基礎。

正如前文中提到,多數的NAS網絡會搜索不同類型的可重複的cell,然後將cell連接起來構成神經網絡。這個cell通常用directed acyclic graph(DAG)表示。一個DAG cell包含N個有順序的node,每一個node可以看成在它前面所有node的結合(就像feature map一樣是一個latent representation)。從node i 到node j的連接是某一種operation記作o(i,j). 每一個cell都有兩個input node和一個output node。下圖中是一個DARTS展示的reduction cell。我們可以看到cell k 有兩個input nodes 分別是c_{k-1}和c_{k-2} (來自cell k-1和cell k-2的output node),cell k包含了4個immediate nodes和一個output node c_{k} 。o(i,j)是邊緣上的max_pool_3x3,max_pool等。理論上從一個node到相鄰的node可以有很多種operation,所以搜索最優網絡結構也可以看成是選擇每一個邊上最佳的operation。

DARTS將每對node之間的每一個operation都賦予一個weight,最優解可以用softmax求得,也就是說有最大probability的path代表最優operation,這也是DARTS的核心部分。DARTS在搜索空間中定義了兩種cell,reduction cell和normal cell。宏觀網絡結構是固定的,作者採用了簡單的chained-structured,將reduction cell放在了網絡結構的1/3和2/3處。所以說在搜索的過程中,cell內部不斷更新而宏觀結構沒有變化。我們定義operation的參數為W,將cell中operation的weight記為Alpha。根據論文和source code,我們總結了DARTS的搜索流程如下圖。

網絡搜索的第一步是對模型結構,optimizer,loss進行初始化。文中定義了幾種operation,代碼中的定義在operation.OPS, 兩種cell在代碼中的定義是genotypes.PROMITIVES. 參數Alpha在代碼中定義為arch_parameters()={alphas.normal, alphas.reduce}. 在搜索過程中,train data被分成兩部分,train patch用來訓練網絡參數W而validation patch用來評估搜索到的網絡結構。在代碼中,搜索過程的核心部分在architect.step()。網絡搜索的目標函數就是讓validation在現有網絡的loss最小,文章中公式(3)給出了objective:

為了減少搜索時間,每一輪只用一個training patch去更新參數W計算train loss。在計算Alpha的時候涉及到二階求導,稍微複雜一點,但是論文和代碼都給了詳細解釋,這裏不贅述,代碼中architect._hession_vector_product是求二階導的實現。在更新W和Alpha之後,最優operation通過softmax來計算。文中保留了top-k probability的operation。W和Alpha不斷計算更新直到搜索過程結束。

文中進行了大量實驗,我們這裏只介紹一下在CIFAR-10數據上面進行的圖像分類任務。作者將DARTS與傳統人工設計的網絡DenseNet,和幾個其他常見的NAS網絡進行對比,如AmoeNet和ENet都是常被提及的。DARTS在準確度上優於其他所有算法,並且在搜索速度上明顯比RL快很多。由於結構簡單效果好,而且不需要大量GPU和搜索時間,DARTS已經被大量引用。

Auto-DeepLab: Hierarchical Neural Architecture Search for Semantic Image Segmentation

基於DARTS的結構,Google DeepLab團隊提出了Auto-DeepLab並發表在2019年CVPR上。在tensorflow deeplab官網上公布了nas backbone並且給出了可以訓練的模型結構,但是搜索過程並沒有公開。於是我們訓練了給出的nas網絡結構,在沒有任何pre-training的情況下與deeplab v3+進行對比。代碼參考 https://github.com/tensorflow/models/tree/master/research/deeplab 。

在DARTS中,宏觀網絡結構是提前定義的,而在Auto-DeepLab中宏觀網絡結構也是搜索的一部分。繼承自DeepLab v3+的encoder-decoder結構,Auto-DeepLab的目的是搜索Encoder代替現有的xception65,MobileNet等backbone,decoder採用ASPP。在搜索空間中定義了reduction cell,normal cell和一些operation。Reduction cell用來改變spatial resolution,使其變大兩倍,或不變,或變小兩倍。為了保證feature map的精度,Auto-DeepLab規定最多downsampling 32倍 (s=32)。下圖定義了宏觀網絡結構(左)和cell內部的結構(右)。

Auto-DeepLab定義了12個cell,而上圖(左)中前面兩個白色的node是固定的兩層為了縮小spatial resolution。如圖左灰色箭頭所示,正式搜索之後,每一個cell的位置都有多種cell類型可以選擇:可以來自於當前cell相同的spatial resolution的cell,也可以是比當前cell的spatial resolution大一倍或小一倍的cell。作者將這些空間路徑(灰色箭頭表示的路徑)也賦予一個weight,記作Beta。如圖右,每一個cell的輸出都是由相鄰spatial resolution的cell結合而成,而Beta的值可以理解成不同路徑的probability。為了更直觀,我們把圖右的三個cell分別用藍色,黃色和綠色標註,對應圖左的三個cell。與DARTS類似,我們將operation的parameters記作W,將cell內部operation的權重記作Alpha。所以搜索最優網絡結構,即迭代計算並更新W,Alpha和Beta。文中給出每一個cell的實際輸出為:

從上面公式可以看出,W和{Alpha,Beta}要分別計算和更新。所有的weight都是非負數。Alpha的計算方式依然是ArgMax,而計算Beta用了經典的貪心算法Viterbi算法。下圖給出的宏觀網絡結構是基於Cityscapes搜索到的結果,對應代碼中的backbone是[0,0,0,1,2,1,2,2,3,3,2,1], 数字代表downsample倍數。在模型中,每一個cell中的node由兩個路徑組成,如圖右。

文中用了三組開源數據PASCAL VOC 12, Cityscapes和ADE20k做了對比實驗。具體實驗參數設置和對比算法在論文中有詳細說明,這裏只對比和Deeplab v3+。Cityscapes訓練數據尺寸是[769×769],而PASCAL VOC 12和ADE20k訓練數據尺寸是[513×513]。一般來說,Auto-DeepLab和DeepLabv3+準確度相差無幾,但是速度上要快2.33倍,並且Auto-DeepLab可以從零開始訓練。

除了文中給出的實驗結果以外,我們在PASCAL VOC 12數據上從零開始訓練了Auto-DeepLab,用代碼中給出的模型結構,並且與DeepLabv3+(xception65)進行結果對比。但是並不是所有結果都能復現,分析原因大概是這樣:首先,上文中給出的模型結構是用Cityscapes數據集搜索得到,也許在PASCAL VOC 12上並不是最優解;其次沒有用ImageNet做pre-training,訓練環境也不同。我們在下面表格中對比了FLOPs, 參數數量, 在K80 GPU上面的fps和mIoU。

下圖中直觀對比了ground truth(第二列),deeplabv3+(第三列)和Auto-DeepLab-S(第四列)的分割結果。與上面的mIoU一致,DeepLabv3+的分割結果要比Auto-DeepLab更精準一些,尤其是在邊緣。對於簡單的圖像案例,兩者分割結果相差無幾,但是在較難的情況下,Auto-DeepLab會有很大誤差(在第三個案例中,Auto-DeepLab將女孩識別成狗)。

總結

本文簡單介紹了NAS的發展現況和在語義分割中的應用,並且詳細解讀了兩篇流行的work:DARTS和Auto-DeepLab。從整體實驗結果來看,還不能看出NAS的方法比傳統的模型有壓倒性優勢,尤其在準確度上。但是NAS給深度學習注入了新鮮的血液,為研究者提供了一種新的思路,並且還有很大的提升空間和待開發領域。也許人工設計網絡結構將會被自動網絡搜索取代。

翻譯或有誤差,請參考原文https://medium.com/@majingting2014/neural-architecture-search-on-semantic-segmentation-1801ee48d6c4

 

對這塊比較關注的同學可以移步繼續閱讀《自動網絡搜索(NAS)在語義分割上的應用(二)》

References

[1] Zoph, Barret, and Quoc V. Le. “Neural architecture search with reinforcement learning.” The International Conference on Learning Representations (ICLR) (2017)

[2] Elsken, Thomas, Jan Hendrik Metzen, and Frank Hutter. “Neural Architecture Search: A Survey.” Journal of Machine Learning Research 20.55 (2019): 1-21.

[3] Zoph, Barret, et al. “Learning transferable architectures for scalable image recognition.” Proceedings of the IEEE conference on computer vision and pattern recognition (CVPR). 2018.

[4] Zhang, Yiheng, et al. “Customizable Architecture Search for Semantic Segmentation.” Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR). 2019.

[5] Chen, Liang-Chieh, et al. “Searching for efficient multi-scale architectures for dense image prediction.” Advances in Neural Information Processing Systems (NIPS). 2018.

[6] Liu, Chenxi, et al. “Auto-deeplab: Hierarchical neural architecture search for semantic image segmentation.” Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR). 2019.

[7] Nekrasov, Vladimir, et al. “Fast neural architecture search of compact semantic segmentation models via auxiliary cells.” Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR). 2019.

[8] Lin, Peiwen, et al. “Graph-guided Architecture Search for Real-time Semantic Segmentation.” arXiv preprint arXiv:1909.06793 (2019).

[9] Liu, Hanxiao, Karen Simonyan, and Yiming Yang. “Darts: Differentiable architecture search.” The International Conference on Learning Representations (ICLR) (2019).

點擊關注,第一時間了解華為雲新鮮技術~

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

※台北網頁設計公司全省服務真心推薦

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

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

Jmeter(八) – 從入門到精通 – JMeter配置元件(詳解教程)

1.簡介

JMeter配置元件可以用來初始化默認值和變量,讀取文件數據,設置公共請求參數,賦予變量值等,以便後續採樣器使用。將在其作用域的初始化階段處理。配置元件(Config Element)提供對靜態數據配置的支持,可以為取樣器設置默認值和變量。

首先我們來看一下JMeter的配置元件,路徑:添加-配置元件;我們可以清楚地看到JMeter5中共有19個配置元件,如下圖所示:

如果上圖您看得不是很清楚的話,宏哥總結了一個思維導圖,關於JMeter5的配置元件類型,如下圖所示:

 通過以上的了解,我們對配置元件有了一個大致的了解和認識。下面宏哥就給小夥伴或則童鞋們分享講解一些通常在工作中會用到的配置元件。

 2.常用配置元件詳解

  這一小節,宏哥就由上而下地詳細地講解一下常用的配置元件。

2.1CSV Data Set Config

1、我們先來看看這個CSV Data Set Config長得是啥樣子,如下圖所示:

 2、參數詳解及說明,如下錶所示:

參 數 描 述 是否必填
Name 腳本中显示的這個元件的描述性名稱 是
Filename 待讀取文件的名稱。可以寫入絕對路徑,也可以寫入相對路徑(相對於bin目錄),如果直接寫文件名,則該文件要放在bin目錄中。對於分佈式測試,主機和遠程機中相應目錄下應該有相同的CSV文件 是
File Encoding 文件讀取時的編碼格式,不填則使用操作系統的編碼格式 否
Ignore first line 是否忽略首行,如果csv文件中沒有表頭,則選擇false 是
Variable Names 變量名列表,多個變量名之間必須用分隔符分隔。如果該項為空,則文件首行會被讀取並解析為列名列表 否
Delimiter 參數分隔符,將一行數據分隔成多個變量,默認為逗號,也可以使用“\t”。如果一行數據分隔后的值比Vairable Names中定義的變量少,這些變量將保留以前的值(如果有值的話) 是
Allow quoted data? 是否允許變量使用雙引號,允許的話,變量將可以括在雙引號內,並且這些變量名可以包含分隔符 否
Recycle on EOF? 是否循環讀取csv文件內容,達到文件結尾后,是否從文件開始循環重新讀取;默認為 true 是
Stop thread on EOF? 是否循環讀取csv文件內容,達到文件結尾后,線程是否該終止;默認為 true 是
Recycle on EOF? 當Recycle on EOF為False時,停止線程,當Recycle on EOF為True時,此項無意義,默認為 false 是
Sharing mode 1、All threads(默認):一個線程組內,各個線程(用戶)唯一順序取值;2、current thread:一個線程組內,各個線程(用戶)各自順序取值;3、線程組各自獨立,但每個線程組內各個線程(用戶)唯一順序取值; 是

 3、Recycle on EOF 和Stop thread on EOF的關係:

當Recycle on EOF 選擇true時,Stop thread on EOF選擇true和false無任何意義,因為既然前面已經設置了文件是不停的循環讀取,後面的控制stop就相當於失效;
當Recycle on EOF 選擇false時,Stop thread on EOF選擇true,則當線程數超過文件里的參數的個數時,實際請求數為參數的個數;
當Recycle on EOF 選擇false時,Stop thread on EOF選擇flase,當線程數超過文件里參數的個數時,實際請求次數為線程數,但當線程數超過參數次數時,由於沒有參數,所以結果仍然是失敗的。
4、Sharing mode:如果希望每個線程擁有自己獨立的值集合,那麼就需要創建一系列數據文件,為每個線程準備一個數據文件,如test1.csv、test2.csv等,使用文件名test${__threadNum}.csv,並將“sharing mode”設置為”Current thread”

All threads:文件在所有線程間共享。

Identifier:所有線程共享相同的標識,共享相同的文件。如有4個線程組,測試人員可以使用一個通用ID,以便在兩個或多個線程組之間共享文件。

Current thread:每個文件會針對每個線程單獨打開。

Current thread group:每個文件會針對每個線程組打開一次。

2.2HTTP Header Manager

支持用戶添加或者重寫HTTP請求頭。JMeter支持多個信息頭管理器。多個信息頭條目合併成一個信息頭列表,跟隨http請求一併提交到服務端。

注意:敲黑板,敲腦殼!!!

(1)當有多個信息頭管理器,且不同的管理器內有名稱相同的信息頭條目存在時,順序靠前的管理器的信息頭條目會覆蓋後面的;

(2)當只有一個信息頭管理器,但管理器內有名稱相同的信息頭條目時,會同時生效;

 1、我們先來看看這個HTTP Header Manager長得是啥樣子,如下圖所示:

 2、參數詳解及說明,如下錶所示:

參數 描述 是否必填
Name 請求頭的名稱,比如Content-Type 否
Value 請求頭的值,比如application/json 否

3、常用請求頭,這些一般可以抓包和在瀏覽器中查到,如下錶所示:

2.3HTTP Cookie Manager

主要有兩個功能:

一個功能是:像web瀏覽器一樣存儲和發送Cookie。如果有一個HTTP請求和相應里包含Cookie,Cookie管理器會自動存儲Cookie,那麼接下來針對特定web站點的所有請求中使用該Cookie。可在結果樹中查看。

接收到的Cookie可以被保存為變量,須定義屬性”CookieManager.save.cookie=true”。另外,在被存儲前Cookie名稱會加上前綴“COOKIE_”,要恢復早前處理方式,則定義屬性”CookieManager.name.prefix=”(一個或多個空格)。

如果啟動了該功能,那麼名稱為TEST的Cookie,可以通過${COOKIE_TEST}加以引用。手動為Cookie管理器添加一個Cookie(為所有JMeter線程所共享)。

1、我們先來看看這個HTTP Cookie Manager長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 樹中显示此元件描述的名稱 是
Comments 註釋 否
Clear cookie each Iteration  每次線程組運行前,都會清楚cookie,但是如果是手動添加的cookie,不會被清除 否
Cookie Policy 選擇Cookie的管理策略,建議選擇兼容性,兼容性強 是
User Define cookie 用戶自定義cookie 否

 

2.4HTTP Cache Manager

  被用來為其作用域內的HTTP請求提供緩存功能,如果“Use Cache-Control/Expires header When …”選中,那麼會根據當前時間來選擇,如果請求是”GET”,而時間指向未來,那麼採樣器就會立即返回,而無須從遠程服務器請求URL,這樣是為了模擬瀏覽器的操作,請注意Cache-Control頭必須是“pulic”的,並且只有”max-age”終結選項會被處理,如果請求文檔自從其被緩存以來沒有發生任何改變,那麼響應包體就會為空。

1、我們先來看看這個HTTP Cache Manager長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 樹中显示此元件的描述性名稱  是
Comments 註釋 否
Clear Cache each iteration 如果選擇此選項,則在線程開始時清除緩存。 否
Use Cache 如果選擇此選項,則在線程開始時使用緩存。 否
Max Number 如果選擇此選項,則在線程開始時最大緩存。 否

 

2.5HTTP Request Defaults

在實際測試計劃中,我們經常會碰到Http Sampler請求有較多的參數與配置會重複,每一個Http Sampler都單獨設置的話比較浪費時間和精力,為了節省工作量,JMeter提供了HTTP Request Defaults元件,用來把這些重複的部分封裝起來,一次設置多次使用。可以設定一些缺省值,假設有10個請求,訪問域名和端口都是一樣的,那HTTP請求中就不再需要單獨配置了,比較方便(增加腳本的移植性)。

這個元件可以設置HTTP請求控制器使用的默認值。例如,圖中【服務器名稱或IP】項目內填入了【example.com】,後面的HTTP請求如果IP也是example.com的話,那麼只要將【服務器名稱或IP】留空,那麼這個字段將自動繼承HTTP請求默認值中的值。其他諸如【協議】、【端口號】、【路徑】等同此。

1、我們先來看看這個HTTP Request Defaults長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 用作標識一個取樣器,建議使用一個見名知義的名稱 是
Comments 註釋 否
Protocol 協議,向目標服務器發送HTTP請求時的協議,可以是http或者是Https  
IP HTTP請求發送的目標服務器名稱或者IP地址  
Port Number 目標服務器端口  
Path 目標URL路徑(不包括服務器地址和端口)  
Content encdoing 內容的編碼方式  
Parameter 參數  
body data 參數  

 

2.6Counter

計數器,顧名思義就是在測試執行過程中會記錄迭代次數。可以在線程組任何位置創建,允許用戶配置起點、最大值和增量。配置后,計數器將從起點循環到最大值,然後重新開始,直到線程結束。允許用戶創建一個計數器,可在線程組中任何地方被引用。

1、我們先來看看這個Counter長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 控制器名稱,可以隨意設置 否
Comments 註釋,可以隨意設置 否
Starting value 啟動,記錄數量起始值  
Increment 遞增,記錄迭代次數步長,1后是2,步長就是1  
Maximum value 記錄的最大值  
Number format 計算器格式,可以是数字,例如000000(6位長度,000,000(6位長度,3位間隔開);字符加数字,例如CUST_000000(字符加6位数字 )  
Exported Variable Name 引用變量名稱,記數器記錄的值可以存入的此引用名(變量),可供其他元件調用  
Track counter independently for each user 與每位用戶獨立的跟蹤計數器,每個線程都有自己的計數器,相互不干擾  
Reset counter on each Thread Group Iteration 每次迭代復原計數器  

 

2.7DNS Cache Manager

1、我們先來看看這個DNS Cache Manager長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 樹中显示此元件的描述性名稱  
Comments 註釋  
Clear cache each iter 清除每個迭代的緩存,如果選擇此選項,則每次啟動新迭代時,都會清除每個線程的DNS緩存。  
Use System DNS resolver 使用系統DNS解析器;將使用系統DNS解析器。為了正確工作,請編輯 $ JAVA_HOME / jre / lib / security / java.security並添加networkaddress.cache.ttl = 0  
Use custom DNS resolver 使用自定義DNS解析器;將使用自定義DNS解析器(來自dnsjava庫)。  

 

2.8FTP Request Defaults

被用於設置FTP請求的默認值

1、我們先來看看這個FTP Request Defaults長得是啥樣子,如下圖所示:

2.9HTTP Authorization Manager

HTTP認證是一種安全機制,在客戶端、瀏覽器或者程序向服務器發起請求時,需要提供用戶名和密碼且驗證通過(拿到憑證)才能繼續發起交互。

1、我們先來看看這個HTTP Authorization Manager長得是啥樣子,如下圖所示: 

2.10JDBC Connection Configuration

1、我們先來看看這個JDBC Connection COnfiguration長得是啥樣子,如下圖所示: 

 2、關於JDBC Connection COnfiguration參數詳細說明,可以參考宏哥的另一篇文章是非常詳細的:傳送門。

2.11Java Request Defaults

1、我們先來看看這個Java Request Defaults長得是啥樣子,如下圖所示: 

2.12Keystore Configuration

1、我們先來看看這個Keystore Configuration長得是啥樣子,如下圖所示: 

2、參數詳細說明,如下錶所示:

參數 描述 是否必填
Name 樹中显示此元件的描述性名稱。可以默認 是
Comments 註釋 否
Preload 預載,是否預加載秘鑰庫,設置為true通常是最佳選擇 是
Variable name holding certificate alias 變量名稱,將包含用於客戶端證書身份驗證的別名。例如,將從CSV數據集中填充變量值。在屏幕截圖中,“ certificat_ssl”也將是CSV數據集中的變量。 否
Alias Start index 從0開始在Keystore中使用的第一個鍵的索引。 是
Alias End index 基於0的密鑰庫中要使用的最後一個密鑰的索引。使用“變量名稱持有證書別名”時,請確保其足夠大,以便在啟動時加載所有密鑰。 是

 2.13LDAP Extended Request Defaults

1、我們先來看看這個LDAP Extended Request Defaults長得是啥樣子,如下圖所示: 

2.14LDAP Request Defaults

1、我們先來看看這個LDAP Request Defaults長得是啥樣子,如下圖所示: 

2.15Login Config Element

1、我們先來看看這個Login Config Element長得是啥樣子,如下圖所示: 

2.16Random Variable

1、我們先來看看這個Random Variable長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示:  

參數 描述 是否必填
Name 樹中显示的此元件的描述性名稱。  
Comments 註釋  
Variable Name 變量名,存儲隨機字符串的變量的名稱。  
Output Format 格式化字符串,要使用的java.text.DecimalFormat格式字符串。例如,“ 000”將生成至少3位数字,或者“ USER_000”將生成USER_nnn形式的輸出。如果未指定,則默認為使用Long.toString()生成数字。  
Minimum Value 最小值;生成的隨機數的最小值(長整數)。  
Maximum Value 最大值;生成的隨機數的最大值(長整數)。  
Seed for Random function 隨機種子,隨機數生成器的種子。默認值為當前時間,以毫秒為單位。如果在“將每個線程”設置為true的情況下使用相同的種子值,則與“ 隨機” 類一樣,您將為earch線程獲得相同的值  
Per Thread(User)? 每個線程,如果為False,則在線程組中的所有線程之間共享生成器。如果為True,則每個線程都有自己的隨機生成器。  

2.17Simple Config Element

1、我們先來看看這個Simple Config Element長得是啥樣子,如下圖所示: 

2、參數詳細說明,如下錶所示:  

參數 描述 是否必填
Name 樹中显示此元件的描述名稱 是
Comments 註釋 是
Name 參數名稱  
Value 參數值  

2.18TCP Sampler Config

TCP採樣器配置為TCP採樣器提供默認數據

1、我們先來看看這個TCP Sampler Config長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示: 

參數 描述 是否必填
Name 樹中显示此元件的描述性名稱  
Comments 註釋  
TCPClient classname TCPClient類的名稱,默認屬性tcp.handler,使TCPClientImpl失敗  
Sever Name or IP TCP服務器的名稱或者IP  
Port Number 使用的端口  
Re-use connection 重用連接,如果選擇,則連接保持打開狀態,否則,在讀取數據后將其關閉  
Close connection 關閉連接,如果選擇此項,則在運行採樣器后將連接關閉  
Set NoDelay 設置節點布局,應該設置nodelay  
SO_LINGER 創建套接字時,以指定的延遲時間(以秒為單位)啟用/禁用SO_LINGER。如果將“SO_LINGER”值設置為0,則則可以防止大量套接字處於TIME_WAT 的狀態  
End of line byte value 判斷行結束的byte值,如果你指定的值大於127或者小於-128,則會跳過EOL檢測。比如服務器端返回的字符串都是以回車符結尾,那麼我們可以將該選項設置成10。  
Text to send 文字發送,要發送的文字  
Connect 連接超時(毫秒。0禁用)  
Response 響應超時(毫秒。0禁用)  

2.19User Defined Variables

如果您有多個線程組,請確保對不同的值使用不同的名稱,因為UDV在線程組之間共享。同樣,這些變量在處理完元素之後才可用,因此您不能引用在同一元素中定義的變量。您可以引用在早期UDV或測試計劃中定義的變量。

1、我們先來看看這個User Defined Variable長得是啥樣子,如下圖所示:

2、參數詳細說明,如下錶所示: 

參數 描述 是否必填
Name 樹中显示此元件描述的名稱  
Comments 註釋  
User Define Variables 用戶定義的變量。變量名稱/值對。您需要在$ {…}結構的方括號內放置“名稱”(Name)列下的字符串,以便以後使用變量。然後,整個$ {…}將由“值”列中的字符串替換  

3.小結

 好了,今天關於JMeter的配置元件就分享到這裏,其中有些常用的要熟練掌握。

 

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

別忘了點 推薦 留下您來過的痕迹

 

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

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

※回頭車貨運收費標準

一文說通Dotnet Core的後台任務

這是一文說通系列的第二篇,裏面有些內容會用到第一篇中間件的部分概念。如果需要,可以參看第一篇:一文說通Dotnet Core的中間件

 

一、前言

後台任務在一些特殊的應用場合,有相當的需求。

比方,我們需要實現一個定時任務、或周期性的任務、或非API輸出的業務響應、或不允許併發的業務處理,像提現、支付回調等,都需要用到後台任務。

 

通常,我們在實現後台任務時,有兩種選擇:WebAPI和Console。

下面,我們會用實際的代碼,來理清這兩種工程模式下,後台任務的開發方式。

    為了防止不提供原網址的轉載,特在這裏加上原文鏈接:https://www.cnblogs.com/tiger-wang/p/13081020.html

二、開發環境&基礎工程

這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。

$ dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.201
 Commit:    b1768b4ae7

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  10.15
 OS Platform: Darwin
 RID:         osx.10.15-x64
 Base Path:   /usr/local/share/dotnet/sdk/3.1.201/

Host (useful for support):
  Version: 3.1.3
  Commit:  4a9f85e9f8

.NET Core SDKs installed:
  3.1.201 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

 

首先,在這個環境下建立工程:

  1. 創建Solution
% dotnet new sln -o demo
The template "Solution File" was created successfully.
  1. 這次,我們用Webapi創建工程
% cd demo
% dotnet new webapi -o webapidemo
The template "ASP.NET Core Web API" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on webapidemo/webapidemo.csproj...
  Restore completed in 179.13 ms for demo/demo.csproj.

Restore succeeded.
% dotnet new console -o consoledemo
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on consoledemo/consoledemo.csproj...
  Determining projects to restore...
  Restored consoledemo/consoledemo.csproj (in 143 ms).

Restore succeeded.
  1. 把工程加到Solution中
% dotnet sln add webapidemo/webapidemo.csproj
% dotnet sln add consoledemo/consoledemo.csproj

基礎工程搭建完成。

三、在WebAPI下實現一個後台任務

WebAPI下後台任務需要作為託管服務來實現,而託管服務,需要實現IHostedService接口。

 

首先,我們需要引入一個庫:

% cd webapidemo
% dotnet add package Microsoft.Extensions.Hosting

引入后,我們就有了IHostedService。

 

下面,我們來做一個IHostedService的派生託管類:

namespace webapidemo
{
    public class DemoService : IHostedService
    {
        public DemoService()
        
{
        }

        public Task StartAsync(CancellationToken cancellationToken)
        
{
            throw new NotImplementedException();
        }

        public Task StopAsync(CancellationToken cancellationToken)
        
{
            throw new NotImplementedException();
        }
    }
}

IHostedService需要實現兩個方法:StartAsync和StopAsync。其中:

StartAsync: 用於啟動後台任務;

StopAsync:主機Host正常關閉時觸發。

 

如果派生類中有任何非託管資源,那還可以引入IDisposable,並通過實現Dispose來清理非託管資源。

 

這個類生成后,我們將這個類注入到ConfigureServices中,以使這個類在Startup.Configure調用之前被調用:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddHostedService<DemoService>();
}

下面,我們用一個定時器的後台任務,來加深理解:

namespace webapidemo
{
    public class TimerService : IHostedService, IDisposable
    {
          /* 下面這兩個參數是演示需要,非必須 */
        private readonly ILogger _logger;
        private int executionCount = 0;

          /* 這個是定時器 */
        private Timer _timer;

        public TimerService(ILogger<TimerService> logger)
        
{
            _logger = logger;
        }

        public void Dispose()
        
{
            _timer?.Dispose();

        }

        private void DoWork(object state)
        
{
            var count = Interlocked.Increment(ref executionCount);

            _logger.LogInformation($"Service proccessing {count}");
        }

        public Task StartAsync(CancellationToken cancellationToken)
        
{
            _logger.LogInformation("Service starting");

            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        
{
            _logger.LogInformation("Service stopping");

            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }
    }
}

注入到ConfigureServices中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddHostedService<TimerService>();
}

就OK了。代碼比較簡單,就不解釋了。

四、WebAPI後台任務的依賴注入變形

上一節的示例,是一個簡單的形態。

下面,我們按照標準的依賴注入,實現一下這個定時器。

 

依賴注入的簡單樣式,請參見一文說通Dotnet Core的中間件。

 

首先,我們創建一個接口IWorkService:

namespace webapidemo
{
    public interface IWorkService
    {
        Task DoWork();
    }
}

再根據IWorkService,建立一個實體類:

namespace webapidemo
{
    public class WorkService : IWorkService
    {
        private readonly ILogger _logger;
        private Timer _timer;
        private int executionCount = 0;

        public WorkService(ILogger<WorkService> logger)
        
{
            _logger = logger;
        }

        public async Task DoWork()
        
{
            var count = Interlocked.Increment(ref executionCount);

            _logger.LogInformation($"Service proccessing {count}");
        }
    }
}

這樣就建好了依賴的全部內容。

 

下面,創建託管類:

namespace webapidemo
{
    public class HostedService : IHostedService, IDisposable
    {
        private readonly ILogger<HostedService> _logger;
        public IServiceProvider Services { get; }
        private Timer _timer;

        public HostedService(IServiceProvider services, ILogger<HostedService> logger)
        
{
            Services = services;
            _logger = logger;
        }

          public void Dispose()
        
{
            _timer?.Dispose();
        }

        private void DoWork(object state)
        
{
            _logger.LogInformation("Service working");

            using (var scope = Services.CreateScope())
            {
                var scopedProcessingService =
                    scope.ServiceProvider
                        .GetRequiredService<IWorkService>();

                scopedProcessingService.DoWork().GetAwaiter().GetResult();
            }
        }

        public Task StartAsync(CancellationToken cancellationToken)
        
{
            _logger.LogInformation("Service starting");

            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
            return Task.CompletedTask;
        }


        public Task StopAsync(CancellationToken cancellationToken)
        
{
            _logger.LogInformation("Service stopping");

            _timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }
    }
}

把託管類注入到ConfigureServices中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddHostedService<HostedService>();
    services.AddSingleton<IWorkService, WorkService>();
}

這樣就完成了。

 

這種模式下,可以根據注入的內容切換應用的執行內容。不過,這種模式需要注意services.AddSingleton、services.AddScoped和services.AddTransient的區別。

五、Console下的後台任務

Console應用本身就是後台運行,所以區別於WebAPI,它不需要託管運行,也不需要Microsoft.Extensions.Hosting庫。

我們要做的,就是讓程序運行,就OK。

 

下面是一個簡單的Console模板:

namespace consoledemo
{
    class Program
    {

        private static AutoResetEvent _exitEvent;

        static async Task Main(string[] args)
        
{
                /* 確保程序只有一個實例在運行 */
            bool isRuned;
            Mutex mutex = new Mutex(true, "OnlyRunOneInstance", out isRuned);
            if (!isRuned)
                return;

            await DoWork();

                        /* 後台等待 */
            _exitEvent = new AutoResetEvent(false);
            _exitEvent.WaitOne();
        }

        private static async Task DoWork()
        
{
            throw new NotImplementedException();
        }
    }
}

這個模板有兩個關鍵的內容:

  1. 單實例運行:通常後台任務,只需要有一個實例運行。所以,第一個小段,是解決單實例運行的。多次啟動時,除了第一個實例外,其它的實例會自動退出;
  2. 後台等待:看過很多人寫的,在這兒做後台等待時,用了一個無限的循環。類似於下面的:
while(true)
{
    Thread.Sleep(1000);
}

這種方式也沒什麼太大的問題。不過,這段代碼總是要消耗CPU的計算量,雖然很少,但做為後台任務,或者說Service,畢竟是一種消耗,而且看着不夠高大上。

 

當然如果我們需要中斷,我們也可以把這個模板改成這樣:

namespace consoledemo
{
    class Program
    {

        private static AutoResetEvent _exitEvent;

        static async Task Main(string[] args)
        
{
            bool isRuned;
            Mutex mutex = new Mutex(true, "OnlyRunOneInstance", out isRuned);
            if (!isRuned)
                return;

            _exitEvent = new AutoResetEvent(false);
            await DoWork(_exitEvent);
            _exitEvent.WaitOne();
        }

        private static async Task DoWork(AutoResetEvent _exitEvent)
        
{
            /* Your Code Here */

            _exitEvent.Set();
        }
    }
}

這樣就可以根據需要,來實現中斷程序並退出。

六、Console應用的其它運行方式

上一節介紹的Console,其實是一個應用程序。

在實際應用中,Console程序跑在Linux服務器上,我們可能會有一些其它的要求:

  1. 定時運行

Linux上有一個Service,叫cron,是一個用來定時執行程序的服務。

這個服務的設定,需要另一個命令:crontab,位置在/usr/bin下。

具體命令格式這兒不做解釋,網上隨便查。

  1. 運行到後台

命令後邊加個&字符即可:

$ ./command &
  1. 運行為Service

需要持續運行的應用,如果以Console的形態存在,則設置為Service是最好的方式。

Linux下,設置一個應用為Service很簡單,就這麼簡單三步:

第一步:在/etc/systemd/system下面,創建一個service文件,例如command.service:

[Unit]
# Service的描述,隨便寫
Description=Command

[Service]
RestartSec=2s
Type=simple
# 執行應用的默認用戶。應用如果沒有特殊要求,最好別用root運行
User=your_user_name
Group=your_group_name
# 應用的目錄,絕對路徑
WorkingDirectory=your_app_folder
# 應用的啟動路徑
ExecStart=your_app_folder/your_app
Restart=always

[Install]
WantedBy=multi-user.target

差不多就這麼個格式。參數的詳細說明可以去網上查,實際除了設置,就是運行了一個腳本。

第二步:把這個command.service加上運行權限:

# chmod +x ./command.service

第三步:註冊為Service:

# systemctl enable command.service

完成。

為了配合應用,還需要記住兩個命令:啟動和關閉Service

# #啟動Service
# systemctl start command.service
# #關閉Service
# systemctl stop command.service

七、寫在後邊的話

今天這個文章,是因為前两天,一個兄弟跑過來問我關於數據總線的實現方式,而想到的一個點。

 

很多時候,大家在寫代碼的時候,會有一種固有的思想:寫WebAPI,就想在這個框架中把所有的內容都實現了。這其實不算是一個很好的想法。WebAPI,在業務層面,就應該只是實現簡單的處理請求,返回結果的工作,而後台任務跟這個內容截然不同,通常它只做處理,不做返回 — 事實上也不太好返回,要麼客戶端等待時間太長,要麼客戶端已經斷掉了。換句話說,用WebAPI實現總線,絕不是一個好的方式。

不過,Console運行為Service,倒是一個總線應用的絕好方式。如果需要按序執行,可以配合MQ服務器,例如RabbitMQ,來實現消息的按序處理。

 

再說代碼。很多需求,本來可以用很簡單的方式實現。模式這個東西,用來面試,用來講課,都是很好的內容,但實際開發中,如果有更簡單更有效的方式,用起來!Coding的工作是實現,而不是秀技術。當然,能否找到簡單有效的方式,這個可能跟實際的技術面有關係。但這並不是一個不能跨越的坎。

多看,多想,每天成長一點點!

 

今天的代碼,在:https://github.com/humornif/Demo-Code/tree/master/0012/demo

(全文完)

 

 

微信公眾號:老王Plus

掃描二維碼,關注個人公眾號,可以第一時間得到最新的個人文章和內容推送

本文版權歸作者所有,轉載請保留此聲明和原文鏈接

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

帶你輕鬆了解C# Lock 關鍵字

  相信絕大多數.NET玩家和我一樣,常常使用Timer這個對象,而在WPF中使用DispatcherTimer的人也是很多,DispatcherTimer是在UI線程跑的。我們的程序中大多數都會充斥很多Timer,可以理解它是一個線程,它繼承自 System.Windows.Threading 。

  程序中也許會有一些靜態變量或是單例模式的對象來讓不同的頁面進行交互,但也就是這樣讓每個線程之間打架提供了基礎。因為資源是單獨的,就像是腳踩兩隻船的人,必定會翻車。例如一個List集合,你在一個線程中對它進行了操作,在同步瞬間的另線程中,如果不對它謹慎處理,就會造成 “集合已修改;可能無法執行枚舉操作”。當然我們說的不是關於集合的相關問題,而是關於資源分配的,當然在資源搶奪上,是在耗時的線程中才會出現的,例如下面的這張圖。

 

  這種耗時的操作,並且在同步線程中,沒有對線程進行封裝,很容易造成資源搶奪問題,假如Object是個集合,我在中間把它改了,下一秒的其它線程對它進行臟讀了,就會產生錯誤,我們可以通過Lock關鍵字。

  首先在Microsoft文檔中對Lock的說明是,lock 關鍵字可以用來確保代碼塊完成運行,而不會被其他線程中斷。這是通過在代碼塊運行期間為給定對象獲取互斥鎖來實現的。

  不過我們需要注意的是Lock本質上Monitor.Enter,Monitor.Enter會使值類型裝箱,每次Lock的是裝箱后的對象。Lock其實是類似編譯器的語法糖,因此編譯器直接限制住不能lock值類型,為啥呢,你仔細想想,每次裝箱后都是不同的對象,我怎麼判斷? object.ReferenceEquals 每次都是false…還有就是千萬不要Lock 字符串,簡單來說Lock字符串之後,只要是你以後有字符串匹配和你Lock里的內容有一樣的,那個該字符串也會被鎖定,相當於死鎖了。

  Lock和Monitor的區別不是很大,具體看以下代碼。

private static object obj = new object();
        public void LockSomething()
        {
            lock (obj)
            {
                dosomething();
            }
        }
        public void MonitorSomeThing()
        {
                Monitor.Enter(obj);
                dosomething();
                Monitor.Exit(obj);
        }
        public void dosomething()
        { 
            //做具體的事情
        }

   lock和Monitor是.NET用一個特殊結構實現的,Monitor對象是完全託管的、完全可移植的,並且在操作系統資源要求方面可能更為有效,同步速度較快,但不能跨進程同步。主要作用是鎖定臨界區,使臨界區代碼只能被獲得鎖的線程執行。Monitor.Wait和Monitor.Pulse用於線程同步,類似信號操作,個人感覺使用比較複雜,容易造成死鎖。

  lock就是封裝了Monitor.Enter和Monitor.Exit方法其實非常不難理解,只要確定Lock在啥時候用,該怎麼用就可以了,總結一句話。經常會應用於防止多線程操作導致公用變量值出現不確定的異常,用於確保操作的安全性。

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

【其他文章推薦】

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

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

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

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

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

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

工作5年,一位來自廣西測試妹子學習《自動化測試》經驗分享

本文來自一位妹子的投稿

大家好,我是黎婷,一位來自廣西南寧的測試妹子,工作5年,藉著原作者平台,今天給大家分享一下近期在學習《自動化測試實戰寶典》一書過程中的一些感想、學習心得,希望對大家有所幫助。

1. 來自大多數測試人員的困惑

在當前整個軟件行業的高速發展中,作為測試人員如何在這個急速浪潮中站穩腳跟,想必大多數人都知道需要學習更多的知識與技能,鞏固並加強自身能力,才能保證自己不被淘汰。

然而目前相當多行業內的測試人員基本都會面臨這種困惑:大體的方向是確定了,但是具體學什麼,怎麼學,落實到地的方案卻比較少有人能想明白。

2. 自己的經歷

想到自己當初在第一家公司離職選擇跳槽的時候,也是帶着這樣的迷茫進入了一家境外電商公司,初入這家公司時,正值質量管理部門大力推廣要求各種自動化測試工具使用的風潮中,基於對KPI的追求,於是開始着手查找當下各種流行的框架工具,為了KPI為了所謂的數據好看使用工具而學習測試工具,前期也是投入較多的時間,成果也是有的,但是很快也就放棄了使用:時間投入高,產出低,還不如手工測試。

然而隨着公司業務的快速發展,產品迭代頻繁,重複性的回歸工作慢慢的佔據很大一部分工作時間,加班處理工作已經成了常態,鑒於這種處境,又開始重拾了測試框架與工具,這回的目的很明確:在能保障產品質量的同時也要提高自己的工作效率,這些觀點在最近學習的書上提到關於自動化測試的意義點也是不謀而合的。

因此在這種狀況下開始主動學習工具和框架時,也多了份思考:如何利用這個框架的特點和優勢契合到實際工作的使用,能夠剛好解決工作中的痛點。帶着這個目的去學習工具使用工具,搞起的自動化,成效也是有很明顯的提升,從測試環境的回歸測試效率提升,以及後續線上核心功能的持續監控落地,都給自己實際工作帶來很大的推動促進作用,自己也有更多時間投入到更有價值的專項測試中,而不是疲於日常的重複性工作不得發展。

繼續帶着這份思考,本人最近花了一兩個星期大體研讀了《自動化測試實戰寶典 Robot Framework+Python》一書,基本成功快速地掌握了用Robot Framework工具進行接口自動化測試。

在這次學習的過程中,不僅僅能掌握到技術方面的知識,同時發覺它不只是一本工具書,更是一本有着完整且與時俱進的測試理論體系知識的書籍,書中成熟的思想理論加循序漸進的實戰講解,讓我從一個之前只聞RF名不識其真實面目的RF小白,到現在只通過一兩周時間就能快速掌握RF進行自動化測試。當然,在這麼短的時間內,很難一下子消化完這麼豐富的知識點,在接下來的測試工作學習中,希望能在本書的指引下,更深入學習RF框架來推動自己的工作更高效、高質量的交付。

3. 學習后的幾點心得

接下來想分享自己在學習該書籍過程中的幾點心得:

心得之一:筆者在接口測試項目實戰章節中講到的一個點:

做好接口測試除了在‘術’上融合工程化的最佳實踐,武裝方式方法,在‘道’上更需要有一個正確的認知和決心。正確的認知和決心更加強調的是團隊成員之間的目標是否一致,大家是不是真心認同這件事,而不是被迫地或者礙於領導下達的任務指標才去做。所有流於形式或者並非發自內心而被迫做的事情,最後收效往往都是甚微的,即便給你的是一把‘屠龍刀’,也發揮不出應有的作用。只有大家有意識地從心出發,主動尋求改變、追求質量和高效率、精益求精,再結合合適的工程實踐,才能做到‘道’和‘術’的雙重兼顧。”

對於筆者提到的這個點,跟讀者我的實際工作遇到的情況也是非常契合的。

在自己之前的公司中,也多多少少接觸到各種測試工具和框架,在工作之初去學習這類工具框架的時候,與其說初始的目的是為以後長遠的職業發展學習一項或多項技能鞏固能力,更不如說是基於領導的各種任務要求而“心不甘情不願”的去學習,在抱着這種想法去學習的技能,不僅不深入而且成果甚微,更是浪費了很多時間,最後甚至磨滅了繼續學習下去的念頭。

直到工作生涯中後期,才開始有意識自發的通過學習一些工具和技術,來真正地解決實際工作中的問題,在這個過程中領悟到學習的技術知識確切能給自己的工作帶來“收大於支”的成果時,不僅工作能力得到提升,內心也認可了技術帶來的改變,讓“屠龍寶刀”發揮其最大化作用,也真正契合了筆者提到的“道”和術的雙重兼顧。

當內心開始認可了這項東西時,會發現學習技術的路上原來如此有趣而引人入勝。利用自己所學,發揮其最大用處,會將自己的學習帶入一個良性的循環中,讓自己不只是在日常工作中得到效益,長遠來講,何嘗不是對將來的職業生涯鋪墊穩固的基石呢。

心得之二:談談學習過程中解決問題發思路。

在學習接口測試實戰項目時,在一些基礎環境配置、導入三方庫、接口認證鑒權等方面都遇到了阻礙。在解決這些問題的過程中,分享下我對於學習過程中遇到問題解決問題的一個思路。

  1. 首先自己在學習一些新技術時,心態都會放得比較平,因為面對新東西新技術,會遇到問題是在所難免的,一帆風順的狀況基本不存在,所以一定要擁有一份积極的心態,而不是遇到問題就開始心煩氣躁,不僅不利於解決問題,還很容易喪失繼續學習下去的信心。
  2. 其次就是解決問題的方法了,這次學習的RF框架,提供了詳細且比較全面的日誌信息,要學會通過具體報錯信息定位問題產生的原因。通過確定問題關鍵字在網絡上各大技術論壇社區平台進行搜索查找,對搜索到的結果也需要學會進行篩選總結,總結出搜索出來的解決方法的規律去解決問題,多次嘗試,不怕試錯,問題在這個階段80%都能得到解決。而不太建議一遇到問題就去找他人協助解決,首先溝通是有成本的,其次也會佔用他人寶貴的時間。
  3. 如果嘗試多次自己解決還是無果,接下來才是找能幫助自己解決問題可能性比較大的小夥伴,因為漫無目的的請教也是浪費雙方時間的行為。有其他小夥伴的協助,也很難有不能解決的問題了。最後解決問題之後的記錄總結也是很重要,作為學習的沉澱,可以在下次遇到同樣問題能快速解決,也可以給後續遇到同樣問題的小夥伴提供幫助。

總的來說,在這次學習RF接口測試中,自己所在部門的小夥伴都很熱心給予了很大的支持幫助,自己也才能在這麼短的時間掌握該項技能。大家能在百忙之中抽出時間給予協助是非常可貴的,作為請教者,在今後的學習中,也要提供給他人力所能及的協助。畢竟,學習是相互的,大家彙集起來的知識泉源才能壯大,也能更好推動大家一起發展。

4. 談一談RF框架可以優化的幾個點

從RF框架本身來談,它的優勢是絕對佔主導的,接下來談一談在使用RF框架中個人覺得還可以優化的幾點:

  1. RIDE編輯器性能有待提高,在加載現有的測試部的接口腳本,左側目錄樹經常出現卡頓,會一定程度影響腳本編寫效率。解決辦法之一可以嘗試用其他編輯器如pycharm等替代。

  2. 希望可以支持通過抓包工具抓取到接口數據進行轉化,自動生成符合腳本編寫格式的關鍵字,可以進一步提高編寫腳本的效率。如httprunner的har2case命令可以很方便的將har文件轉換成對應框架支持的腳本書寫規範。當然對於這塊優化點,也是有解決辦法的:可以通過自己寫工具去轉換抓到的har文件,而目前自己所在團隊也已經有大佬實現拓展了該工具。

  3. robot framework 的編輯器RIDE目前對Python3 的兼容性還不是特別好,主要是擴展庫的語法有比較多不兼容。當然RF框架本身一直是支持Python3的,若習慣使用如pycharm這些編輯器來編寫腳本,這塊不足也不存在了。但如果是習慣使用RIDE編輯器的話,對於當前主流使用的Python3維護兼容性成本還是比較高。

總得來說,本次的學習實戰時間還是比較短暫,而本書提供的技術及知識理論是非常豐富的,在接下來的學習中,還需要繼續精讀細讀,慢慢消化,利用好這把“屠龍寶刀”加強自己的“戰鬥值”。

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

【其他文章推薦】

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

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

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

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

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

面試官:說說Redis的Hash底層 我:……(來自閱文的面試題)

redis源碼分析系列文章

[Redis源碼系列]在Liunx安裝和常見API 

為什麼要從Redis源碼分析 

String底層實現——動態字符串SDS 

Redis的雙向鏈表一文全知道

前言

hello,各位小可愛們,又見面了。今天這篇文章來自去年面試閱文的面試題,結果被虐了。這一part不說了,下次專門開一篇,寫下我面試被虐的名場面,尷尬的不行,全程尬聊。哈哈哈哈,話不多說,開始把。

 

今天要寫Redis的Hash類型,如果有對Redis不熟悉,或者對其他數據類型感興趣的,可以移步上面的系列文章。(最上面的最上面最上面,重要的事情說三遍)

在Redis中Hash類型的應用非常廣泛,其中key到value的映射就通過字典結構來維護的。記筆記,此處要考。

​

API使用

API的使用比較簡單,所以以下就粗略的寫了。

插入數據hset

使用hset命令往myhash中插入兩個key,value的鍵值對,分別是(name,zhangsan)和(age,20),返回值當前的myhash的長度。

獲取數據hget

使用hget命令獲取myhash中key為name的value值。

獲取所有數據hgetall

使用hgetall命令獲取myhash中所有的key和value值。

獲取所有key

使用hkeys命令獲取myhash中所有的key值。

獲取長度

使用hlen命令獲取myhash的長度。

獲取所有value

使用hvals命令獲取myhash中所有的value值。

具體邏輯圖

hash的底層主要是採用字典dict的結構,整體呈現層層封裝。

首先dict有四個部分組成,分別是dictType(類型,不咋重要),dictht(核心),rehashidx(漸進式hash的標誌),iterators(迭代器),這裏面最重要的就是dictht和rehashidx。

接下來是dictht,其有兩個數組構成,一個是真正的數據存儲位置,還有一個用於hash過程,包括的變量分別是真正的數據table和一些常見變量。

最後數據節點,和上篇說的雙向鏈表一樣,每個節點都有next指針,方便指向下一個節點,這樣目的是為了解決hash碰撞。具體的可以看下圖。

這邊看不懂沒關係,後面會針對每個模塊詳細說明。(千萬不要看到這裏就跳過啦)

​

雙向鏈表的定義

字典結構體dict

我們先看字典結構體dict,其包括四個部分,重點是dictht[2](真正的數據)和rehashidx(漸進式hash的標誌)。具體圖如下。

具體代碼如下:

//字典結構體 
 typedef struct dict {
    dictType *type;//類型,包括一些自定義函數,這些函數使得key和value能夠存儲 
    void *privdata;//私有數據 
    dictht ht[2];//兩張hash表 
    long rehashidx; //漸進式hash標記,如果為-1,說明沒在進行hash
    unsigned long iterators; //正在迭代的迭代器數量
} dict;

 

數組結構體dictht

dictht主要包括四個部分,1是真正的數據dictEntry類型的數組,裏面存放的是數據節點;2是數組長度size;3是進行hash運算的參數sizemask,這個不咋重要,只要記住等於size-1;4是數據節點數量used,當前有多少個數據節點。

具體代碼如下:

//hash結構體 
typedef struct dictht {
    dictEntry **table;//真正數據的數組 
    unsigned long size;//數組的大小 
    unsigned long sizemask;//用戶將hash映射到table的位置索引,他的值總是等於size-1 
    unsigned long used;//已用節點數量 
} dictht;

 

數據節點dictEntry

dictEntry為真正的數據節點,包括key,value和next節點。

//每個節點的結構體  
typedef struct dictEntry {
    void *key; //key
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;//value
    struct dictEntry *next; //下一個數據節點的地址
} dictEntry;

 

 

 

擴容過程和漸進式Hash圖解

我們先來第一個部分,dictht[2]為什麼會要2個數組存放,真正的數據只要一個數組就夠了?

這其實和Java的HashMap相似,都是數據加鏈表的結構,隨着數據量的增加,hash碰撞發生的就越頻繁,每個數組後面的鏈表就越長,整個鏈表顯得非常累贅。如果業務需要大量查詢操作,因為是鏈表,只能從頭部開始查詢,等一個數組的鏈表全部查詢完才能開始下一個數組,這樣查詢時間將無線拉長。

這無疑是要進行擴容,所以第一個數組存放真正的數據,第二個數組用於擴容用。第一個數組中的節點經過hash運算映射到第二個數組上,然後依次進行。那麼過程中還能對外提供服務嗎?答案是可以的,因為他可以隨時停止,這就到了下一個變量rehashidx。(一點都不生硬的轉場,哈哈哈)

rehashidx其實是一個標誌量,如果為-1說明當前沒有擴容,如果不為-1則表示當前擴容到哪個下標位置,方便下次進行從該下標位置繼續擴容。

這樣說是不是太抽象了,還是一臉懵逼,貼心的送上擴容過程全解,一定要點贊評論多誇誇我哦。(越來越不要臉了。。。)

步驟1

首先是未擴容前,rehashidx為-1,表示未擴容,第一個數組的dictEntry長度為4,一共有5個節點,所以used為5。

步驟2

當發生擴容了,rahashidx為第一個數組的第一個下標位置,即0。擴容之後的大小為大於used*2的2的n次方的最小值,即能包含這些節點*2的2的倍數的最小值。因為當前為5個數據節點,所以used*2=10,擴容后的數組大小為大於10的2的次方的最小值,為16。從第一個數組0下標位置開始,查找第一個元素,找到key為name,value為張三的節點,將其hash過,找到在第二個數組的下標為1的位置,將節點移過去,其實是指針的移動。這邊就簡單說了。

 

步驟3

key為name,value為張三的節點移動結束后,繼續移動第一個數組dictht[0]的下標為0的後續節點,移動步驟和上面相同。

步驟4

繼續移動第一個數組dictht[0]的下標為0的後續節點都移動完了,開始移動下標為1的節點,發現其沒有數據,所以移動下標為2的節點,同時修改rehashidx為2,移動步驟和上面相同。

整個過程的重點在於rehashidx,其為第一個數組正在移動的下標位置,如果當前內存不夠,或者操作系統繁忙,擴容的過程可以隨時停止。

停止之後如果對該對象進行操作,那是什麼樣子的呢?

  • 如果是新增,則直接新增后第二個數組,因為如果新增到第一個數組,以後還是要移過來,沒必要浪費時間
  • 如果是刪除,更新,查詢,則先查找第一個數組,如果沒找到,則再查詢第二個數組。

字典的實現(源碼分析)

創建並初始化字典

首先分配內存,接着調用初始化方法_dictInit,主要是賦值操作,重點看下rehashidx賦值為-1(這驗證了剛才的圖解,-1表示未進行hash擴容),最後返回是否創建成功。

/* 創建並初始化字典 */
dict *dictCreate(dictType *type,
        void *privDataPtr)
{
    dict *d = zmalloc(sizeof(*d));
    _dictInit(d,type,privDataPtr);
    return d;
}

/* Initialize the hash table */
int _dictInit(dict *d, dictType *type,
        void *privDataPtr)
{
    _dictReset(&d->ht[0]);
    _dictReset(&d->ht[1]);
    d->type = type;
    d->privdata = privDataPtr;
    d->rehashidx = -1;//賦值為-1,表示未進行hash
    d->iterators = 0;
    return DICT_OK;
}

 

擴容

dict裏面有一個靜態方法_dictExpandIfNeed,判斷是否需要擴容。

首先判斷通過dictIsRehashing方法,判斷是否處於hash狀態,其調用的是宏常量#define dictIsRehashing(d) ((d)->rehashidx != -1),即判斷rehashidx是否為-1,如果為-1,即不處於hash狀態,if條件為false,可以進行擴容,如果不為-1,即處於hash狀態,if條件為true,不可以進行擴容,直接返回常量DICT_OK。

接着判斷第一個數組的size是否為0,如果為0,則擴容為默認大小4,如果不為0,則執行下面的代碼。

再接着判斷是否需要擴容,if中有三個條件,具體的分析如下。

最後就是調用dictExpand擴容方法了,參數為數據節點的雙倍大小ht[0].used*2。此處驗證了上面擴容過程的數組大小16。

擴容方法比較簡單點,獲取擴容后的大小,將第二個設置新的大小。

這樣講感覺有點空,看下流程圖。

擴容流程圖

具體代碼:

static int _dictExpandIfNeeded(dict *d)
{
    //判斷是否處於擴容狀態中,通過調用宏常量#define dictIsRehashing(d) ((d)->rehashidx != -1)
    //來判斷是否可以擴容
    if (dictIsRehashing(d)) return DICT_OK;

    //判斷第一個數組size是否為0,如果為0,則調用擴容方法,大小為宏常量
    //#define DICT_HT_INITIAL_SIZE     4
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

    //下面先列出if條件中所使用到的參數 
    // static int dict_can_resize = 1;數值為1表示可以擴容
    //static unsigned int dict_force_resize_ratio = 5;
    //我們來分析if條件,如果第一個數組的所有節點數量大於等於第一個數組的大小(表示節點數據已經有些多)
    //並且可用擴容(數值為1)或者所有節點數量除以數組大小大於5
    //這個條件表示擴容那個的條件,第一個就是節點必要大於等於數組長度,
    //第二點就再可以擴容和數據太多,超過5兩个中選其一
    if (d->ht[0].used >= d->ht[0].size &&
        (dict_can_resize ||
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
        //調用擴容方法
        return dictExpand(d, d->ht[0].used*2);
    }
    return DICT_OK;
}

int dictExpand(dict *d, unsigned long size)
{
    dictht n;
    //獲取擴容后真正的大小,找到比size大的最小值,且是2的倍數
    unsigned long realsize = _dictNextPower(size);

    //一些判斷條件
    if (dictIsRehashing(d) || d->ht[0].used > size)
        return DICT_ERR;

    if (realsize == d->ht[0].size) return DICT_ERR;

    n.size = realsize;
    n.sizemask = realsize-1;
    n.table = zcalloc(realsize*sizeof(dictEntry*));
    n.used = 0;

    //第一個hash為null,說明在初始化 
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return DICT_OK;
    }
    //正在hash,給第二個hash的長度設置新的, 
    d->ht[1] = n;
    d->rehashidx = 0;//設置當前正在hash 
    return DICT_OK;
}

/* 找到比size大的最小值,且是2的倍數 */
static unsigned long _dictNextPower(unsigned long size)
{
    unsigned long i = DICT_HT_INITIAL_SIZE;

    if (size >= LONG_MAX) return LONG_MAX;
    while(1) {
        if (i >= size)
            return i;
        i *= 2;
    }
}

 

 

漸進式hash

漸進式hash過程已經通過上面圖解說明,以下主要看下代碼是如何實現的,以及過程是不是對的。

擴容之後就是執行dictRehash方法,參數包括待移動的哈希表d和步驟数字n。

首先判斷標誌量rehashidx是否等於-1,如果等於-1,則表示hash完成,如果不等於-1,則執行下面的代碼。

接着進行循環,遍歷第一個數組上的每個下標,每次移動下標位置,都需要更新rehashidx值,每次加1。

再接着進行第二個循環,遍歷下標的鏈表每個節點,完成數據的遷移,主要是指針的移動和一些參數的修改。

最後,返回int數值,如果為0表示整個數據全部hash完成,如果返回1則表示部分hash結束,並沒有全部完成,下次可以通過rehashidx值繼續hash。

具體代碼如下:

//重新hash這個哈希表
 // Redis的哈希表結構公有兩個table數組,t0和t1,平常只使用一個t0,當需要重hash時則重hash到另一個table數組中
 //參數列表
 // 1. d: 待移動的哈希表,結構中存有目前已經重hash到哪個桶了
  //  2. n: N步進行rehash 
// 返回值 返回0說明整個表都重hash完成了,返回1代表未完成
int dictRehash(dict *d, int n) {
    int empty_visits = n*10; 
    //如果當前rehashidx=-1,則返回0,表示hash完成 
    if (!dictIsRehashing(d)) return 0;
    //分n步,而且ht[0]還有沒有移動的節點 
    while(n-- && d->ht[0].used != 0) {
        dictEntry *de, *nextde;
        assert(d->ht[0].size > (unsigned long)d->rehashidx);
        //第一個循環用來更新 rehashidx 的值,因為有些桶為空,所以 rehashidx並非每次都比原來前進一個位置,而是有可能前進幾個位置,但最多不超過 10。
        //將rehashidx移動到ht[0]有節點的下標,也就是table[d->rehashidx]非空
        while(d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }
        de = d->ht[0].table[d->rehashidx];  //第二個循環用來將ht[0]表中每次找到的非空桶中的鏈表(或者就是單個節點)拷貝到ht[1]中

        /* 利用循環講數據節點移過去 */
        while(de) {
            unsigned int h;

            nextde = de->next;
            /* Get the index in the new hash table */
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = nextde;
        }
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    }

    if (d->ht[0].used == 0) {
        zfree(d->ht[0].table);
        d->ht[0] = d->ht[1];
        _dictReset(&d->ht[1]);
        d->rehashidx = -1;
        return 0;
    }

    return 1;
}

總結

該篇主要講了Redis的Hash數據類型的底層實現字典結構Dict,先從Hash的一些API使用,引出字典結構Dict,剖析了其三個主要組成部分,字典結構體Dict,數組結構體Dictht,數據節點結構體DictEntry,進而通過多幅過程圖解釋了擴容過程和rehash過程,最後結合源碼對字典進行描述,如創建過程,擴容過程,漸進式hash過程,中間穿插流程圖講解。

如果覺得寫得還行,麻煩給個贊,您的認可才是我寫作的動力!

如果覺得有說的不對的地方,歡迎評論指出。

好了,拜拜咯。

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

【其他文章推薦】

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

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

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

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

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