文:宋瑞文(加州能源特約撰述)
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
![]() |
先前在媒體上熱烈討論的電動車 Gogoro 繼續以生活品味為主打形象,這次與時裝雜誌的活動結合,試圖要把騎機車這件事事情變得更潮。Gogoro 獲邀參與 GQ 紳裝正義 2016 SUIT WALK 遊行與派對活動。
為響應 GQ 派對主題,Gogoro 也舉辦「紳士有禮」體驗,只要消費者在 Taipei 101 四樓 Gogoro 展區完成最新智慧解鎖、拍照分享後即可獲得神秘限定騎乘邀請函,現場亦可索取潮男限定試騎卷,憑卷在 3 月 14 日白色情人節到 Gogoro 信義全球體驗中心試騎,即可獲得 Gogoro 限量潮 T 一件,數量有限敬請把握! Gogoro 執行長暨創辦人陸學森表示:「Gogoro 非常榮幸可以成為這次時尚盛會中唯一的二輪交通品牌,我們希望鼓勵無論男性女性,時時刻刻都用各式方式展現自己的風格,例如 Smartscooter® 智慧雙輪可以讓消費者更換不同設計的面板,也是希望突破機車只是交通工具的想像,讓智慧雙輪不僅可以帶你帥氣的在城市間遊走、也可以透過不同的面板造型與設計,讓車主展現你獨一無二的個人風格。」
![]() |
▲ 全球最時尚大叔 Nick Wooster 稱讚 Gogoro 智慧雙輪時尚外型最合紳士出遊。(Source:Gogoro) 紳裝正義活動到 2016 年已經是第三屆舉行,從 2014 年開始主辦人就串連台灣、香港、新加坡與上海等亞洲時尚重地,邀請所有的型男紳士在每年白色情人節期間,穿上最時尚、最具個人風格的西裝到街頭遊行展現紳士魅力。此次 Gogoro 除了是唯一受邀參加遊行的交通工具品牌之外,遊行後也與其他時尚品牌如 Porches、HUGO BOSS、瑞士頂級 Zenith 真力時手錶等,於 Taipei 101 四樓參與 GQ 紳裝正義派對,在現場展現如何透過科技時尚魅力、展現自我風格。
![]() |
▲ 時尚男孩小杰帶領 Gogoro 型男軍團參與 GQ 紳裝正義遊行。(Source:Gogoro) (首圖說明:Gogoro 型男軍團參與GQ SUIT WALK 2016 於台北101時尚派對。Source:Gogoro) (本文授權轉載自《》─〈〉)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
1. ArrayList 和Vector 的區別
ArrayList和Vector底層實現原理都是一樣得,都是使用數組方式存儲數據
Vector是線程安全的,但是性能比ArrayList要低。
ArrayList,Vector主要區別為以下幾點:
(1):Vector是線程安全的,源碼中有很多的synchronized可以看出,而ArrayList不是。導致Vector效率無法和ArrayList相比;
(2):ArrayList和Vector都採用線性連續存儲空間,當存儲空間不足的時候,ArrayList默認增加為原來的50%,Vector默認增加為原來的一倍;
(3):Vector可以設置capacityIncrement,而ArrayList不可以,從字面理解就是capacity容量,Increment增加,容量增長的參數。
2.說說ArrayList,Vector, LinkedList 的存儲性能和特性
ArrayList採用的數組形式來保存對象,這種方法將對象放在連續的位置中,所以最大的缺點就是插入和刪除的時候比較麻煩,查找比較快;
Vector使用了sychronized方法(線程安全),所以在性能上比ArrayList要差些.
LinkedList採用的鏈表將對象存放在獨立的空間中,而且在每個空間中還保存下一個鏈表的索引。使用雙向鏈表方式存儲數據,按序號索引數據需要前向或後向遍曆數據,所以索引數據慢,是插入數據時只需要記錄前後項即可,所以插入的速度快。
3.快速失敗(fail-fast) 和安全失敗(fail-safe) 的區別是什麼?
1.快速失敗
原理是:
迭代器在遍歷時直接訪問集合中的內容,並且在遍歷過程中使用一個modCount變量。集合在被遍歷期間如果內容發生變化,就會改變modCount的值。每當迭代器使用hasNext()或next()遍歷下一個元素之前,都會先檢查modCount變量是否為expectmodCount值。如果是的話就返回遍歷;否則拋出異常,終止遍歷。
查看ArrayList源碼,在next方法執行的時候,會執行checkForComodification()方法。
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1 ;
return (E) elementData[lastRet = i];
}
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
這裏異常的拋出條件是modCount != expectedModCount這個條件。如果集合發生變化時修改modCount值剛好又設置為了expectedModCount值,則異常不會拋出。因此,不能依賴於這個異常是否拋出而進行併發操作,這個異常只建議用於檢測併發修改的bug。
2.安全失敗
採用安全失敗機制的集合容器,在遍歷時不是直接在集合上訪問的,而是先複製原有集合內容,在拷貝的集合上進行遍歷。
原理:
由於迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所做的修改並不能被迭代器檢測到,所以不會觸發ConcurrentModificationException,例如CopyOnWriteArrayList。
缺點:
基於拷貝內容的優點是避免了ConcurrentModificationException,但同樣地,迭代器並不能訪問到修改後的內容。即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的。
場景:
Java.util.concurrent包下的容器都是安全失敗的,可以在多線程下併發使用,併發修改。
快速失敗和安全失敗都是對迭代器而言的。快速失敗:當在迭代一個集合時,如果有另外一個線程在修改這個集合,就會跑出ConcurrentModificationException,java.util下都是快速失敗。安全失敗:在迭代時候會在集合二層做一個拷貝,所以在修改集合上層元素不會影響下層。在java.util.concurrent包下都是安全失敗。
4.HashMap 的數據結構
HashMap的主幹類是一個Entry數組(jdk1.7) ,每個Entry都包含有一個鍵值隊(key-value).
我們可以看一下源碼:
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry <K,V> next; // 存儲指向下一個Entry的引用,單鏈表結構 int hash; // 對key的hashcode值進行hash運算後得到的值,存儲在Entry,避免重複計算 /** * Creates new entry. */ Entry( int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; }
所以,HashMap的整體結果如下
簡單來說,HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希衝突而存在的,如果定位到的數組位置不含鏈表(當前entry的next指向null ),那麼對於查找,添加等操作很快,僅需一次尋址即可;如果定位到的數組包含鏈表,對於添加操作,其時間複雜度為O(n),首先遍歷鏈表,存在即覆蓋,否則新增;對於查找操作來講,仍需遍歷鏈表,然後通過key對象的equals方法逐一比對查找。所以,性能考慮,HashMap中的鏈表出現越少,性能才會越好。
5.HashMap 的工作原理
HashMap基於hashing原理,我們通過put()和get()方法存儲和獲取對象,當我們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算hashcode,讓後找到bucket位置來存儲值對象。當獲取對象時,通過鍵對象的equals()方法找到正確的鍵值對,然後返回對象。
我們看一下put()源碼:
public V put(K key, V value) { // 當key為null,調用putForNullKey方法,保存null與table第一個位置中,這是HashMap允許為null的原因 if (key == null ) return putForNullKey( value); // 計算key的hash值 int hash = hash(key.hashCode()); // 計算key hash值在table數組中的位置 int i = indexFor(hash, table.length); // 從i出開始迭代e,找到key保存的位置 for (Entry<K, V> e = table[i]; e != null ; e = e.next) { Object k; // 判斷該條鏈上是否有hash值相同的(key相同) // 若存在相同,則直接覆蓋value,返回舊value if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; // 舊值=新值 e.value = value; e.recordAccess( this ); return oldValue; // 返回舊值 } } // 修改次數增加1 modCount++ ; // 將key、value添加至i位置處 addEntry(hash, key, value, i); return null ; }
通過源碼我們可以清晰看到HashMap保存數據的過程為:首先判斷key是否為null,若為null,則直接調用putForNullKey方法。若不為空則先計算key的hash值,然後根據hash值搜索在table數組中的索引位置,如果table數組在該位置處有元素,則通過比較是否存在相同的key,若存在則覆蓋原來key的value,否則將該元素保存在鏈頭(最先保存的元素放在鏈尾)。若table在該處沒有元素,則直接保存。
get()源碼:
public V get(Object key) { // 若為null,調用getForNullKey方法返回相對應的value if (key == null ) return getForNullKey(); // 根據該key的hashCode值計算它的hash碼 int hash = hash(key.hashCode()); // 取出table數組中指定索引處的值 for (Entry<K, V> e = table[indexFor(hash, table.length)]; e != null ; e = e .next) { Object k; // 若搜索的key與查找的key相同,則返回相對應的value if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e .value; } return null ; }
在這裡能夠根據key快速的取到value除了和HashMap的數據結構密不可分外,還和Entry有莫大的關係,在前面就提到過,HashMap在存儲過程中並沒有將key,value分開來存儲,而是當做一個整體key-value來處理的,這個整體就是Entry對象。同時value也只相當於key的附屬而已。在存儲的過程中,系統根據key的hashcode來決定Entry在table數組中的存儲位置,在取的過程中同樣根據key的hashcode取出相對應的Entry對象。
6.Hashmap 什麼時候進行擴容呢?
這裏我們再來複習put的流程:當我們想一個HashMap中添加一對key-value時,系統首先會計算key的hash值,然後根據hash值確認在table中存儲的位置。若該位置沒有元素,則直接插入。否則迭代該處元素鏈表並依此比較其key的hash值。如果兩個hash值相等且key值相等(e.hash == hash && ((k = e.key) == key || key.equals(k))),則用新的Entry的value覆蓋原來節點的value。如果兩個hash值相等但key值不等,則將該節點插入該鏈表的鏈頭。具體的實現過程見addEntry方法,如下:
void addEntry( int hash, K key, V value, int bucketIndex) { // 獲取bucketIndex處的Entry Entry<K, V> e = table[bucketIndex]; // 將新創建的Entry放入bucketIndex索引處,並讓新的Entry指向原來的Entry table[bucketIndex] = new Entry<K, V> (hash, key, value, e); // 若HashMap中元素的個數超過極限了,則容量擴大兩倍 if ( size++ >= threshold) resize( 2 * table.length); }
這個方法中有兩點需要注意:
一是鏈的產生。這是一個非常優雅的設計。系統總是將新的Entry對象添加到bucketIndex處。如果bucketIndex處已經有了對象,那麼新添加的Entry對象將指向原有的Entry對象,形成一條Entry鏈,但是若bucketIndex處沒有Entry對象,也就是e==null,那麼新添加的Entry對象指向null ,也就不會產生Entry鏈了。
二、擴容問題。
隨著HashMap中元素的數量越來越多,發生碰撞的概率就越來越大,所產生的鏈表長度就會越來越長,這樣勢必會影響HashMap的速度,為了保證HashMap的效率,系統必須要在某個臨界點進行擴容處理。該臨界點在當HashMap中元素的數量等於table數組長度*加載因子。但是擴容是一個非常耗時的過程,因為它需要重新計算這些數據在新table數組中的位置並進行複製處理。所以如果我們已經預知HashMap中元素的個數,那麼預設元素的個數能夠有效的提高HashMap的性能。
7.HashSet怎樣保證元素不重複
都知道HashSet中不能存放重複的元素,有時候可以用來做去重操作。但是其內部是怎麼保證元素不重複的呢?
打開HashSet源碼,發現其內部維護一個HashMap:
public class HashSet<E> extends AbstractSet <E> implements Set <E> , Cloneable, java.io.Serializable { static final long serialVersionUID = - 5024744406713321676L ; private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); public HashSet() { map = new HashMap<> (); }
...
}
HashSet的構造方法其實就是在內部實例化了一個HashMap對象,其中還會看到一個static final的PRESENT變量;
想知道為什麼HashSet不能存放重複對象,那麼第一步看看它的add方法進行的判重,代碼如下
public boolean add(E e) { return map.put(e, PRESENT)== null ; }
其實看add()方法,這時候答案已經出來了:HashMap的key是不能重複的,而這裏HashSet的元素又是作為了map的key,當然也不能重複了。
順便看一下HashMap裏面又是怎麼保證key不重複的,代碼如下:
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null ) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e ! = null ; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess( this ); return oldValue; } } modCount ++ ; addEntry(hash, key, value, i); return null ; }
其中最關鍵的一句:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
調用了對象的hashCode和equals方法進行判斷,所以又得到一個結論:若要將對象存放到HashSet中並保證對像不重複,應根據實際情況將對象的hashCode方法和equals方法進行重寫
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
3月25日,“太原比亞迪新能源汽車基地奠基儀式”在太原經濟技術開發區舉行。比亞迪將在太原打造中西部地區首個以純電動客車、專用車、工礦作業車為重點領域的新能源汽車製造與研發基地。
根據規劃,太原比亞迪新能源汽車基地位於太原市經濟技術開發區太太路(西攢村),專案分兩期建設,計畫總投資40億元人民幣,占地1100畝,規劃年產5000輛純電動客車、5000輛純電動專用車和2000輛工礦作業車生產能力。兩期工程全部建成投產後,可實現年產值超過150億元。此外,比亞迪還將進駐山西省科創城,成立純電動工礦作業車研發中心,全面深化新能源汽車“7+4”全市場戰略佈局。
未來,太原比亞迪基地將立足山西,在輻射中西部的同時,面向中、西、南亞,構建山西省新能源汽車產業核心競爭力,積極融入國家“一帶一路”經濟圈。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
定時任務的實現非常多,JDK的Timer、Spring提供的輕量級的Scheduled Task、QuartZ和Linux Cron等,還有一些分佈式的任務調度框架。本文主要介紹Scheduled Task的使用。
註解@Scheduled只能用於滿足下面兩個條件的方法上:
(1)沒有返回類型,或者說返回類型為void;
(2)沒有參數;
開啟Spring的Scheduler非常簡單,一個註解@EnableScheduling即可:
@Configuration
@EnableScheduling
public class SchedulingConfig {
}
如果是Springboot應用,則直接在啟動類上面加上@EnableScheduling就可以使用了。
代表下一個任務的開始與上一個任務的結束間隔總是固定的時長,而且總是會等上一個任務完成了,才會開啟下一個任務。如果需求是有這樣依賴要求的,使用這種模式是非常合適的。代碼如下:
@Scheduled(fixedDelay = 1000)
public void fixedDelay() {
log.info("fixedDelay");
}
參數為1000,代表固定延遲為1000毫秒,即1秒鐘,所以輸出為:
2019-11-19 21:02:43,977 scheduling-1:fixedDelay
2019-11-19 21:02:44,981 scheduling-1:fixedDelay
2019-11-19 21:02:45,983 scheduling-1:fixedDelay
2019-11-19 21:02:46,984 scheduling-1:fixedDelay
定頻任務的特性是任務的執行的時間間隔總是一樣的。比如每1小時執行一次,就是任務執行開始的時間點的時間間隔為1小時。代碼如下:
@Scheduled(fixedRate = 2000)
public void fixedRate() {
log.info("fixedRate");
}
參數為2000,則每2秒執行一次,輸出為:
2019-11-19 21:38:45,073 scheduling-1:fixedRate
2019-11-19 21:38:47,076 scheduling-1:fixedRate
2019-11-19 21:38:49,073 scheduling-1:fixedRate
2019-11-19 21:38:51,075 scheduling-1:fixedRate
需要注意的是它默認是單線程的,不會并行執行。即使是固定頻率,但下一次的任務也必須等到上一次任務執行完畢才會開始。下面這個例子能很好說明:
@Scheduled(fixedRate = 1000)
public void fixedRateLongTimeTask() throws InterruptedException {
log.info("fixedRateLongTimeTask");
Thread.sleep(3000);
}
由於任務需要執行3秒才能完成,即使fixedRate設置為1秒,並不能每一秒執行一次,輸出如下:
2019-11-19 21:46:00,108 scheduling-1:fixedRateLongTimeTask
2019-11-19 21:46:03,113 scheduling-1:fixedRateLongTimeTask
2019-11-19 21:46:06,113 scheduling-1:fixedRateLongTimeTask
2019-11-19 21:46:09,117 scheduling-1:fixedRateLongTimeTask
每3次輸出一次。
上述問題有辦法解決嗎?答案是肯定的,而且非常簡單。只需要加一個註解@Async就可以使任務能異步多線程地執行了,代碼如下:
@Async
@Scheduled(fixedRate = 1000)
public void fixedRateLongTimeTask() throws InterruptedException {
log.info("fixedRateLongTimeTask");
Thread.sleep(3000);
}
通過日誌可以看出是每秒執行一次的,即使前面的任務還沒有完成。而且線程名不一樣,通過多線程來執行,輸出結果為:
2019-11-19 21:54:22,261 task-5:fixedRateLongTimeTask
2019-11-19 21:54:23,257 task-6:fixedRateLongTimeTask
2019-11-19 21:54:24,257 task-4:fixedRateLongTimeTask
2019-11-19 21:54:25,257 task-8:fixedRateLongTimeTask
2019-11-19 21:54:26,259 task-1:fixedRateLongTimeTask
2019-11-19 21:54:27,262 task-2:fixedRateLongTimeTask
2019-11-19 21:54:28,260 task-3:fixedRateLongTimeTask
注意:需要指出的是,需要像@EnableScheduling一樣,需要添加配置註解@EnableAsync來打開這個功能開關。另外,如果任務執行時間很長,例如1分鐘,情況又不一樣。以後再詳細介紹@Async的使用吧。
初始延遲是用initialDelay來指定的,它可以延遲第一次任務執行的時間。如下例子的參數為30秒,則在啟動30秒后,才開始執行第一次。可以減輕項目啟動的負擔,也可以為任務執行前準備數據。
@Scheduled(fixedDelay = 1000, initialDelay = 30*1000)
public void fixedDelayWithIntialDelay() {
log.info("fixedDelayWithIntialDelay");
}
輸出如下:
2019-11-19 22:10:02,092 main:Tomcat started on port(s): 443 (http) with context path ''
2019-11-19 22:10:02,095 main:Started DemoApplication in 1.272 seconds (JVM running for 1.767)
2019-11-19 22:10:32,063 scheduling-1:fixedDelayWithIntialDelay
2019-11-19 22:10:33,067 scheduling-1:fixedDelayWithIntialDelay
2019-11-19 22:10:34,069 scheduling-1:fixedDelayWithIntialDelay
2019-11-19 22:10:35,069 scheduling-1:fixedDelayWithIntialDelay
可以看出,在項目啟動后30秒左右,才開始執行任務。
上述提供的功能並不能滿足定時任務調度的所有需求,比如需要每個月1號發送短信,每周六做數據分析等。這裏Cron表達式就派上用場了。
下面的例子表示每當秒數為06的時候就執行。代碼如下:
@Scheduled(cron = "6 * * ? * *")
public void cron() {
log.info("cron");
}
結果如下:
2019-11-19 22:20:06,003 scheduling-1:cron
2019-11-19 22:21:06,004 scheduling-1:cron
2019-11-19 22:22:06,002 scheduling-1:cron
Cron表達式功能非常強大,網上資料很豐富,這裏不展開講了。
之前的例子都將參數寫死在代碼上了,如果需要更靈活,其實可以用參數來配置。這樣需要修改參數的時候,不用修改代碼、編譯打包再部署了,直接修改配置文件即可。
代碼如下:
@Scheduled(cron = "${pkslow.cron}")
public void cronWithConfig() {
log.info("cronWithConfig");
}
在application.properties配置如下:
pkslow.cron=* * * ? * *
代碼1秒執行一次。
由於Spring的Scheduler默認是單線程的,這樣會存在一個問題,如果某個任務執行卡住了,那就無法繼續往下執行了。在日誌上表現就是突然消失了。這種情況出現的概率還是不小的,如操作數據庫死鎖了,http請求timeout為無限等待,還有其它原因的死鎖等。
當遇到這種情況,應通過命令jstack pid > pid.ThreadDump.txt獲取當前線程情況,然後分析是否真的是卡住了,卡在了哪個環節,然後再分析具體代碼。通過設置超時或重試等方法來解決。
本文主要介紹了Spring的定時任務註解@Scheduled的使用,講述了多種方式的使用和配置。它非常方便簡潔,對於簡單的定時任務足以應對了。
歡迎關注公眾號<南瓜慢說>,將持續為你更新…
多讀書,多分享;多寫作,多整理。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
摘錄自2020年03月22日中央通訊社美國報導
為避免武漢肺炎疫情擴散,包括美國在內,全球許多國家呼籲民眾不要外出留在家。但待在家裡無法與人接觸引發的孤獨感,以及可以空出時間照料,都讓寵物認養數目大幅攀升。
華盛頓郵報(Washington Post)報導,過去一段時間寵物認養倍增,馬里蘭州蓋瑟斯堡(Gaithersburg)動物救援所過去週日平均約15隻貓狗能找到認養家庭,最近排隊認養的人數從10人增加到40人,3小時就有30隻動物被認養。
美國愛護動物協會(American Society for the Prevention of Cruelty to Animals)總裁兼執行長薄夏德克爾(Matt Bershadker)透過郵件表示,動物能夠提供舒適感與陪伴感,特別是在危機時刻,他們鼓勵民眾繼續認養或暫時認養動物。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
4月11日,神州專車的運營主體神州優車股份有限公司正式宣佈,已與阿裡巴巴(中國)網路技術有限公司簽署戰略合作協定,結成戰略合作夥伴關係。
根據協定,雙方將在“互聯網+汽車”領域進行全方位的戰略合作,致力於重塑相關產業鏈及生態體系。 在雙方戰略合作的總體框架下,雙方將首先在汽車電商、大資料行銷、雲計算應用、高精地圖及出行大資料、智慧汽車等各方面推進合作。
對此,阿裡方面表示,阿裡巴巴與神州優車確已達成戰略合作協定,但阿裡巴巴目前在神州優車沒有股權。
值得注意的是,神州專車、神州租車與阿裡旅行在業務層面合作早已展開,並已經在阿裡旅行平臺上開設官方旗艦店。神州專車曾對媒體表示,未來神州專車、神州租車還將把用車服務與阿裡旅行出行項目進行諸如“酒店+車”“高爾夫+車”“旅遊路線+車”等形式增值打包。
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
在之前的文章里,通過Zabbix對主機的磁盤、CPU以及內存進行了監控,並在首頁Dashboard里創建了監控圖形,但是只有當我們登錄到Zabbix后才能看到監控到的問題(Problem),因此在本篇文章里,將利用觸發器(Trigger),以及媒介(Media)等配置項,實現當觸發器觸發時,通過不同媒介,如:郵件、釘釘,發送動作(Action),實現實時通知告警功能。
由於使用到腳本告警媒介,本文中通過調用Python腳本觸發告警,因此需要在Zabbix Server主機上安裝pip以及相關模塊。(這裏Python使用Centos7自帶的Python2.7.5)
安裝pip
# yum install -y epel-release
# yum install -y python-pip
安裝requests模塊
# pip install requests
Zabbix默認自帶了2種報警媒介類型(Media Type),电子郵件以及短信,我們將修改电子郵件類型配置,並新建腳本類型和Webhook類型。希望通過腳本、Webhook告警媒介發送釘釘消息。
注:Webhook告警媒介是Zabbix 4.4的新特性
修改电子郵件告警媒介
點擊【管理】-【報警媒介類型】-【Email】
修改Email配置,我這裏用的是Outlook郵箱,具體SMTP服務器可以參考。使用其他郵箱也可以去對應官網查詢SMTP配置。
測試發送郵箱,點擊【測試】
輸入收件人郵箱
收到郵件
新增腳本告警媒介
新建Python腳本告警媒介,用戶釘釘告警
點擊【創建媒體類型】
進行配置
| 配置項 | 值 |
|---|---|
| * 名稱 | Python腳本 |
| 類型 | 腳本 |
| * 腳本名稱 | pythonScript.py |
| 腳本參數(參數1) | {ALERT.MESSAGE} |
| 腳本參數(參數2) | {ALERT.SENDTO} |
| 腳本參數(參數3) | {ALERT.SUBJECT} |
接下來新建Python腳本,Zabbix Server配置文件中可以配置告警腳本路徑,默認為 /usr/lib/zabbix/alertscripts
# 查看告警腳本路徑
# cat zabbix_server.conf | grep AlertScriptsPath
編寫告警腳本
# cd /usr/lib/zabbix/alertscripts
# vim pythonScript.py
腳本內容
#!/usr/bin/env python
#coding:utf-8
import requests,json,sys,os,datetime
# 釘釘機器人地址
webhook="https://oapi.dingtalk.com/robot/send?access_token=your_dingding_robot_access_token"
# 對應{ALERT.SENDTO}, Zabbix告警媒介配置界面第2個參數
user=sys.argv[2]
# 對應{ALERT.MESSAGE}, Zabbix告警媒介配置界面第1個參數
text=sys.argv[1]
data={
"msgtype": "text",
"text": {
"content": text
},
"at": {
"atMobiles": [
user
],
"isAtAll": False
}
}
headers = {'Content-Type': 'application/json'}
x=requests.post(url=webhook,data=json.dumps(data),headers=headers)
給腳本可執行權限
# chmod uo+x /usr/lib/zabbix/alertscripts/pythonScript.py
測試腳本
釘釘收到消息
新增Webhook告警媒介
| 配置項 | 值 |
|---|---|
| * 名稱 | Webhook |
| 類型 | Webhook |
| 參數: (名稱) | 值 |
| user | {ALERT.SENDTO} |
| subject | {ALERT.SUBJECT} |
| message | {ALERT.MESSAGE} |
腳本:
try {
Zabbix.Log(4, 'params= '+value);
params = JSON.parse(value);
req = new CurlHttpRequest();
data = {};
result = {};
req.AddHeader('Content-Type: application/json');
data.msgtype = "text";
// 對應 message參數
data.text = {"content" : params.message};
// 對應 user參數
data.at = {"atMobiles": [params.user], "isAtAll": "false"};
// 釘釘機器人
resp = req.Post('https://oapi.dingtalk.com/robot/send?access_token=your_access_token',
JSON.stringify(data)
);
} catch (error) {
result = {};
}
return JSON.stringify(result);
測試Webhook
需要將新增的告警媒介添加給用戶
點擊【用戶】-【告警媒介】
將上述步驟添加的告警媒介(Python腳本、Webhoob、Email),進行添加(收件人根據告警媒介類型填寫郵箱或手機號),嚴重性也根據需要勾選。
完成上述配置完成后,需要創建動作(Action),將觸發器(Trigger)和告警媒介(Media Type)進行關聯,一旦觸發器觸發,那麼Zabbix會執行動作,再去執行告警媒介。
添加動作
點擊【配置】-【動作】-【創建動作】
配置【動作】相關信息
| 配置項 | 值 |
|---|---|
| * 名稱 | 告警動作 |
| 新的觸發條件 | 【觸發器】【等於】【Template Disk Free Size: 磁盤剩餘空間觸發器】 |
操作步驟如下圖:
群組選擇 ->Linux servers
主機選擇 -> Template Disk Free Size 模板()
勾選觸發器 -> 磁盤剩餘空間觸發器 ()
勾選後點擊【選擇】
配置【操作】相關信息
點擊【操作】
先配置以下信息
| 配置項 | 值 |
|---|---|
| * 默認操作步驟持續時間 | 1h(保持默認) |
| 默認標題 | 告警: {EVENT.NAME} |
| 消息內容 | 【磁盤空間不足告警】 告警事件: {EVENT.DATE} {EVENT.TIME} 告警問題: {EVENT.NAME} 告警主機: {HOST.IP} {HOST.NAME} 告警級別: {EVENT.SEVERITY} 磁盤剩餘:{ITEM.VALUE} |
上述配置表格【默認標題】和【消息內容】值中形如{EVENT.NAME}的內容是Zabbix中的宏(Marco),宏是一個變量,例如 {HOST.IP} 表示告警主機的IP地址,Zabbix自帶的宏可以參考
繼續配置操作
點擊【新的】
【操作類型】選擇發送消息,【發送到用戶】添加Admin
【僅送到】根據需要選擇之前配置的,本文選擇Email和Python腳本(這裏只能單選或全選,所以需要先選擇一個,因此需要多次)
添加完成後點擊【添加】
向被監控主機拷貝或下載大文件,使其磁盤剩餘空間低於觸發器監控閾值,等待觸發器觸發問題,查看儀錶盤、郵件等。
儀錶盤
釘釘
郵件
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?
Redis 提供了兩種持久化方式,一種是基於快照形式的 RDB,另一種是基於日誌形式的 AOF,每種方式都有自己的優缺點,本文將介紹 Redis 這兩種持久化方式,希望閱讀本文後你對 Redis 的這兩種方式有更加全面、清晰的認識。
先從 RDB 快照方式聊起,RDB 是 Redis 默認開啟的持久化方式,並不需要我們單獨開啟,先來看看跟 RDB 相關的配置信息:
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
# save ""
# 自動生成快照的觸發機制 中間的是時間,單位秒,後面的是變更數據 60 秒變更 10000 條數據則自動生成快照
save 900 1
save 300 10
save 60 10000
# 生成快照失敗時,主線程是否停止寫入
stop-writes-on-bgsave-error yes
# 是否採用壓縮算法存儲
rdbcompression yes
# 數據恢復時是否檢測 RDB文件有效性
rdbchecksum yes
# The filename where to dump the DB
# RDB 快照生成的文件名稱
dbfilename dump.rdb
# 快照生成的路徑 AOF 也是存放在這個路徑下面
dir .
關於 RDB 相關配置信息不多,需要我們調整的就更少了,我們只需要根據自己的業務量修改生成快照的機制和文件存放路徑即可。
RDB 有兩種持久化方式:手動觸發 和 自動觸發,手動觸發使用以下兩個命令:
save:會阻塞當前 Redis 服務器響應其他命令,直到 RDB 快照生成完成為止,對於內存 比較大的實例會造成長時間阻塞,所以線上環境不建議使用
bgsave:Redis 主進程會 fork 一個子進程,RDB 快照生成有子進程來負責,完成之後,子進程自動結束,bgsave 只會在 fork 子進程的時候短暫的阻塞,這個過程是非常短的,所以推薦使用該命令來手動觸發
除了執行命令手動觸發之外,Redis 內部還存在自動觸發 RDB 的持久化機制,在以下幾種情況下 Redis 會自動觸發 RDB 持久化:
在配置中配置了 save 相關配置信息,如我們上面配置文件中的 save 60 10000 ,也可以把它歸類為“save m n”格式的配置,表示 m 秒內數據集存在 n 次修改時,會自動觸發 bgsave。
在主從情況下,如果從節點執行全量複製操作,主節點自動執行 bgsave 生成 RDB 文件併發送給從節點
執行 debug reload 命令重新加載 Redis 時,也會自動觸發 save 操作
默認情況下執行 shutdown 命令時,如果沒有開啟 AOF 持久化功能則自動執行 bgsave
上面就是 RDB 持久化的方式,可以看出 save 命令使用的比較少,大多數情況下使用的都是 bgsave 命令,所以這個 bgsave 命令還是有一些東西,那接下來我們就一起看看 bgsave 背後的原理,先從流程圖開始入手:
bgsave 命令大概有以下幾個步驟:
上面就是 bgsave 命令背後的一些內容,RDB 的內容就差不多了,我們一起來總結 RDB 持久化的優缺點,RDB 方式的優點:
有優點同樣存在缺點,RDB 的缺點有:
如果我們對數據要求比較高,每一秒的數據都不能丟,RDB 持久化方式肯定是不能夠滿足要求的,那 Redis 有沒有辦法滿足呢,答案是有的,那就是接下來的 AOF 持久化方式
Redis 默認並沒有開啟 AOF 持久化方式,需要我們自行開啟,在 redis.conf 配置文件中將 appendonly no 調整為 appendonly yes,這樣就開啟了 AOF 持久化,與 RDB 不同的是 AOF 是以記錄操作命令的形式來持久化數據的,我們可以查看以下 AOF 的持久化文件 appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$6
mykey1
$6
你好
*3
$3
set
$4
key2
$5
hello
*1
$8
大概就是長這樣的,具體的你可以查看你 Redis 服務器上的 appendonly.aof 配置文件,這也意味着我們可以在 appendonly.aof 文件中國修改值,等 Redis 重啟時將會加載修改之後的值。看似一些簡單的操作命令,其實從命令到 appendonly.aof 這個過程中非常有學問的,下面時 AOF 持久化流程圖:
在 AOF 持久化過程中有兩個非常重要的操作:一個是將操作命令追加到 AOF_BUF 緩存區,另一個是 AOF_buf 緩存區數據同步到 AOF 文件,接下來我們詳細聊一聊這兩個操作:
1、為什麼要將命令寫入到 aof_buf 緩存區而不是直接寫入到 aof 文件?
我們知道 Redis 是單線程響應,如果每次寫入 AOF 命令都直接追加到磁盤上的 AOF 文件中,這樣頻繁的 IO 開銷,Redis 的性能就完成取決於你的機器硬件了,為了提升 Redis 的響應效率就添加了一層 aof_buf 緩存層, 利用的是操作系統的 cache 技術,這樣就提升了 Redis 的性能,雖然這樣性能是解決了,但是同時也引入了一個問題,aof_buf 緩存區數據如何同步到 AOF 文件呢?由誰同步呢?這就是我們接下來要聊的一個操作:fsync 操作
2、aof_buf 緩存區數據如何同步到 aof 文件中?
aof_buf 緩存區數據寫入到 aof 文件是有 linux 系統去完成的,由於 Linux 系統調度機制周期比較長,如果系統故障宕機了,意味着一個周期內的數據將全部丟失,這不是我們想要的,所以 Linux 提供了一個 fsync 命令,fsync 是針對單個文件操作(比如這裏的 AOF 文件),做強制硬盤同步,fsync 將阻塞直到寫入硬盤完成后返回,保證了數據持久化,正是由於有這個命令,所以 redis 提供了配置項讓我們自行決定何時進行磁盤同步,redis 在 redis.conf 中提供了appendfsync 配置項,有如下三個選項:
# appendfsync always
appendfsync everysec
# appendfsync no
這就是三種磁盤同步策略,但是你有沒有注意到一個問題,AOF 文件都是追加的,隨着服務器的運行 AOF 文件會越來越大,體積過大的 AOF 文件對 redis 服務器甚至是主機都會有影響,而且在 Redis 重啟時加載過大的 AOF 文件需要過多的時間,這些都是不友好的,那 Redis 是如何解決這個問題的呢?Redis 引入了重寫機制來解決 AOF 文件過大的問題。
3、Redis 是如何進行 AOF 文件重寫的?
Redis AOF 文件重寫是把 Redis 進程內的數據轉化為寫命令同步到新 AOF 文件的過程,重寫之後的 AOF 文件會比舊的 AOF 文件占更小的體積,這是由以下幾個原因導致的:
重寫之後的 AOF 文件體積更小了,不但能夠節約磁盤空間,更重要的是在 Redis 數據恢復時,更小體積的 AOF 文件加載時間更短。AOF 文件重寫跟 RDB 持久化一樣分為手動觸發和自動觸發,手動觸發直接調用 bgrewriteaof 命令就好了,我們後面會詳細聊一聊這個命令,自動觸發就需要我們在 redis.conf 中修改以下幾個配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
滿足了這兩個條件,Redis 就會自動觸發 AOF 文件重寫,AOF 文件重寫的細節跟 RDB 持久化生成快照有點類似,下面是 AOF 文件重寫流程圖:
AOF 文件重寫也是交給子進程來完成,跟 RDB 生成快照很像,AOF 文件重寫在重寫期間建立了一個 aof_rewrite_buf 緩存區來保存重寫期間主進程響應的命令,等新的 AOF 文件重寫完成后,將這部分文件同步到新的 AOF 文件中,最後用新的 AOF 文件替換掉舊的 AOF 文件。需要注意的是在重寫期間,舊的 AOF 文件依然會進行磁盤同步,這樣做的目的是防止重寫失敗導致數據丟失,
我們知道 Redis 是基於內存的,所有的數據都存放在內存中,由於機器宕機或者其他因素重啟了就會導致我們的數據全部丟失,這也就是要做持久化的原因,當服務器重啟時,Redis 會從持久化文件中加載數據,這樣我們的數據就恢復到了重啟前的數據,在數據恢復這一塊Redis 是如何實現的?我們先來看看數據恢復的流程圖:
Redis 的數據恢複流程比較簡單,優先恢復的是 AOF 文件,如果 AOF 文件不存在時則嘗試加載 RDB 文件,為什麼 RDB 的恢復速度比 AOF 文件快,但是還是會優先加載 AOF 文件呢?我個人認為是 AOF 文件數據更全面並且 AOF 兼容性比 RDB 強,需要注意的是當存在 RDB/AOF 時,如果數據加載不成功,Redis 服務啟動會失敗。
目前互聯網上很多大佬都有 Redis 系列教程,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有所錯誤之處,還望提出,謝謝。
歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”
※網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※小三通海運與一般國際貿易有何不同?
※小三通快遞通關作業有哪些?