六一兒童節,程序員寫給女兒的一封信_貨運

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

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

今天是六一兒童節,不想寫技術了,就寫點別的吧——給四歲的女兒寫封信。我的讀者群體里大部分都是大學生或者初入職場的新人,一時半會是體會不到做父母的辛酸和樂趣。

但我希望我的故事能給讀者朋友們枯燥的生活增添一丟丟樂趣,這就足夠了,對吧?這也是我一直以來的心愿。

親愛的女兒,你好呀。

我是你的爸爸馬偉青(有些讀者叫我二哥、有些叫王老師、有些叫馬老師、有些叫青哥,我統統接受,一個也不挑),等你能看懂這封信的時候,你已經可以自由地閱讀了。大量的閱讀,對人的一生至關重要,所以咱們家最多的就是書本了——這也是我一直引以為傲的。

媽媽也喜歡給你買書,我覺得她給你買的書都質量蠻高的,比如說甜心英語、長頸鹿卡密爾、馬努和米娜、巧虎、窗邊的小豆豆等等,這些書都是成套的,爸爸讀起來都覺得挺有意思,我想你也一定非常喜歡。

因為你讀書的樣子真的非常專註,非常可愛。

記得你剛從媽媽肚子里剖出來的時候,外婆抱着你,對爸爸說了一句夠驕傲一輩子的話:“長得真像你啊。”不是爸爸要和你媽媽爭寵,而是你的確是爸爸媽媽愛情的結晶,你的到來,豐富了我們的生活和情感,更重要的是,賦予了爸爸媽媽神聖的職責。

如果沒有你,爸爸在業餘時間里,可能還沉浸在 Dota、火炬之光、三國群英傳 7 的遊戲中。想想都覺得無比的可怕,打遊戲沒有錯,但爸爸應該去做一些更积極向上、更富有意義的事情,比如說讀書和寫作。

我比你姑姑大 11 歲,所以她小的時候,爸爸帶的非常多。就連上了小學,爸爸去哪裡她都要跟到哪裡,包括和你媽媽約會的時候。

也許是這樣一份特殊的經歷,爸爸在帶你的過程中顯然比你媽媽更專業一些,就連拍飽嗝(防止你奶吃多了吐)這種高級別的手法爸爸都會。由於媽媽工作上的限制,爸爸總體上帶你的時間確實要比媽媽多一些,陪你去上金寶貝早教課、上兔加熊體能課、上樂高積木、上愛貝英語,包括在小區裏面和小朋友玩,以及日常護理,洗澡洗頭髮,爸爸都能得心應手。

一開始,爸爸也是放不開的,畢竟帶孩子這件事,女性的佔比要比男性的佔比多得多,站在女人堆里,有時候真的是舉足無措啊。

但爸爸硬是挺了過來。關鍵是,還兼顧了家務、工作和寫作,想想都覺得自己挺牛逼的,我想你也這麼覺得吧?

爸爸和媽媽畢竟有一些不同,有的時候會嚴厲一些,所以你從來不鬧爸爸,你的這份克制多多少少會讓爸爸覺得內疚,但我想,對你的性格,對你以後的為人處事會有一些幫助,畢竟社會有美好的一面,也有殘忍的一面。

你長這麼大,只有一件事,讓爸爸至今懷恨在心,就是在你滿月之前,剪手指甲的時候把你手指頭上的肉剪掉了一小塊。鮮紅的血液流出來了你才後知后覺地哭了起來,可把爸爸嚇壞了。從此以後,再沒敢替你剪過指甲。

好了,過往就先煽情到這裏。接下來,爸爸寫一下對你的期許,或許不叫期許,更應該叫做祝福。無論你成為怎樣的你,爸爸永遠都是你背後最強有力的支柱。

第一,爸爸媽媽會努力給你力所能及最好的教育條件。但是,爸爸不希望學校過早的壓榨你的潛力,如果作業真的多到沒有時間去玩,爸爸寧願你不寫,家長會挨批的話,爸爸不嫌丟臉。

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

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

在爸爸看來,學習固然重要,但如果是通過寫不完的作業來達成的話,我認為是有問題的。爸爸來自於農村,所以讀書上學是唯一改變命運的出路,但你不同,你可以放心大膽地去做更多你喜歡做的事情,不用去承擔後果,以及背負巨大的壓力。

第二,在上學的年紀,一定要多多讀書,你喜歡讀什麼樣的書,爸爸都會給你買,沒有任何限制。

爸爸在上初中的時候,讀書的興趣是最濃烈,最純粹的,但那時候學校不允許讀任何課外書,真的是抹殺了爸爸求知的天性。

人最怕的除了窮,就是無知了。“窮”這個難題,爸爸媽媽會努力幫你解決,但“無知”這個難題,就需要你自己去完成了。而書,是解決這個難題最強有力的武器。

第三,英語非常非常重要,了解我們自己的文化固然重要,吸取別人的長處同樣重要。如果你精通英語的話,周遊世界,探索你感興趣的領域就會容易得多,這是不爭的事實。

爸爸就時常後悔沒有把英語學到極致,否則爸爸會在編程領域更上一層樓的。但爸爸沒有放棄,仍然在盡最大的努力改善中。你現在已經認識四五十個單詞了,這在爸爸看來,太棒了。

第四,女孩子一定要學會撒嬌和賣萌,這也是爸爸從小一直教你的,遇到一些問題時,哭鬧不是最好的解決辦法,變通才是最好的。

你可以像爸爸一樣,做一個情緒化的人,該哭的時候哭,該笑的時候笑,不要做女強人,學會與朋友分享你的痛苦和快樂。

第五,女孩子一定要學會化妝和打扮,把自己收拾的美美的,衣着得體,糟糕的情緒也會逃之夭夭。

體能鍛煉也是必不可少的,一方面是為了保持身體健康,塑造完美體形,另一方面,能夠幫助你養成良好的生活習慣,以及堅強的意志力。

第六,做你自己,做好你自己。不要太計較別人的眼光,把自己活得快活一點,樂觀一點、瀟洒一點。

如果以後爸爸媽媽的觀點和你不一致,不要聽我們的,聽從你自己內心最真實的那個聲音。你雖然是我們的女兒,但生命是你自己的,由你負責。但不管怎樣,爸爸和媽媽,都會做你最堅強的後盾,永遠支持你和愛護你。

就寫這麼多吧。Peace。

如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。

本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。

我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻

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

※回頭車貨運收費標準

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

深入了解ConcurrentHashMap_網頁設計公司

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

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

在上一篇文章【簡單了解系列】從基礎的使用來深挖HashMap里,我從最基礎的使用中介紹了HashMap,大致是JDK1.7和1.8中底層實現的變化,和介紹了為什麼在多線程下可能會造成死循環,擴容機制是什麼樣的。感興趣的可以先看看。

我們知道,HashMap是非線程安全的容器,那麼為什麼ConcurrentHashMap能夠做到線程安全呢?

底層結構

首先看一下ConcurrentHashMap的底層數據結構,在Java8中,其底層的實現方式與HashMap一樣的,同樣是數組、鏈表再加紅黑樹,具體的可以參考上面的HashMap的文章,下面所有的討論都是基於Java 1.8。

transient volatile Node<K,V>[] table;

volatile關鍵字

對比HashMap的底層結構可以發現,table的定義中多了一個volatile關鍵字。這個關鍵字是做什麼的呢?我們知道所有的共享變量都存在主內存中,就像table。

而線程對變量的所有操作都必須在線程自己的工作內存中完成,而不能直接讀取主存中的變量,這是JMM的規定。所以每個線程都會有自己的工作內存,工作內存中存放了共享變量的副本。而正是因為這樣,才造成了可見性的問題。

ABCD四個線程同時在操作一個共享變量X,此時如果A從主存中讀取了X,改變了值,並且寫回了內存。那麼BCD線程所得到的X副本就已經失效了。此時如果沒有被volatile修飾,那麼BCD線程是不知道自己的變量副本已經失效了。繼續使用這個變量就會造成數據不一致的問題。

內存可見性

而如果加上了volatile關鍵字,BCD線程就會立馬看到最新的值,這就是內存可見性。你可能想問,憑什麼加了volatile的關鍵字就可以保證共享變量的內存可見性?

那是因為如果變量被volatile修飾,在線程進行寫操作時,會直接將新的值寫入到主存中,而不是線程的工作內存中;而在讀操作時,會直接從主存中讀取,而不是線程的工作內存。

基礎使用

首先這個使用與HashMap沒有任何區別,只是實現改成了ConcurrentHashMap。

Map<String, String> map = new ConcurrentHashMap<>();
map.put("微信搜索", "SH的全棧筆記");
map.get("微信搜索"); // SH的全棧筆記

取值

首先我們來看一下get方法的使用,源碼如下。

public V get(Object key) {
  Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
  int h = spread(key.hashCode());
  if ((tab = table) != null && (n = tab.length) > 0 &&
      (e = tabAt(tab, (n - 1) & h)) != null) {
    if ((eh = e.hash) == h) {
      if ((ek = e.key) == key || (ek != null && key.equals(ek)))
        return e.val;
    }
    else if (eh < 0)
      return (p = e.find(h, key)) != null ? p.val : null;
    while ((e = e.next) != null) {
      if (e.hash == h &&
          ((ek = e.key) == key || (ek != null && key.equals(ek))))
        return e.val;
    }
  }
  return null;
}

大概解釋一下這個過程發生了什麼,首先根據key計算出哈希值,如果找到了就直接返回值。如果是紅黑樹的話,就在紅黑樹中查找值,否則就按照鏈表的查找方式查找。

這與HashMap也差不多的,元素會首先以鏈表的方式進行存儲,如果該桶中的元素數量大於TREEIFY_THRESHOLD的值,就會觸發樹化。將當前的鏈錶轉換為紅黑樹。因為如果數量太多的話,鏈表的查詢效率就會變得非常低,時間複雜度為O(n),而紅黑樹的查詢時間複雜度則為O(logn),這個閾值在Java 1.8中的默認值為8,定義如下。

static final int TREEIFY_THRESHOLD = 8;

賦值

put的源碼就不放出來了,放在這大家估計也不會一行一行的去看。所以我就簡單的解釋一下put的過程發生了什麼事,並貼上關鍵代碼就好了。

整個過程,除開併發的一些細節,大致的流程和1.8中的HashMap是差不多的。

  • 首先會根據傳入的key計算出hashcode,如果是第一次被賦值,那自然需要進行初始化table
  • 如果這個key沒有存在過,直接用CAS在當前槽位的頭節點創建一個Node,會用自旋來保證成功
  • 如果當前的Node的hashcode是否等於-1,如果是則證明有其它的線程正在執行擴容操作,當前線程就加入到擴容的操作中去
  • 且如果該槽位(也就是桶)上的數據結構如果是鏈表,則按照鏈表的插入方式,直接接在當前的鏈表的後面。如果數量大於了樹化的閾值就會轉為紅黑樹。
  • 如果這個key存在,就會直接覆蓋。
  • 判斷是否需要擴容

看到這你可能會有一堆的疑問。

例如在多線程的情況下,幾個線程同時來執行put操作時,怎麼保證只執行一次初始化,或者怎麼保證只執行一次擴容呢?萬一我已經寫入了數據,另一個線程又初始化了一遍,豈不是造成了數據不一致的問題。同樣是多線程的情況下, 怎麼保證put值的時候不會被其他線程覆蓋。CAS又是什麼?

接下來我們就來看一下在多線程的情況下,ConcurrentHashMap是如何保證線程安全的。

初始化的線程安全

首先我們來看初始化的源碼。

private final Node<K,V>[] initTable() {
  Node<K,V>[] tab; int sc;
  while ((tab = table) == null || tab.length == 0) {
    if ((sc = sizeCtl) < 0)
      Thread.yield(); // lost initialization race; just spin
    else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
      try {
        if ((tab = table) == null || tab.length == 0) {
          int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
          @SuppressWarnings("unchecked")
          Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
          table = tab = nt;
          sc = n - (n >>> 2);
        }
      } finally {
        sizeCtl = sc;
      }
      break;
    }
  }
  return tab;
}

可以看到有一個關鍵的變量,sizeCtl,其定義如下。

private transient volatile int sizeCtl;

sizeCtl使用了關鍵字volatile修飾,說明這是一個多線程的共享變量,可以看到如果是首次初始化,第一個判斷條件if ((sc = sizeCtl) < 0)是不會滿足的,正常初始化的話sizeCtl的值為0,初始化設定了size的話sizeCtl的值會等於傳入的size,而這兩個值始終是大於0的。

CAS

然後就會進入下面的U.compareAndSwapInt(this, SIZECTL, sc, -1)方法,這就是上面提到的CAS,Compare and Swap(Set),比較並交換,Unsafe是位於sun.misc下的一個類,在Java底層用的比較多,它讓Java擁有了類似C語言一樣直接操作內存空間的能力。

例如可以操作內存、CAS、內存屏障、線程調度等等,但是如果Unsafe類不能被正確使用,就會使程序變的不安全,所以不建議程序直接使用它。

compareAndSwapInt的四個參數分別是,實例、偏移地址、預期值、新值。偏移地址可以快速幫我們在實例中定位到我們要修改的字段,此例中便是sizeCtl。如果內存當中的sizeCtl是傳入的預期值,則將其更新為新的值。這個Unsafe類的方法可以保證這個操作的原子性。當你在使用parallelStream進行併發的foreach遍歷時,如果涉及到修改一個整型的共享變量時,你肯定不能直接用i++,因為在多線程下,i++每次操作不能保證原子性。所以你可能會用到如下的方式。

AtomicInteger num = new AtomicInteger();
arr.parallelStream().forEach(item -> num.getAndIncrement());

你可能會好奇,為什麼使用了AtomicInteger就可以保證原子性,跟Unsafe類和CAS又有什麼關係,讓我們接着往下,看getAndIncrement方法的底層實現。

public final int getAndIncrement() {
  return unsafe.getAndAddInt(this, valueOffset, 1);
}

可以看到,底層調用的是Unsafe類的方法,這不就聯繫上了嗎,而getAndIncrement的實現又長這樣。

public final int getAndAddInt(Object var1, long var2, int var4) {
  int var5;
  do {
    var5 = this.getIntVolatile(var1, var2);
  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  return var5;
}

沒錯,這裏底層調用了compareAndSwapInt方法。可以看到這裏加了while,如果該方法返回false就一直循環,直到成功為止。這個過程有個的名字,叫自旋。特別高端啊,說人話就是無限循環。

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

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

什麼情況會返回false呢?那就是var5變量存儲的值,和現在內存中實際var5的值不同,說明這個變量已經被其他線程修改過了,此時通過自旋來重新獲取,直到成功為止,然後自旋結束。

結論

聊的稍微有點多,這小節的問題是如何保證不重複初始化。那就是執行首次擴容時,會將變量sizeCtl設置為-1,因為其被volatile修飾,所以其值的修改對其他線程可見。

其它線程再調用初始化時,就會發現sizeCtl的值為-1,說明已經有線程正在執行初始化的操作了,就會執行Thread.yield(),然後退出。

yield相信大家都不陌生,和sleep不同,sleep可以讓線程進入阻塞狀態,且可以指定阻塞的時間,同時釋放CPU資源。而yield不會讓線程進入阻塞狀態,而且也不能指定時間,它讓線程重新進入可執行狀態,讓出CPU調度,讓CPU資源被同優先級或者高優先級的線程使用,稍後再進行嘗試,這個時間依賴於當前CPU的時間片劃分。

如何保證值不被覆蓋

我們在上一節舉了在併發下i++的例子,說在併發下i++並不是一個具有原子性的操作,假設此時i=1,線程A和線程B同時取了i的值,同時+1,然後此時又同時的寫回。那麼此時i++的值會是2而不是3,在併發下1+1+1=2是可能出現的。

讓我們來看一下ConcurrentHashMap在目標key已經存在時的賦值操作,因為如果不存在會直接調用Unsafe的方法創建一個Node,所以後續的線程就會進入到下面的邏輯中來,由於太長,我省略了一些代碼。

......
V oldVal = null;
synchronized (f) {
  if (tabAt(tab, i) == f) {
    if (fh >= 0) {
      binCount = 1;
      for (Node<K,V> e = f;; ++binCount) {
        ......
      }
    }
    else if (f instanceof TreeBin) {
      Node<K,V> p;
      binCount = 2;
      if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {
        oldVal = p.val;
        if (!onlyIfAbsent)
          p.val = value;
      }
    }
  }
}
if (binCount != 0) {
  if (binCount >= TREEIFY_THRESHOLD)
    treeifyBin(tab, i);
  if (oldVal != null)
    return oldVal;
  break;
}

上述代碼在賦值的邏輯外層包了一個synchronized,這個有什麼用呢?

synchronized關鍵字

這個地方也可以換一個方式來理解,那就是synchronized如何保證線程安全的。線程安全,我認為更多的是描述一種風險。在堆內存中的數據由於可以被任何線程訪問到,在沒有任何限制的情況下存在被意外修改的風險。

synchronized是通過對共享資源加鎖的方式,使同一時間只能有一個線程能夠訪問到臨界區(也就是共享資源),共享資源包括了方法、鎖代碼塊和對象。

那是不是使用了synchronized就一定能保證線程安全呢?不是的,如果不能正確的使用,很可能就會引發死鎖,所以,保證線程安全的前提是正確的使用synchronized

自動擴容的線程安全

除了初始化、併發的寫入值,還有一個問題值得關注,那就是在多線程下,ConcurrentHashMap是如何保證自動擴容是線程安全的。

擴容的關鍵方案是transfer,但是由於代碼太多了,貼在這個地方可能會影響大家的理解,感興趣的可以自己的看一下。

還是大概說一下自動擴容的過程,我們以一個線程來舉例子。在putVal的最後一步,會調用addCount方法,然後在方法里判讀是否需要擴容,如果容量超過了實際容量 * 負載因子(也就是sizeCtl的值)就會調用transfer方法。

計算分區的範圍

因為ConcurrentHashMap是支持多線程同時擴容的,所以為了避免每個線程處理的數量不均勻,也為了提高效率,其對當前的所有桶按數量(也就是上面提到的槽位)進行分區,每個線程只處理自己分到的區域內的桶的數據即可。

當前線程計算當前stride的代碼如下。

stride = (NCPU > 1) ? (n >>> 3) / NCPU : n);

如果計算出來的值小於設定的最小範圍,也就是private static final int MIN_TRANSFER_STRIDE = 16;,就把當前分區範圍設置為16。

初始化nextTable

nextTable也是一個共享變量,定義如下,用於存放在正在擴容之後的ConcurrentHashMap的數據,當且僅當正在擴容時才不為空。

private transient volatile Node<K,V>[] nextTable;

如果當前transfer方法傳入的nextTab(這是個局部變量,比上面提到的nextTable少了幾個字母,不要搞混了)是null,說明是當前線程是第一個調用擴容操作的線程,就需要初始化一個size為原來容量2被的nextTable,核心代碼如下。

Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1]; // 可以看到傳入的初始化容量是n << 1。

初始化成功之後就更新共享變量nextTable的值,並設置transferIndex的值為擴容前的length,這也是一個共享的變量,表示擴容使還未處理的桶的下標。

設置分區邊界

一個新的線程加入擴容操作,在完成上述步驟后,就會開始從現在正在擴容的Map中找到自己的分區。例如,如果是第一個線程,那麼其取到的分區就會如下。

start = nextIndex - 1;
end = nextIndex > stride ? nextIndex - stride : 0;
// 實際上就是當還有足夠的桶可以分的時候,線程分到的分區為 [n-stride, n - 1]

可以看到,分區是從尾到首進行的。而如果是首次進入的線程,nextIndex 的值會被初始化為共享變量transferIndex 的值。

Copy分區內的值

當前線程在自己劃分到的分區內開始遍歷,如果當前桶是null,那麼就生成一個 ForwardingNode,代碼如下。

ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);

並把當前槽位賦值為fwd,你可以把ForwardingNode理解為一個標誌位,如果有線程遍歷到了這個桶, 發現已經是ForwardingNode了,就代表這個桶已經被處理過了,就會跳過這個桶。

如果這個桶沒有被處理過,就會開始給當前的桶加鎖,我們知道ConcurrentHashMap會在多線程的場景下使用,所以當有線程正在擴容的時候,可能還會有線程正在執行put操作,所以如果當前Map正在執行擴容操作,如果此時再寫入數據,很可能會造成的數據丟失,所以要對桶進行加鎖。

總結

對比在1.7中採用的Segment分段鎖的臃腫設計,1.8中直接使用了CASSynchronized來保證併發下的線程安全。總的來說,在1.8中,ConcurrentHashMap和HashMap的底層實現都差不多,都是數組、鏈表和紅黑樹的方式。其主要區別就在於應用場景,非併發的情況可以使用HashMap,而如果要處理併發的情況,就需要使用ConcurrentHashMap。關於ConcurrentHashMap就先聊到這裏。

本文使用 mdnice 排版

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

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

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

啪啪,打臉了!領導說:try-catch必須放在循環體外!_租車

※超省錢租車方案

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

哈嘍,親愛的小夥伴們,技術學磊哥,進步沒得說!歡迎來到新一期的性能解讀系列,我是磊哥。

今天給大家帶來的是關於 try-catch 應該放在循環體外,還是放在循環體內的文章,我們將從性能業務場景分析這兩個方面來回答此問題。

很多人對 try-catch 有一定的誤解,比如我們經常會把它(try-catch)和“低性能”直接畫上等號,但對 try-catch 的本質(是什麼)卻缺少着最基礎的了解,因此我們也會在本篇中對 try-catch 的本質進行相關的探索

小貼士:我會盡量用代碼和評測結果來證明問題,但由於本身認知的局限,如有不當之處,請讀者朋友們在評論區指出。

性能評測

話不多說,我們直接來開始今天的測試,本文我們依舊使用 Oracle 官方提供的 JMH(Java Microbenchmark Harness,JAVA 微基準測試套件)來進行測試。

首先在 pom.xml 文件中添加 JMH 框架,配置如下:

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
   <groupId>org.openjdk.jmh</groupId>
   <artifactId>jmh-core</artifactId>
   <version>{version}</version>
</dependency>

完整測試代碼如下:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

/**
 * try - catch 性能測試
 */
@BenchmarkMode(Mode.AverageTime) // 測試完成時間
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) // 預熱 1 輪,每次 1s
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 測試 5 輪,每次 3s
@Fork(1) // fork 1 個線程
@State(Scope.Benchmark)
@Threads(100)
public class TryCatchPerformanceTest {
    private static final int forSize = 1000; // 循環次數
    public static void main(String[] args) throws RunnerException {
        // 啟動基準測試
        Options opt = new OptionsBuilder()
                .include(TryCatchPerformanceTest.class.getSimpleName()) // 要導入的測試類
                .build();
        new Runner(opt).run(); // 執行測試
    }

    @Benchmark
    public int innerForeach() {
        int count = 0;
        for (int i = 0; i < forSize; i++) {
            try {
                if (i == forSize) {
                    throw new Exception("new Exception");
                }
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return count;
    }

    @Benchmark
    public int outerForeach() {
        int count = 0;
        try {
            for (int i = 0; i < forSize; i++) {
                if (i == forSize) {
                    throw new Exception("new Exception");
                }
                count++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return count;
    }
}

以上代碼的測試結果為:

從以上結果可以看出,程序在循環 1000 次的情況下,單次平均執行時間為:

  • 循環內包含 try-catch 的平均執行時間是 635 納秒 ±75 納秒,也就是 635 納秒上下誤差是 75 納秒;
  • 循環外包含 try-catch 的平均執行時間是 630 納秒,上下誤差 38 納秒。

也就是說,在沒有發生異常的情況下,除去誤差值,我們得到的結論是:try-catch 無論是在 for 循環內還是 for 循環外,它們的性能相同,幾乎沒有任何差別

try-catch的本質

要理解 try-catch 的性能問題,必須從它的字節碼開始分析,只有這樣我能才能知道 try-catch 的本質到底是什麼,以及它是如何執行的。

此時我們寫一個最簡單的 try-catch 代碼:

public class AppTest {
    public static void main(String[] args) {
        try {
            int count = 0;
            throw new Exception("new Exception");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然後使用 javac 生成字節碼之後,再使用 javap -c AppTest 的命令來查看字節碼文件:

 javap -c AppTest 
警告: 二進制文件AppTest包含com.example.AppTest
Compiled from "AppTest.java"
public class com.example.AppTest {
  public com.example.AppTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: new           #2                  // class java/lang/Exception
       5: dup
       6: ldc           #3                  // String new Exception
       8: invokespecial #4                  // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
      11: athrow
      12: astore_1
      13: aload_1
      14: invokevirtual #5                  // Method java/lang/Exception.printStackTrace:()V
      17: return
    Exception table:
       from    to  target type
           0    12    12   Class java/lang/Exception
}

從以上字節碼中可以看到有一個異常表:

Exception table:
       from    to  target type
          0    12    12   Class java/lang/Exception

參數說明:

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

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

  • from:表示 try-catch 的開始地址;
  • to:表示 try-catch 的結束地址;
  • target:表示異常的處理起始位;
  • type:表示異常類名稱。

從字節碼指令可以看出,當代碼運行時出錯時,會先判斷出錯數據是否在 fromto 的範圍內,如果是則從 target 標誌位往下執行,如果沒有出錯,直接 gotoreturn。也就是說,如果代碼不出錯的話,性能幾乎是不受影響的,和正常的代碼的執行邏輯是一樣的。

業務情況分析

雖然 try-catch 在循環體內還是循環體外的性能是類似的,但是它們所代碼的業務含義卻完全不同,例如以下代碼:

public class AppTest {
    public static void main(String[] args) {
        System.out.println("循環內的執行結果:" + innerForeach());
        System.out.println("循環外的執行結果:" + outerForeach());
    }
    
    // 方法一
    public static int innerForeach() {
        int count = 0;
        for (int i = 0; i < 6; i++) {
            try {
                if (i == 3) {
                    throw new Exception("new Exception");
                }
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return count;
    }

    // 方法二
    public static int outerForeach() {
        int count = 0;
        try {
            for (int i = 0; i < 6; i++) {
                if (i == 3) {
                    throw new Exception("new Exception");
                }
                count++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return count;
    }
}

以上程序的執行結果為:

java.lang.Exception: new Exception

at com.example.AppTest.innerForeach(AppTest.java:15)

at com.example.AppTest.main(AppTest.java:5)

java.lang.Exception: new Exception

at com.example.AppTest.outerForeach(AppTest.java:31)

at com.example.AppTest.main(AppTest.java:6)

循環內的執行結果:5

循環外的執行結果:3

可以看出在循環體內的 try-catch 在發生異常之後,可以繼續執行循環;而循環外的 try-catch 在發生異常之後會終止循環。

因此我們在決定 try-catch 究竟是應該放在循環內還是循環外,不取決於性能(因為性能幾乎相同),而是應該取決於具體的業務場景

例如我們需要處理一批數據,而無論這組數據中有哪一個數據有問題,都不能影響其他組的正常執行,此時我們可以把 try-catch 放置在循環體內;而當我們需要計算一組數據的合計值時,只要有一組數據有誤,我們就需要終止執行,並拋出異常,此時我們需要將 try-catch 放置在循環體外來執行。

總結

本文我們測試了 try-catch 放在循環體內和循環體外的性能,發現二者在循環很多次的情況下性能幾乎是一致的。然後我們通過字節碼分析,發現只有當發生異常時,才會對比異常表進行異常處理,而正常情況下則可以忽略 try-catch 的執行。但在循環體內還是循環體外使用 try-catch,對於程序的執行結果來說是完全不同的,因此我們應該從實際的業務出發,來決定到 try-catch 應該存放的位置,而非性能考慮

關注公眾號「Java中文社群」回復“乾貨”,獲取 50 篇原創乾貨 Top 榜

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

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

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

【大廠面試01期】高併發場景下,如何保證緩存與數據庫一致性?_包裝設計

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

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

PS:本文已收錄到1.1K Star數開源學習指南——《大廠面試指北》,如果想要了解更多大廠面試相關的內容及獲取《大廠面試指北》離線PDF版,請掃描下方二維碼碼關注公眾號“大廠面試”,謝謝大家了!項目地址:https://github.com/NotFound9/interviewGuide

《大廠面試指北》項目截圖:

獲取《大廠面試指北》離線PDF版,請掃描下方二維碼關注公眾號“大廠面試”

面試題:高併發場景下,如何保證緩存與數據庫一致性?

問題分析

我們日常開發中,對於緩存用的最多的場景就像下圖一樣,可能僅僅是對數據進行緩存,減輕數據庫壓力,縮短接口響應時間。

這種方案在不需要考慮高併發得去寫緩存,高併發得讀寫緩存時,是不會有問題,但是如果是在高併發場景下,要保證緩存和數據庫的一致性,至少需要解決以下問題:

高併發寫時的數據不一致問題

高併發讀寫時,請求執行各步驟的順序是不可控的。假設此時有一個請求A,B都在在執行寫流程,請求A是需要將某個數據改成1,請求B是需要將某個數據改為2,執行操作如下時就會導致數據不一致的問題:

1.請求A執行操作1.1刪除緩存。

2.請求A執行操作1.2更新數據庫,將值改為1。

3.請求B執行操作1.1刪除緩存。

4.請求B執行操作1.2更新數據庫,將值改為2

5.假設說請求B所在服務器網絡延遲比較低,請求B先更新緩存,此時緩存中的key對應的value是2。

6.請求A更新緩存,將緩存中B更新的數據進行覆蓋,將key對應的值改為1。

此時數據庫中是B修改后的數據,值為2,而緩存中的數據是1,這樣在緩存過期錢,用戶讀到的都是臟數據,與數據庫不一致。

高併發讀寫時的數據不一致的問題

高併發讀寫時,請求執行各步驟的順序是不可控的。假設此時有一個請求A在執行寫流程,將原值由1改成2,請求B執行讀流程,執行操作如下時就會導致數據不一致的問題:

1.寫請求A執行1.1操作刪除緩存key,value是原值1。

2.讀請求B執行2.1操作發現緩存中沒有數據,就去執行2.2操作讀數據庫,讀到舊數據,值為1。

3.寫請求A執行1.2操作更新數據庫,將數據由1改為2。

4.寫請求A執行1.3操作更新緩存,此時緩存中的數據key對應的value是2。

5.讀請求B執行2.3操作更新緩存,將之前讀到的舊數據1設置到緩存中,此時緩存中的數據key對應的value是1。

所以如果說讀請求B所在服務器網絡延遲比較高,去執行2.3操作比寫請求A晚,就會導致寫請求A更新完緩存后,讀請求B使用之前讀到的舊數據去更新緩存,此時緩存中數據就與數據庫中的不一致。

解決方案

保證數據一致性,網上有很多種方案,例如:

1.先刪除緩存,再更新數據庫。

2.先更新數據庫,再刪除緩存。

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

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

3.先刪除緩存,再更新數據庫,然後異步延遲一段時間再去刪一次緩存。

但是這些方案都是存在各種各樣的問題,這裏篇幅有限,只給出目前相對正確的三套方案,目前的這些方案也有自己的局限性。

方案1.寫請求串行化

寫請求

1.寫請求更新之前先獲取分佈式鎖,獲得之後才能去數據庫更新這個數據,獲取不到就進行等待,超時后就返回更新失敗。

2.更新完之後去刷新緩存,如果刷新失敗,放到內存隊列中進行重試(重試時取數據庫最新數據更新緩存)。

讀請求

讀請求發現緩存中沒有數據時,直接去讀取數據庫,讀完更新緩存。

總結

這種技術方案通過對寫請求的實現串行化來保證數據一致性,但是會導致吞吐量變低。比較適合銀行相關的業務,因為對於銀行項目來說,保證數據一致性比可用性更加重要,就像是去存款機存錢,取錢時,為了保證賬戶安全,都是會讓用戶執行操作后,等待一段時間才能獲得反饋,這段時間其實取款機是不可用的。

方案2.先更新數據庫,異步刪除緩存,刪除失敗后重試

1.先更新數據庫

2.異步刪除緩存(如果數據庫是讀寫分離的,那麼刪除緩存時需要延遲刪除,否則可能會在刪除緩存時,從庫還沒有收到更新后的數據,其他讀請求就去從庫讀到舊數據然後設置到緩存中。)

3.刪除緩存失敗時,將刪除的key放到內存隊列或者是消息隊列中進行異步重試

發散思考

在更新完數據庫后,我們為什麼不直接更新,而是採用刪除緩存呢?

這是因為直接更新緩存的話,在高併發場景下,有多個更新請求時,難以保證后更新數據庫的請求會後更新緩存,也就是上面的高併發寫問題。如果採用刪除緩存,可以讓下次讀時讀取數據庫,更新緩存,保證一致性。

方案3.業務項目更新數據庫,其他項目訂閱binlog更新

1.業務項目直接更新數據庫。

2.cannal項目會讀取數據庫的binlog,然後解析后發消息到kafka。

3.然後緩存更新項目訂閱topic,從kafka接收到更新數據庫操作的消息后,更新緩存,更新緩存失敗時,新建異步線程去重試或者將操作發到消息隊列,後續再進行處理。

總結:

但是這種方案在更新數據庫后,緩存中還是舊值,必須等緩存更新項目消費消息后,更新緩存,緩存中才是最新值。所以更新操作完成與更新生效之間會有一定的延遲。

最後

大家有了解其他的技術方案,歡迎進群一起討論!

評論裏面有朋友問延時雙刪策略是什麼?

這裏解釋一下:延時雙刪策略就是先刪除緩存,再更新數據庫,再異步過一小段時間后刪除緩存(時間取決於MySQL主從同步的時間)。

是因為MySQL如果是讀寫分離時(寫請求寫主庫,讀請求讀從庫),我們更新主庫后,需要一段時間,從庫才會收到更新。

如果是寫請求更新主庫后,第二次立即刪除緩存,MySQL從庫還沒有收到更新,還是舊數據,那麼讀請求直接從庫讀到舊數據,設置到緩存的數據就是舊數據,就會數據不一致,所以這也是延時雙刪策略提出的初衷。

參考鏈接:

https://www.cnblogs.com/-wenli/p/11474164.html

https://www.cnblogs.com/rjzheng/p/9041659.html

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

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

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

Python驗證碼識別_台中搬家

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

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

項目地址:https://github.com/kerlomz/captcha_trainer

編譯版下載地址: https://github.com/kerlomz/captcha_trainer/releases/tag/v1.0

注意:若使用雲服務器 (Windows Server版) 遇到閃退,請按照步驟:我的電腦——屬性——管理——添加角色和功能——勾選桌面體驗,點擊安裝,安裝之後重啟即可。

2020/06/01編外:

想必各位只是偶然間搜到這篇文章,網上文章參差不齊,標題黨很多,能跑起來的開源代碼很少,對於能跑起來的代碼,也經常遇到以下問題如:內存泄漏,網絡參數寫死導致更換訓練集報錯,網絡跑其他樣本識別率低,沒有調用示例等等。

再往下看之前,我可以向你們保證,它絕對會是你所見過的所有驗證碼有關的文章中最實用,最接近生產水平的。

  1. 對小白: 你可以不需要動手寫任何一行代碼。
  2. 對小企業: 它的可用性和穩定性是經得起考驗的,在性能上也是同行領先的,可以放心入坑。

因為小編打算轉行了,離開這個行業之前總要留下點什麼證明自己來過,總有人和我說的這個部署不會調用,可能你們想要的是一行pip就搞定環境的,所以今天給你們安排了麻瓜OCR(MuggleOCR)。
https://pypi.org/project/muggle-ocr
它整合了簡單驗證碼識別通用模型+印刷文字通用識別,並且支持調用本文框架訓練的模型。調用只需要三行核心代碼:

import time
# STEP 1
import muggle_ocr
import os
# STEP 2
sdk = muggle_ocr.SDK(model_type=muggle_ocr.ModelType.OCR)
root_dir = r"./imgs"
for i in os.listdir(root_dir):
    n = os.path.join(root_dir, i)
    with open(n, "rb") as f:
        b = f.read()
    st = time.time()
    # STEP 3
    text = sdk.predict(image_bytes=b)
    print(i, text, time.time() - st)

這真的很簡單,應付一般的文字識別和驗證碼都足夠了。(文字識別過幾天會更新一下新模型,畢竟0601模型就跑了半天。

1. 前言

本項目適用於Python3.7,GPU>=NVIDIA GTX1050Ti,原master分支新增了GUI配置界面以及編譯版本了,是時候寫一篇新的文章了。

長話短說,開門見山,網絡上現有的代碼以教學研究為主,本項目是為實用主義者定製的,只要基本的環境安裝常識,便可很好的訓練出期望的模型,重定義幾個簡單的參數任何人都能使用深度學習技術訓練一個商業化成品。

筆者選用的時下最為流行的CNN+BLSTM+CTC(CRNN)進行端到端的不定長驗證碼識別,代碼中預留了CNNX(搜不到因為是小編自己拼湊的)/MobileNet/DenseNet121/ResNet50等選項,可以在配置界面中直接選用。首先,介紹個大概吧。

網格結構 predict-CPU predict-GPU 模型大小
CNN5+Bi-LSTM+H64+CTC 15ms 8ms 2mb
CNN5+CrossEntropy 8ms 2ms 1.5mb

H16/H64指的是Bi-LSTM的隱藏神經元個數UnitsNum,所以本項目使用GPU訓練,使用CPU進行預測。預測服務部署項目源碼請移步此處:https://github.com/kerlomz/captcha_platform
部署項目的編譯版下載地址:https://github.com/kerlomz/captcha_platform/releases

2.環境依賴:

花了超長篇幅介紹了訓練環境的基本搭建,主要是給尚未入門的讀者看的,老鳥們隨便跳過,若不希望在環境上面浪費時間的,歡迎使用編譯版,可在文章開頭找到下載地址。

關於CUDA和cuDNN版本的問題,不少人很糾結,這裏就列出官方通過pip安裝的TensorFlow的版本對應表:

Linux

Version Python version Compiler Build tools cuDNN CUDA
tensorflow_gpu-1.14.0 3.7 GCC 4.8 Bazel 0.15.0 7.6 9

Windows

Version Python version Compiler Build tools cuDNN CUDA
tensorflow_gpu-1.14.0 3.7 MSVC 2015 update 3 Bazel 0.15.0 7.6 10

如果希望使用上面對應之外的搭配的CUDA和cuDNN,可以自行編譯TensorFlow,或者去Github上搜索TensorFlow Wheel找到第三方編譯的對應版本的whl安裝包。提前預警,若是自己編譯將會苦難重重,坑很多,這裏就不展開了。

2.1 本項目環境依賴

目前在以下主流操作系統平台均測試通過:

操作系統 最低支持版本
Ubuntu 16.04
Windows 7 SP1
MacOS N/A

本訓練項目主要的環境依賴清單如下

依賴 最低支持版本
Python 3.7
TensorFlow-GPU 1.14.0
Opencv-Python 4.1.2.30
Numpy 1.16.0
Pillow 4.3.0
PyYaml 3.13
tqdm N/A

2.1.1 Ubuntu 16.04 下的 Python 3.7

1)先安裝Python環境(有Python 3.7環境的可以忽略)

sudo apt-get install openssl  
sudo apt-get install libssl-dev
sudo apt-get install libc6-dev gcc  
sudo apt-get install -y make build-essential zlib1g-dev libbz2-dev libreadline-dev $ libsqlite3-dev wget curl llvm tk-dev 
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tgz
tar -vxf Python-3.7.6.tar.xz
cd Python-3.7.6
./configure --prefix=/usr/local  --enable-shared
make -j8
sudo make install -j8

經過上面指令就安裝好Python3.7環境了,如果提示找不到libpython3.7m.so.1.0就到/usr/local/lib路徑下將該文件複製一份到/usr/lib和/usr/lib64路徑下。
2)安裝相關依賴(這一步Windows和Linux通用)
可以直接在項目路徑下執行pip3 install -r requirements.txt安裝所有依賴,注意這一步是安裝在全局Python環境下的,強烈建議使用虛擬環境進行項目間的環境隔離,如VirtualenvAnaconda等等。
我一般使用的是Virtualenv,有修改代碼需要的,建議安裝PyCharm作為Python IDE

virtualenv -p /usr/bin/python3 venv # venv is the name of the virtual environment.
cd venv/ # venv is the name of the virtual environment.
source bin/activate # to activate the current virtual environment.
cd captcha_trainer # captcha_trainer is the project path.
pip3 install -r requirements.txt

2.1.2 Ubuntu 16.04 下的 CUDA/cuDNN

網上看到過很多教程,我自己也部署過很多次,Ubuntu 16.04遇到的坑還是比較少的。14.04支持就沒那麼好,如果主板不支持關閉SecureBoot的話千萬不要安裝Desktop版,因為安裝好之後一定會無限循環在登陸界面無法進入桌面。
網上教程說要加驅動黑名單什麼的我直接跳過了,親測沒那個必要。就簡單的幾步:
1. 下載好安裝包
注意下載runfile類型的安裝包,deb安裝會自動安裝默認驅動,極有可能導致登陸循環
NVIDIA 驅動下載:https://www.geforce.cn/drivers
CUDA 下載地址:https://developer.nvidia.com/cuda-downloads
cuDNN 下載地址:https://developer.nvidia.com/cudnn (需要註冊NVIDIA賬號且登陸,下載deb安裝包)

2. 關閉圖形界面
Ctrl+alt+F1進入字符界面,關閉圖形界面

sudo service lightdm stop

3. 安裝Nvidia Driver

命令中的版本自己對應下載的版本改,在上面的下載地址根據自己的顯卡型號下載最新版,切記是runfile格式的安裝包。

sudo chmod a+x NVIDIA-Linux-x86_64-384.90.run //獲取執行權限
sudo ./NVIDIA-Linux-x86_64-384.90.run –no-x-check –no-nouveau-check –no-opengl-files //安裝驅動

安裝成功以後使用以下命令驗證,如果显示顯卡信息則表示安裝成功

nvidia-smi

4. 安裝CUDA

1)先安裝一些系統依賴庫

sudo apt-get install freeglut3-dev build-essential libx11-dev libxmu-dev libxi-dev libgl1-mesa-glx libglu1-mesa libglu1-mesa-dev
  1. 執行安裝程序,按指示無腦繼續就好了,如果提示是否安裝驅動選不安裝。
sudo sh cuda_9.0.176_384.81_linux.run

安裝完如果環境變量沒配上去,就寫到 ~/.bashrc 文件的尾部

export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

然後在終端執行 sudo ldconfig更新,安裝完畢就可以重啟機器重啟圖形界面了。

sudo service lightdm start

2.1.3 Windows 系統

在Windows其實簡單很多,只要到官網下載安裝包無腦安裝就可以了,下載連接同Ubuntu,先安裝Python,顯卡驅動,CUDA,然後下載對應的cuDNN替換到對應路徑即可。

3 使用

在訓練之前,有不少群友經常問我“訓練4位數英文数字需要多少樣本?”諸如此類的問題,我這裏統一做個回復,樣本數量主要是看樣本的特徵複雜度而定。

這裏可以提供幾個參考依據: 是否變形?是否旋轉?是否有複雜背景干擾?是否多種字體?字符集(分類數)多大?位數(標籤數)多少?

  1. 一般簡單的幾百個樣本(需要自行調整 驗證集大小驗證批次大小 )即可。
  2. 稍微複雜的幾千個樣本一般都能搞定。
  3. 特別複雜的幾萬樣本起。
  4. 中文這種幾千個分類的一般十萬起。

注:只準備一百個不到樣本的親們,千萬不要嘗試訓練測試,因為根本跑不起來。

入手的第一步環境搭建好了,那就是準備跑代碼了,還是有幾個必要的條件,巧婦難為無米之炊,首先,既然是訓練,要先有訓練集,有一個新手嘗鮮的訓練集,是mnist手寫識別的例子,可以在騰訊雲下載:https://share.weiyun.com/5pzGF4V
,現在萬事俱備,只欠東風。

3.1 定義一個模型

本項目基於參數化配置,不需要改動任何代碼,可以通過可視化界面操作訓練幾乎任何字符型圖片驗證碼。訓練框架界面可以大致劃分為幾個部分:

  1. Neural Network – 神經網絡區
  2. Project Configuration – 項目配置區
  3. Sample Source – 樣本源配置區
  4. Training Configuration – 訓練配置區
  5. Buttons – 功能控制區

依此類推的訓練配置的步驟如下:

  1. 神經網絡區 的配置項看起來很多,對於新手來說,可以直接使用默認的配置:CNNX+GRU+CTC+C1組合(CNN前置網絡+GRU+CTC+單通道)。
  2. 項目配置區 的配置項在網絡選好之後配置項目名,按回車或者點擊空白處確認。
  3. 樣本源配置區 的配置項用來配置樣本源的路徑,訓練樣本是根據此路徑進行打包成TFRecords格式,驗證樣本可以不指定,使用[Validation Set Num]參數隨機從訓練集總抽樣成驗證集。
  4. 訓練配置區 的配置項負責定義訓練完成的條件如:結束準確率,結束COST,結束Epochs,批次大小
  5. 功能控制區 的配置項,設置完上面步驟,先點擊[Make Dataset] 打包樣本,再點擊[Start Training]開始訓練。

以下部分有基礎的讀者們可以了解一下:

如若使用CrossEntropy作為解碼器需要注意標籤數LabelNum和圖片尺寸需要滿足的關係,因為網絡為多標籤而設計(一般的多標籤採用直接連接多個分類器),卷積層的輸出 outputs 經過了以下變換:

Reshape([label_num, int(outputs_shape[1] / label_num)])

為了保證運算 int(outputs_shape[1] / label_num) 能夠取得正整數,也意味着他們之間存在某種關係,對於CNN5+Cross Entropy的網絡結構,Conv2D層的步長皆為1,那麼需要保證以下關係成立:

\[mod(\frac{輸入寬度\times輸入高度\times輸出層參數}{池化步長^{池化層數}\times標籤數})= 0 \]

所以有時候需要Resize網絡輸入的Shape

網絡 池化步長^池化層數 輸出層參數
CNN5 16 64
CNNX 8 64
ResNet50 16 1024
DenseNet 32 2048

例如使用CNN5+CrossEntropy組合,則輸入寬度與輸入高度需要滿足:

\[mod(\frac{輸入寬度\times輸入高度\times64}{16\times標籤數})= 0 \]

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

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

同理如果CNN5+RNN+CTC,卷積層之後的輸出經過以下變換:

Reshape([-1, outputs_shape[2] * outputs_shape[3]])

原輸出(batch_size, outputs_shape[1], outputs_shape[2], outputs_shape[3]),RNN層的輸入輸出要求為(batch, timesteps, num_classes),為了接入RNN經過以上操作,那麼又引出一個Time Step的概念,所以timesteps的值也是 outputs_shape[1],而CTC Loss要求的輸入為 [batch_size, frames, num_labels],若是 timesteps 小於標籤數則無法計算損失,也就無法找損失函數中找到極小值,梯度何以下降。timesteps 最合理的值一般是標籤數的2倍,為了達到目的,也可以通過Resize網絡輸入的Shape解決,一般情況timesteps直接關聯於圖片寬度,大多情況只要按比例放大寬度即可。

ExtractRegex 參數:

注意:如果訓練集的命名格式和我提供的新手訓練集不一樣,請根據實際情況修改ExtractRegex的正則表達式。目前只支持在yaml配置文件中直接修改,尚未提供GUI界面修改的支持。 DatasetPath 和SourcePath參數允許多個路徑,這種操作適用於需要將多種樣本訓練為一個模型,或者希望訓練一套通用泛化模型的人。
字符集Category其實大多數情況下不需要修改,一般的圖形驗證碼離不開数字和英文,而且一般來說是大小寫不敏感的,不區分大小寫,因為打碼平台收集的訓練集質量參差不齊,有些大寫有些小寫,不如全部統一為小寫,默認ALPHANUMERIC_LOWER則會自動將大寫的轉為小寫,字符集可定製化很靈活,除了配置備註上提供的幾種類型,還可以訓練中文,自定義字符集用list表示,示例如下:

Category: ['常', '世', '寧', '慢', '南', '制', '根', '難']

如果是單標籤分類,可以配合LabelNum=1,例如:

Category: ["航母", "雨靴", "毛線", "安全帽", "調色板", "海鷗", "日曆", "網球拍", ......]

其文件名示例:航母_1231290424123.png

如果是多標籤分類,可以配合LabelSplit=&,例如:

Category: ["航母", "雨靴", "毛線", "安全帽", "調色板", "海鷗", "日曆", "網球拍", ......]

其文件名示例:航母&雨靴&毛線_1231290424123.png

可以自己根據收集訓練集的實際字符集使用率來定義,也可以無腦網上找3500常用字來訓練,注意:中文字符集一般比数字英文大很多,剛開始收斂比較慢,需要更久的訓練時間,也需要更多的樣本量,請量力而行

形如上圖的圖片能輕鬆訓練到95%以上的識別率。
ImageWidth、ImageHeight只要和當前圖片尺寸匹配即可,其實這裏的配置主要是為了方便後面的部署智能策略。

Pretreatment參數:

該參數是用來做圖片預處理的,例如形如以下的GIF動圖,

可以使用ConcatFrames參數選取幀對兩幀進行水平拼接,適用於處理滾動型GIF,而閃爍型GIF可以使用BlendFrames參數進行融合。

3.2 開始訓練

  1. 經過 採集標註樣本形如 xxx_隨機數.png
  2. 打包樣本
    通過GUI界面的 [Make Dataset] 或者 make_dataset.py 直接打包。
    注意:使用源碼運行本項目的功能模塊需要具備一定的語言基礎,參數修改的部分和示例已預留好,盡量不修改核心類或函數的代碼以免出現錯誤。

按照上面的介紹,配置只要修改極少數的參數對應的值,就可以開啟正式的訓練之旅了,具體操作如下:
可以直接使用 PyCharm 的 Run,執行 trains.py,也可以在激活Virtualenv下使用終端亦或在安裝依賴的全局環境下執行,但本文建議全程使用GUI界面進行操作,使用GUI僅需啟動 app.py 即可。

python3 trains.py

剩下的就是等了,看過程,等結果。
正常開始訓練的模樣應該是這樣的:

訓練結束會在項目的out路徑下生成一個包含pb文件的graph目錄和包含yaml文件的model目錄,下面該到部署環節了。

3.3 部署

真的很有必要認真的介紹一下部署項目,比起訓練,這個部署項目傾注了筆者更多的心血,為什麼呢?
項目地址:https://github.com/kerlomz/captcha_platform

如希望將本系統集成於自己的項目中的可以參考python-sdk的使用:
https://pypi.org/project/muggle-ocr/
該項目的核心基於 captcha_platform/sdk/pb/sdk.py 可以根據需要自行修改,抑或直接使用MuggleOCR 調用訓練框架生產的模型。(具體調用方法可點擊上面鏈接有對應的文檔介紹)

編譯版:https://github.com/kerlomz/captcha_platform/releases,使用編譯版無需安裝Python和TensorFlow環境。

真的值得了解的幾點

  1. 同時管理多個模型,支持模型熱拔插
  2. 靈活的版本控制
  3. 支持批量識別
  4. 服務智能路由策略

首先筆者重寫了TensorFlow的Graph會話管理,設計會話池,允許同時管理多模型,實現多模型動態部署方案。

1) 訓練好的 pb模型只要放在部署項目的graph路徑下,yaml模型配置文件放在model, 即可被服務發現並加載。(用SDK調用時,兩者置於同一目錄下)

2) 如果需要卸載一個正在服務的模型,只需要在model中刪除該模型的yaml配置文件,在graph中刪除對應的pb模型即可。

3) 如果需要更新一個已經服務中的模型,只需修改新版的模型yaml配置文件的版本號高於原模型的版本號,按先放pb後放yaml的順序,服務便會自動發現新版的模型並加載使用,舊的模型將因版本低於新版模型不會被調用,可以按照上述的卸載方法卸載已被棄用的模型釋放內存。
上面的操作中無需重啟服務,完全的無縫切換

其次,一套服務想要服務於各式各樣的圖像識別需求,可以定義一套策略,訓練時將所有尺寸一樣的圖片訓練成一個模型,服務根據圖片尺寸自動選擇使用哪個模型,這樣的設計使定製化和通用性共存,等積累到一定多樣的訓練集時可以將所有的訓練集合到一起訓練一個通用模型,亦可以彼此獨立,每個模型的疊加僅僅增加了少量的內存或顯存,網上的方案大多是不同的模型單獨部署一套服務,每個進程加載了一整套TensorFlow框架勢必是過於龐大和多餘的。

用到批量識別需求的人相對少很多這裏就不展開介紹了。但是這裏給出一個12306的例子:

FieldParam:
  CorpParams: [
    {
      "start_pos": [118, 0],
      "interval_size": [0, 0],
      "corp_num": [1, 1],
      "corp_size": [60, 30]
    },
    {
      "start_pos": [5, 40],
      "interval_size": [5, 5],
      "corp_num": [4, 2],
      "corp_size": [66, 66]
    }
  ]
  OutputCoord: True

該參數可以用於大圖的裁剪組成一批小圖作為一個批次的輸入,改用法可以避免多次調用。

但是識別項目提供了多套可選的服務有:gRPC,Flask,Tornado,Sanic,其中Flask和Tornado提供了加密接口,類似於微信公眾號開發接口的SecretKey和AccessKey接口,感興趣的可以在demo.py中閱讀調用源碼了解。

部署的使用可以經過package.py編譯為可執行文件,這樣可以免去更換機器環境安裝的煩惱,部署項目安裝流程同訓練項目,項目中提供的requirements.txt已經將所需的依賴都列清楚了,強烈建議部署項目安裝cpu版TensorFlow。

本項目部署推薦使用Tornado版,功能最齊全,性能最為穩定。

Linux:

  1. Tornado:
# 端口 19952
python3 tornado_server.py
  1. Flask
# 方案1,裸啟動, 端口 19951
python flask_server.py 
# 方案2,使用gunicorn,端口 5000
pip install gunicorn 
gunicorn -c deploy.conf.py flask_server:app
  1. Sanic:
# 端口 19953
python3 sanic_server.py
  1. gRPC:
# 端口 50054
python3 grpc_server.py
  1. 編譯版(基於Tornado)
# 前台運行
./captcha_platform_tornado
#後台運行
nohup ./captcha_platform_tornado &

Windows:
Windows平台下都是通過python3 xxx_server.py啟動對應的服務,注意,Tornado、Flask、Sanic的性能在Windows平台都大打折扣,gRPC是Google開源的RPC服務,有較為優越的性能。
編譯版直接運行編譯后的exe可執行文件即可。

3.4 調用/測試

1. Tornado服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19952/captcha/v1 application/json JSON POST

具體參數:

參數名 必選 類型 說明
image Yes String Base64 編碼
model_name No String 模型名,yaml配置中可綁定
need_color No String 顏色過濾,black/red/blue/yellow/green/white
output_split No String 多標籤分割字符

請求為JSON格式,形如:{“image”: “base64編碼后的圖像二進制流”}

返回結果:

參數名 類型 說明
message String 識別結果或錯誤消息
code String 狀態碼
success String 是否請求成功

該返回為JSON格式,形如:{“message”: “xxxx”, “code”: 0, “success”: true}

2. Flask服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19951/captcha/v1 application/json JSON POST

請求參數和返回格式同上

3. Sanic服務:

請求地址 Content-Type 參數形式 請求方法
http://localhost:19953/captcha/v1 application/json JSON POST

請求參數和返回格式同上

4. gRPC服務:
需要安裝依賴,grpcio、grpcio_tools和對應的grpc.proto文件,可以直接從項目中的示例代碼demo.py中提取。

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./grpc.proto

grpcio、grpcio_tools 是根據 grpc.proto 使用上述命令生成的。

class GoogleRPC(object):

    def __init__(self, host: str):
        self._url = '{}:50054'.format(host)
        self.true_count = 0
        self.total_count = 0

    def request(self, image, model_type=None, model_site=None):

        import grpc
        import grpc_pb2
        import grpc_pb2_grpc
        channel = grpc.insecure_channel(self._url)
        stub = grpc_pb2_grpc.PredictStub(channel)
        response = stub.predict(grpc_pb2.PredictRequest(
            image=image, split_char=',', model_type=model_type, model_site=model_site
        ))
        return {"message": response.result, "code": response.code, "success": response.success}

if __name__ == '__main__':
    result = GoogleRPC().request("base64編碼后的圖片二進制流")
    print(result)

3.5 奇技淫巧

該項目還可以直接用於識別帶顏色的驗證碼,部署項目middleware/impl/color_extractor.py基於k-means實現了顏色分離模塊,可用於處理如下形式的驗證碼:

還有一種方案是同時預測驗證碼和每個字符對應的顏色,不過這需要修改現有的神經網絡進行支持,在最後一層修改為雙輸出,一個輸出顏色,一個輸出對應字符,這對於樣本標註的要求較高,也提高的成本,所以如果能用無限生成樣本,那問題就迎刃而解了,比如上圖,筆者就寫了樣本生成代碼,感興趣的可以移步:
https://www.jianshu.com/p/da1b972e24f2
其實還有很多很多技巧,例如,用生成的樣本代替訓練集,其實網上的圖片驗證碼大多是採用開源的,稍作修改而已,大多數情況都能被近似生成出來,上述展示的驗證碼圖片不代表任何實際的網站,如有雷同,純屬巧合,該項目只能用於學習和交流用途,不得用於非法用途。

後記

如果文章描述不夠詳盡或需要技術支持的,可以加群 857149419 諮詢,或在開源項目中提issue,很榮幸能為開源社區貢獻綿薄之力。

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

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

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

Postman+Newman+Git+Jenkins接口自動化測試_台中搬家公司

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

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

一、Postman

   1、創建Collection,在Collection中創建接口請求,如下圖所示。

   2、編寫接口對應的斷言Test和Pre-request Script,如下圖所示。

   3、配置接口對應的環境變量或全局變量。

   4、導出接口集合Collection和導出對應接口的環境變量,導出生成的是JSON文件。

  如下圖所示,導出生成的接口集合和環境變量JOSN文件

  如大家對以上Postman的操作疑惑的,可參考之前我寫過的Postman(一)兩篇博客。

 二、Newman

 1、簡介

  newman是Postman的命令集合運行器,可以通過它直接從命令行運行和測試Postman,以便後續在持續集成服務器構建。

  Newman 是 Postman 推出的一個 nodejs 庫,直接來說就是 Postman 的json文件可以在命令行執行的插件。

  官方文檔:https://www.npmjs.com/package/newman

2、安裝

  淘寶鏡像全局安裝newman的命令:

1 npm install -g newman --registry=https://registry.npm.taobao.org

   安裝成功,如下圖所示,显示newman成功安裝好的版本號。

   驗證newman是否安裝成功,查看newman版本號命令:newman -version

 3、常用Newman語法

  列舉一些常用的Newman命令語法。

1、運行命令 newman run URL連接   或   newman run 集合文件名
2、-e,- environment <path>    指定Postman環境的URL或路徑。
3、-g,–globals    指定全局變量的文件地址或url 
4、-d,—-iteration-data   指定用於迭代的數據源文件路徑
5、-n,–iteration-count     指定迭代次數
6、–export-globals     導出全局環境變量
7、–timeout (ms)     設置整個集合運行完成執行的時間
8、–export-collection     導出集合文件
9、–timeport-request (ms)    指定等待請求返迴響應的時間
10、–timeout-script (ms)    指定等待腳本執行完成的時間

  我們可通過cmd終端查看newman run運行時可以所帶的相關參數。newman run -h查看所有相關參數語法,如下圖所示。

 4、Newman實例

  該newman run命令允許您指定要運行的集合。您可以輕鬆地從Postman App中將Postman Collection導出為json文件,並使用Newman運行它。

1 newman run examples/sample-collection.json

  例:運行Postman導出的接口集合及環境變量,打開cmd,定位至Json文件目錄,輸入newman運行命令。

1 newman run Test.postman_collection.json -e mukeOnline.postman_environment.json

  run運行接口集合的命令,-e 帶入接口集合的環境變量

  我們在cmd終端定位至接口集合和環境變量的JSON文件目錄,運行命令行結果如下圖:

 

   由上面的運行可看出在cmd終端運行的結果不太方便測試人員的查看與測試報告的形成。那麼我們如何利用newman命令來生成輸出測試報告文件?

指定測試報告的導出格式:目前支持四種格式:cli,json,html,junit

  –reporter-{{reporter-name}}-{{reporter-option}}

支持同時導出多種測試報告格式

  JSON –reporter-json-export <path>

  HTML –reporter-html-export <path>

  JUNIT/XML –reporter-junit-export <path>

  我們還拿上面的集合和環境變量來舉例,生成HTML報告時需要安裝html套件,命令行中執行:

1 npm install -g newman-reporter-html --registry=https://registry.npm.taobao.org

  安裝newman-reporter-html,如下圖

   運行命令,即可在文件目錄下查看生成的HTML文件形式的測試報告。

1 newman run Test.postman_collection.json -e mukeOnline.postman_environment.json -r html --reporter-html-export report.html

  生成的HTML測試報告文件,如下圖所示。

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

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

  我們可以看出用上面HTML生成的報告樣式比較簡單,怎麼讓我們的報告顯得高大上呢?那麼我們就得使用Htmlextra套件

  首先安裝使用htmlextra套件生成比較高級的html報告,使用方法和html的相同,需要安裝htmlextra,命令行中輸入:

1 npm install -g newman-reporter-htmlextra

  安裝成功,如下圖所示。

   我們把上面的接口集合用例再執行一下,看看這個測試報告的效果如何。newman運行命令如下。

1 newman run Test.postman_collection.json -e mukeOnline.postman_environment.json -r htmlextra --reporter-html-export htmlReport.html

  測試報告結果如下圖,可看出報告的樣式比之前好多了。

三、Git

  下載GitExtensions和Git,將Postman導出的json文件上傳至github上,拉取下來可進行迭代。這裏不過多說明,可自行查找資料。

 四、Jenkins

1、簡介

  Jenkins是一個開源的、可擴展的持續集成、交付、部署(軟件/代碼的編譯、打包、部署)的基於web界面的平台。允許持續集成和持續交付項目,無論用的是什麼平台,可以處理任何類型的構建或持續集成。

2、安裝

  下載Jenkins:https://www.jenkins.io/download/,下載后並安裝。

  安裝時出現的問題:

(1)問題一:注意安裝Jenkins之前先安裝tomcat和Java jdk,否進入不了Jenkins頁面。當安裝好tomcat和java jdk后,Jenkins頁面仍卡着不動。

  解決方法:查看並修改配置文件設置,重啟tomcat服務。進入Jenkins根目錄,打開hudson.model.UpdateCenter.xml文件,將文件中的url,修改為http://updates.jenkins.io/update-center.json,即去掉https中的s或更改為

https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json,修改好后,重新啟動tomcat服務即可。

(2)問題二:進入Jenkins頁面,到配置安裝組件時,出現No such plugin: cloudbees-folder的報錯。

  解決方法:打開鏈接“http://ftp.icm.edu.pl/packages/jenkins/plugins/cloudbees-folder/”,在最下面找到並打開“latest”目錄。將目錄中的“cloudbees-folder.hpi”下載下來後放在“jenkins安裝目錄\war\WEB-INF\detached-plugins”文件夾中。在“控制面板-管理工具-服務”中找到“jenkins”服務,重啟服務,重啟tomcat服務。重新訪問http://localhost:8080/即可。

 

3、Jenkins配置Git與newman

  新建Item,選擇Freestyle project

  任務的Git地址、證書與分支的相關配置

  設置構建newman運行命令

1 newman run Test.postman_collection.json -e mukeOnline.postman_environment.json

   設置好后保存,點擊Build Now

   生成構建結果

   構建後生成Junit報告,Git上新建空xml文件,nweman運行命令指向該xml文件。構建前運行命令修改為

newman run Test.postman_collection.json -e mukeOnline.postman_environment.json -r junit --reporter-junit-export newman\xmlReport.xml

  運行結果測試報告可在Test Result中可查看。

 

   設置定時任務

  Build periodically 每天上午10點構建一次  H 10 * * *,如下圖所示。

  以上就是簡單說明了一下Postman+Newman+Git+Jenkins接口自動化測試的設計與執行。後期有待優化與完善,請各位看官多多指教。

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

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

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

怎麼才能識破賣車銷售詭計 底價提車回家?_網頁設計公司

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

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

對於一些包牌價比我們自己完成一系列上牌過程的便宜,是因為它們在購車發票上進行“少開”,購置稅是按發票價進行收取,它們只要少開數額就可以少交購置稅以及保險,中間的差價就是它們的盈利額。但是對於我們消費者來說若是車輛發生重大事故、泡水、自燃或者盜搶,保險公司是按發票價為基準進行賠償,這無疑增加了一部分風險。

前言

買車的過程,其實就是一個和銷售鬥智斗勇的過程,稍有不留心就很有可能讓你沒了幾千元,或者是買到一些沒用的東西,而且整個購車過程都是那麼複雜,我們應該如何應對呢?還有我們在購車的時候怎樣才能最低價買到自己心儀的汽車呢?

盡量選擇全款購車而不是貸款購車

全款購車的時候雙方就相當交換貨物而已,手續可以達到最簡化,僅僅需要拿到車輛合格證、發票就可以完成這次交易。但是貸款購車的話4S店多半強制讓你在店裡購買車險以及上牌,而且對於原本僅僅是500元左右的上牌費用,在他們手上就成了2000以上的費用了,並且也有個人資料泄露的危險。保險方面也要比起自行購買要高400-1000元。所以說全款購車是比較省心省錢的。

選擇提裸車而不是落地價

很多4S美而言之“一條龍服務”,會為用戶提供上牌、保險等一系列服務,但是正正和上一理由一樣,這樣會給你增加額外的費用。對於一些包牌價比我們自己完成一系列上牌過程的便宜,是因為它們在購車發票上進行“少開”,購置稅是按發票價進行收取,它們只要少開數額就可以少交購置稅以及保險,

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

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

中間的差價就是它們的盈利額。但是對於我們消費者來說若是車輛發生重大事故、泡水、自燃或者盜搶,保險公司是按發票價為基準進行賠償,這無疑增加了一部分風險。

不選購精品

在購車以後,銷售基本會“乘勝追擊”向你推薦各種精品,例如汽車貼膜、迎賓踏板、發動機下護板等,而且這些產品的價格基本都是外面的一倍有餘,會美而言之“原裝件”,但實際上汽車廠商並沒有做到這類型的精品,我們在某寶上只要搜索“原裝件”就能找到很多打着“原廠”符號的精品,並且會發現這些產品都是一些小作坊的產品。

對比各類型的金融產品

對於選擇貸款購車的人來說,利息以及手續費都是一筆不小的開支,而事實上每一個金融機構的利息以及手續費都是不一樣的,所以我們要合理對比,有些金融機構甚至是有着廠家貼息的,這樣無疑能給你省下上萬元,這種貼息的品牌多半以上汽、日產為主,具體還是要多方面比較、詢問,選擇合適的金融產品。

明確購車所需費用

對於一些無良的4S店,他們總是給你設置各種各樣的雜費,貸款手續費、出庫費層出不窮。所以我們在買車的時候一定要白紙黑字明確各方面的費用,對於所贈送的東西也要將其記錄下來並雙方簽字,這樣就不會出現突然收取不該有的費用或者是該有的優惠、贈品沒有落實的情況。

所以買車過程講究的就是不怕麻煩、細心、謹慎以及理性,這樣才能以最低價格買到自己心儀的汽車,而對於不熟悉流程或者是汽車的新手朋友最好還是尋求一位“老司機”一同購車。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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

10萬元買不到喜歡的7座SUV?不妨考慮一下這款車_如何寫文案

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

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

海馬汽車的說法是,福美來七座版能夠提供多達60種的座椅組合形式。很顯然,正常人都不可能用盡這60種的座椅組合形式。在滿載的情況下,我試着觀察了車廂內部的空間表現。二排的乘客大多能夠擁有兩拳的腿部表現,1455mm的橫向空間也避免了乘客肩並肩的過分親密,頭部空間仍算開闊。

在MpV普遍被標籤化為微面小車的當下,海馬汽車試着以福美來轎車的聲量,在10萬級的家用MpV市場撕開一道口子。而結果又是否如其所願?

海馬汽車特意強調:這不是一輛MpV

“有很多的媒體把福美來七座版定位MpV,但我們依然更願意稱之為福美來七座版。”海馬汽車市場部相關人士在產品說明會上特意強調。對於擁有正統MpV造型的福美來7座版,為什麼要併入福美來的產品線,以及為什麼要刻意弱化福美來七座版的MpV概念,成了許多人心中的一個疑問。

從市場層面分析,目前的MpV市場存在劇烈的兩極分化的情況。在別克GL8以及本田奧德賽等合資MpV佔據20萬以上高端市場的同時,在品牌以及技術方面缺乏必要競爭力的自主品牌則集中發軔於10萬級以下的低端MpV市場。根據中汽協公布數據显示,9月的MpV細分市場當中,銷量前九位車型均為10萬以下的車型。而儘管這9款熱銷車型,在宣傳口徑上無一例外地強調“宜家宜商”的MpV概念,但從鋼板彈簧、扭力梁等車輛机械本質的角度上看,這些MpV依然沒有徹底擺脫微面的範疇。“大空間,能盡可能拉多一些貨物,是我購買長安歐諾的主要原因。”一位個體戶坦然跟我分享其購車的態度。這位車主的態度觀點,對於整個由自主品牌所主導的10萬級以下MpV市場而言,具有代表性的意義。

福美來七座版的售價區間為8.99萬-12.89萬,共6款車型。在主打家庭7座概念的基礎下,在配置以及價格均衡度較好的售價9.89萬、10.69萬的 1.5T 手動7座適·享型以及1.5T 自動7座適·享型 將會是支撐車系銷量的主力。如何在10萬元的MpV市場撕開一道口子以及擺脫自主品牌MpV固有的“工具車”標籤,最直接的方法,自然是打造7座概念而非MpV。至於為什麼要在7座的前面,以福美來作為背書。大家都很清楚在過去很長的一段時間內,福美來都是海馬汽車不可或缺的銷量貢獻車型。福美來的市場聲量,能夠幫助福美來七座版更好地導入市場。

“這是一輛7座的福美來轎車。”整場的說明會,海馬汽車都在重複地陳述着這一概念。當然,支撐起這一概念仍然需要實質性的產品力。若不,就成了黃婆賣瓜,自賣自誇。顯然,如今的消費者要比過去精明得多。“福美來七座版衍生自福美來轎車平台,零部件的通用率高達80%。“海馬汽車的產品講師表示,“這說明福美來七座版在產品的本質上,比起寶駿730等車型更符合家用的使用需求。”在我看來,這是海馬汽車一直堅持使用福美來七座作為產品名稱的主要原因。

以下是文風突變的試駕

試駕的路程由海口市的濱海大道至瓊海市的博鰲鎮, 全程140餘公里,其中又以高速路居多。有意思的一點是,考慮到旅遊城市的定位,海口市內採用了高速路免費的政策。而高速免收費的政策,也使得運輸貨車成為高速路上最主要的通行車輛。結果是,各種深深淺淺的坑窪縫隙隨處可見。

這底盤有點意思

以時速140公里行駛在高速路上,必要的操作是緊急變道以躲過這些大小不一的“陷阱”。試想一下,一輛自重將近1.6噸的MpV在時速140公里的情況下緊急變道的情景—真是狼狽又瘋狂地搖晃。但實際上,福美來7座版並沒有出現這一情況。變道時的車身姿態稱得上穩紮穩打,而非尷尬地從一個車道甩到另一車道。從同行媒體老濕在後排聊得興緻嫣然的情況來看,這一次又一次突如其來的緊急變道並沒有讓她們有不能容忍的不適。除了底盤調校等客觀性的因素以外,前麥弗遜獨立懸挂后多連桿獨立懸挂的底盤結構更為深刻地影響車身的動態表現。對於一輛MpV車型,這是最為理想的狀態。

福美來七座版的底盤表現比我所駕駛過的奧德賽、GL8等MpV車型都更硬朗一些。這種風格在MpV車型上,實在是比較少見的調校。當我不可避免地以高速碾過各種連續的坑窪時,福美來七座的底盤的的確確表現出了讓我滿意的整體性。那種更趨向於德系車的底盤韌性,讓我清楚地知道車輪碾過的到底是一個沙井蓋亦或是爛路的坑窪,並且使得車身的縱向搖擺被控制在了很小的幅度之內。再簡單一點地說,這車開起來並不會讓你有太多的睡意。當然了,這隻是我個人所更偏愛的底盤風格。對於部分的消費者而言,或許近乎於海綿觸感的底盤表現更合乎他們對於MpV的期待。

七座豐富的實用性

而福美來七座版讓我更為滿意的一點,是其空間上的表現。福美來七座版的車長4750mm,軸距為2800mm,內部採用的是2+3+2的傳統7座布局。海馬汽車的說法是,

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

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

福美來七座版能夠提供多達60種的座椅組合形式。很顯然,正常人都不可能用盡這60種的座椅組合形式。

在滿載的情況下,我試着觀察了車廂內部的空間表現。二排的乘客大多能夠擁有兩拳的腿部表現,1455mm的橫向空間也避免了乘客肩並肩的過分親密,頭部空間仍算開闊。即便是最為人所詬病的第三排座椅,兩名乘坐的女老濕也擁有着一定的活動空間。更重要的是,設計師在第三排座椅的兩旁都設置了水杯架以及可調節高度的頭枕,我表示這是一個相當具有人性的設計。

第三排在座椅與儲物空間空間的靈活變換,應該是MpV消費者最為喜愛的一點。福美來七座版在第三排無需坐人的情況下,通過第三排座椅背後的拉繩即可放平座椅,進而形成一個平整的儲物空間。我沒有測量實際的容積,但目測的結果是要比一般的緊湊型SUV的行李箱容積都要大得多。而既然是無需考慮第三排的感受,能夠實現前後滑動310mm的二排座椅要實現一般C級轎車的空間也並不是天荒夜談。即便是不放倒第三排座椅的情況下,其行李箱也能輕鬆放下3個20寸登機包。無論是7座的剛性需求,還是5座+超大儲物空間的柔性需求,福美來七座版都能夠做到有所兼顧。

足夠家用的動力

福美來七座版全系配備了1.5T渦輪增壓發動機,最大馬力156ps,最大扭矩220Nm(1800-4000rpm)。通過手動變速箱拉升轉速,以達到動力的最佳輸出平台的情況下,1.6噸車重再加以7位成年人的體重,並沒有讓福美來七座版在陡坡上表現出動力的羸弱。反之,在平路上,福美來七座版超車、急加速等表現也更為輕鬆。當然,這一切都建立在你把發動機轉速控制在2000rpm以上的基礎上。

至於高速的動力表現,其實1.5T渦輪增壓發動機因排氣量的先決條件所致,在邁過80公里/小時這道分水嶺以後,動力的爆發力以及再加速能力逐漸減弱已經是意料當中的事情。不過念及小排量優惠政策以及每日真實反映在儀錶上的油耗,大多數的消費者是願意接受的。

略有槽點的手動變速箱

在上市的6款車型當中,只有售價8.99萬的1.5T 手動適·悅型以及售價9.89萬的1.5T 手動適·享型配備的是6擋手動變速箱,其餘4款車型均為6擋手自一體變速箱。6擋手自一體變速箱的表現如何尚不清楚,但就我所試駕的8.99萬的1.5T 手動適·悅型的6擋手動變速箱倒是有3點是值得我吐槽的。一是1、2擋的行程不清晰,偶爾掛不上擋的情況讓我在綠燈時有些着急。二是擋桿在換擋時缺乏一定的吸入感,雖說無傷大雅,但手感總是豐富一些更好。三是離合器的結合點寬容度一般,對於新手而言,腳下動作稍大就有可能導致熄火。

這是一輛好看的MpV

至於外觀上的設計,論定一輛車的美與丑沒有明確的標準可循。但以MpV的標準以及如今潮流審美的眼界來看,福美來七座的確實拿捏到了一個比較理想的平衡。比如採用銀色鍍鉻設計的豎幅式進氣中網,滿足了大多數國內消費者對大氣的追求心理。由燈眉延伸至恭弘=叶 恭弘子板的弧狀線條使得車頭側面的厚重感得到了減弱,避免讓人產生像GL8一般的商務車印象。全系配備的16寸鋁合金輪轂,也算是這一價位不可多得的良心之舉。車輛的尾部略有心思地通過橫向線條營造出了一定的層次感,類似鷹眼式的尾燈造型也與尾部的整體設計相得益彰,致使我在選擇圖片的時候開始糾結起來。

內飾足夠溫馨,但用料有待提高

定位於家用七座的福美來七座版,在內飾氛圍上更偏向於家用溫馨的風格。多處弧形線條的運用讓整个中控趨於活潑、生氣。涉及到空調調節的所有物理按鈕均被設置在了擋桿的前方,就操作性而言算是便利的設計。而更多的多媒體功能操作,則被整合在了8英寸的觸控显示屏內。較為讓人驚喜的一點是,傳統的机械手剎被替換成电子手剎,在熄火時电子手剎會自動執行,算是我等健忘症人士的福音。不過在中控材質的運用上,滿眼的硬塑材質則多少令人感覺不完美。

期待的全系標配ESp

作為目前大多數自主品牌MpV車型的痛點—安全配置,福美來七座版全系標配了ESp車身穩定控制系統以及HSA上坡輔助系統,前者是安全的先決條件,後者是手動擋車型的一項必要保障。根據海馬汽車的說法,在C-NCAp碰撞測試中,福美來七座版的成績為56.93分,屬5星的範疇。在這一點上,福美來七座版的確要比同價位的自主品牌MpV以及SUV等車型的表現都要更加出色。

10萬元級別的一款理想家用車

外觀滿意,動力足夠。空間的表現比起5座轎車更能滿足多人口家庭的日常用車需求。並且相較於一眾由微面基礎改造而來的所謂MpV車型,福美來七座版從底盤結構到人機工學設計再到安全配置表現都優勝太多。很顯然,在10萬元價位的同類別車型中,福美來七座並沒有太多實質性意義上的競爭對手。至於其定位,福美來七座版到底是不是MpV,其實真不那麼重要。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

真男人就應該開這種車越野、家用、動力全能的汽車!_網頁設計公司

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

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

7L V8自然吸氣發動機,這向來裝載在超級跑車上的利器如今卻裝載到豐田坦途上,一腳油門下去,V8發動機的咆哮直達心靈,重重地衝擊你的內心,甚至讓你這種可怕的聲浪融為一體,這是一種會上癮的行為,不過在此之前你要考慮一下剛才那一腳油門值多少錢,畢竟這是一輛“耗油老虎”,百公里油耗基本是20L左右的了,油門重點隨時突破20L。

福特F150

如果說起福特F150的話可能沒有多少人會認識,但是如果說是福特猛禽的話,相信很多人都會知道,車如其名真真是一輛“猛禽”,兇猛十分,雖然以前那個大排量V8自然吸氣發動機被現在的3.5T V6 渦輪增壓發動機所代替,但是依舊有着強悍的動力以及有着更低的油耗,依然的魅力十足,只是在野外咆哮時聲音沒有以往那麼澎湃。而它在野外可謂是一方霸主,強悍的四驅系統,加上多種駕駛模式征服野外還不是一件輕而易舉的事情?另外貨倉使用了鋁合金材料,使得它在眾多皮卡中重量上佔了不少優勢,雖然被雪佛蘭黑了一把硬度。

豐田坦途

5.7L V8自然吸氣發動機,這向來裝載在超級跑車上的利器如今卻裝載到豐田坦途上,一腳油門下去,V8發動機的咆哮直達心靈,重重地衝擊你的內心,甚至讓你這種可怕的聲浪融為一體,

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

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

這是一種會上癮的行為,不過在此之前你要考慮一下剛才那一腳油門值多少錢,畢竟這是一輛“耗油老虎”,百公里油耗基本是20L左右的了,油門重點隨時突破20L。不過帶來的動力表現又是非常可怕的,6.87s的百公里加速時間,要知道這是一輛2.7噸的怪獸,是一般家用轎車的兩倍重量,在外面奔跑起來有着一種極強的氣勢以及震懾力,這后視鏡看到它還是乖乖讓路吧,說不定它要去征服哪個無人區,而且即使是一輛5.7L V8的“農用机械”,它依然保持着豐田的低故障率,起碼不用擔心發生故障的問題,不過貨倉就是加個備用油箱而已。

道奇 Ram

大排量V8發動機這是應該說是美國人玩得最溜,所以這和福特猛禽同出一個國家的道奇Ram同樣性能味十足,而且始終保留5.7L V8發動機的動力總成,畢竟作為一台充滿美國氣息的高端皮卡,氣勢是不可以失掉的。只是在性能方面是弱於福特猛禽以及豐田坦途這些車型,就差了在離地間隙上,不過內飾方面的粗糙做工也是“很美國范”,所以不少人還是選擇福特猛禽或者是豐田坦途,雖然高配有着可調節式懸挂,但是價格也由50萬左右升到了70萬,着實不划算。

這些高端皮卡都有着非常強大的性能,能給以你力量感、征服感以及安全感,要是不能打動你的話着實有點難,而且由於“農用机械”的商品屬性又或者是平行進口車的身份使得他們不到50萬就可以買到了,相對它們的性能來說,真的是性價比很高。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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

聯合國氣候峰會COP26因武漢肺炎延期 專家:把握轉型低碳經濟良機_網頁設計公司

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

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

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

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

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