【故障公告】新版博客後台部署時的配置問題引發故障

最近,我們對新版博客後台(Angular 8.2.7 + .NET Core 3.0)進行了灰度發布,如果您訪問博客後台時跳轉到 ,說明使用的就是新版博客後台。

今天我們在一次基於 gitlab-ci 的自動化發布過程中,由於操作問題在發布前沒有對 appsettings.Production.json 的修改進行保存,造成容器在啟動時使用了舊版的配置文件,再加上容器的健康檢查不能檢查出這種不正常情況(這個地方的改進還沒完成),最不該的是在發布后沒有對關鍵功能進行測試驗證以及值班人員沒有及時處理用戶反饋,從而造成 18:22~19:27 期間使用新版博客后的用戶無法正常發布博文,非常抱歉由此給您帶來了麻煩,請您諒解。

我們會吸取教訓,並採取以下改進措施:

  • 更高優先級改進健康檢查。一是容器的健康檢查,二是阿里云云監控的健康檢查。當關鍵功能不可用時,讓健康檢查失敗(之前的健康檢查沒有對業務功能進行檢查)。這樣發布時如果出現問題,容器健康檢查失敗,docker swarm 就不會部署新容器。當正在運行的容器出現問題影響關鍵功能的使用時及時報警。
  • 盡可能實現在生產環境發布後用“機器人”對關鍵功能進行測試驗證。
  • 每次自動化發布時在值班群發消息通知值班人員留意用戶反饋。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

全球最長壽黑犀牛 57歲壽終正寢

摘錄自2019年12月28日中央通訊社綜合報導

坦尚尼亞保護區管理局指出,據信是全球最長壽的一頭雌黑犀牛昨天(27日)在恩戈羅恩戈羅保護區壽終正寢,享壽57歲。

恩戈羅恩戈羅保護區管理局(Ngorongoro Conservation Area Authority)28日發表聲說,名為佛斯塔(Fausta)的這頭雌黑犀牛,於12月27日在保護區內據信因自然原因死亡,牠生前絕大部分時間都是在野外生活。

恩戈羅恩戈羅保護區管理局估計,野生犀牛的壽命介於37到43歲間,圈養犀牛則能活到50歲以上。聲明指出,紀錄顯示,佛斯塔較全球任何其他犀牛都更長壽,在恩戈羅恩戈羅放養超過54年,2016年才移至庇護區。

聲明又說:「三蘭港大學(University of Dar Es Salaam)一位科學家於1965年首度在恩戈羅恩戈羅火山口發現佛斯塔,當時牠的年齡介於3至4歲間。繼多次遭鬣狗攻擊且嚴重受傷後,牠的健康狀況於2016年開始惡化,我們不得不把牠置於圈養狀態。」

而全球最長壽的白犀牛,則是55歲的雌南方白犀牛沙納(Sana),於2017年在圈養地法國奇幻星球動物園(La Planete Sauvage Zoological park)死亡。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

雷諾:2020 年前電動車續航力將增 3 至 4 成

英國金融時報 (FT) 15 日報導,雷諾 (Renault) 電動車行銷主管 Vincent Carre 表示,電池科技發展極為迅速,他們已把「里程焦慮」(range anxiety) 拋諸腦後,因為幾年內電動車的里程數將會加倍;到 2020 年前,不只里程數加倍,還會額外再增 30% 至 40% 的續航力。   這表示雷諾估計,未來電動車的里程數可達 200 英里以上。業界普遍認為,要是電動車里程超越 200 英里,將有望打入主流車款。LMC Automotive 預估,歐洲電動車、油電混合車、燃料電池車的銷售,今年將增加 30% 至 36 萬輛。    不只雷諾信心滿滿,部分專家也有類似看法。ecomento 15日引述 OilPrice.com 文章報導,電動車可能會出現飛躍成長,以往冰箱、個人電腦剛問世時,也都被視為奢侈品,但不久後就成為家庭必需品,該文認為電動車廣為接受的時刻將近,因為價格將更加經濟實惠。估計等到電池價格降到每千瓦小時 100 或 150 美元,電動車的總持有成本將可減少 75%,和一般汽車相比,極具競爭力。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

【從今天開始好好學數據結構01】數組

面試的時候,常常會問數組和鏈表的區別,很多人都回答說,“鏈表適合插入、刪除,時間複雜度O(1);數組適合查找,查找時間複雜度為O(1)”。實際上,這種表述是不準確的。數組是適合查找操作,但是查找的時間複雜度並不為O(1)。即便是排好序的數組,你用二分查找,時間複雜度也是O(logn)。所以,正確的表述應該是,數組支持隨機訪問,根據下標隨機訪問的時間複雜度為O(1)。

每一種編程語言中,基本都會有數組這種數據類型。不過,它不僅僅是一種編程語言中的數據類型,還是一種最基礎的數據結構。儘管數組看起來非常基礎、簡單,但是我估計很多人都並沒有理解這個基礎數據結構的精髓。在大部分編程語言中,數組都是從0開始編號的,但你是否下意識地想過,為什麼數組要從0開始編號,而不是從1開始呢? 從1開始不是更符合人類的思維習慣嗎?帶着這個問題來學習接下來的內容,帶着問題去學習往往效果會更好!!!

什麼是數組?我估計你心中已經有了答案。不過,我還是想用專業的話來給你做下解釋。數組(Array)是一種線性表數據結構。它用一組連續的內存空間,來存儲一組具有相同類型的數據。這個定義里有幾個關鍵詞,理解了這幾個關鍵詞,我想你就能徹底掌握數組的概念了。下面就從我的角度分別給你“點撥”一下。

第一是線性表(Linear List)。顧名思義,線性表就是數據排成像一條線一樣的結構。每個線性表上的數據最多只有前和后兩個方向。其實除了數組,鏈表、隊列、棧等也是線性表結構。而與它相對立的概念是非線性表,比如二叉樹、堆、圖等。之所以叫非線性,是因為,在非線性表中,數據之間並不是簡單的前後關係。

第二個是連續的內存空間和相同類型的數據。正是因為這兩個限制,它才有了一個堪稱“殺手鐧”的特性:“隨機訪問”。但有利就有弊,這兩個限制也讓數組的很多操作變得非常低效,比如要想在數組中刪除、插入一個數據,數組為了保持內存數據的連續性,會導致插入、刪除這兩個操作比較低效,相反的數組查詢則高效

數組java代碼:

package array;

/**
 * 1) 數組的插入、刪除、按照下標隨機訪問操作;
 * 2)數組中的數據是int類型的;
 *
 * Author: Zheng
 * modify: xing, Gsealy
 */
public class Array {
    //定義整型數據data保存數據
    public int data[];
    //定義數組長度
    private int n;
    //定義中實際個數
    private int count;

    //構造方法,定義數組大小
    public Array(int capacity){
        this.data = new int[capacity];
        this.n = capacity;
        this.count=0;//一開始一個數都沒有存所以為0
    }

    //根據索引,找到數據中的元素並返回
    public int find(int index){
        if (index<0 || index>=count) return -1;
        return data[index];
    }

    //插入元素:頭部插入,尾部插入
    public boolean insert(int index, int value){
        //數組中無元素 

        //if (index == count && count == 0) {
        //    data[index] = value;
        //    ++count;
        //    return true;
        //}

        // 數組空間已滿
        if (count == n) {
            System.out.println("沒有可插入的位置");
            return false;
        }
        // 如果count還沒滿,那麼就可以插入數據到數組中
        // 位置不合法
        if (index < 0||index > count ) {
            System.out.println("位置不合法");
            return false;
        }
        // 位置合法
        for( int i = count; i > index; --i){
            data[i] = data[i - 1];
        }
        data[index] = value;
        ++count;
        return true;
    }
    //根據索引,刪除數組中元素
    public boolean delete(int index){
        if (index<0 || index >=count) return false;
        //從刪除位置開始,將後面的元素向前移動一位
        for (int i=index+1; i<count; ++i){
            data[i-1] = data[i];
        }
        //刪除數組末尾元素  這段代碼不需要也可以
        /*int[] arr = new int[count-1];
        for (int i=0; i<count-1;i++){
            arr[i] = data[i];
        }
        this.data = arr;*/

        --count;
        return true;
    }
    public void printAll() {
        for (int i = 0; i < count; ++i) {
            System.out.print(data[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Array array = new Array(5);
        array.printAll();
        array.insert(0, 3);
        array.insert(0, 4);
        array.insert(1, 5);
        array.insert(3, 9);
        array.insert(3, 10);
        //array.insert(3, 11);
        array.printAll();
    }
}

GenericArray數組代碼

public class GenericArray<T> {
    private T[] data;
    private int size;

    // 根據傳入容量,構造Array
    public GenericArray(int capacity) {
        data = (T[]) new Object[capacity];
        size = 0;
    }

    // 無參構造方法,默認數組容量為10
    public GenericArray() {
        this(10);
    }

    // 獲取數組容量
    public int getCapacity() {
        return data.length;
    }

    // 獲取當前元素個數
    public int count() {
        return size;
    }

    // 判斷數組是否為空
    public boolean isEmpty() {
        return size == 0;
    }

    // 修改 index 位置的元素
    public void set(int index, T e) {
        checkIndex(index);
        data[index] = e;
    }

    // 獲取對應 index 位置的元素
    public T get(int index) {
        checkIndex(index);
        return data[index];
    }

    // 查看數組是否包含元素e
    public boolean contains(T e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    // 獲取對應元素的下標, 未找到,返回 -1
    public int find(T e) {
        for ( int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }


    // 在 index 位置,插入元素e, 時間複雜度 O(m+n)
    public void add(int index, T e) {
        checkIndex(index);
        // 如果當前元素個數等於數組容量,則將數組擴容為原來的2倍
        if (size == data.length) {
            resize(2 * data.length);
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }

    // 向數組頭插入元素
    public void addFirst(T e) {
        add(0, e);
    }

    // 向數組尾插入元素
    public void addLast(T e) {
        add(size, e);
    }

    // 刪除 index 位置的元素,並返回
    public T remove(int index) {
        checkIndexForRemove(index);

        T ret = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size --;
        data[size] = null;

        // 縮容
        if (size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }

        return ret;
    }

    // 刪除第一個元素
    public T removeFirst() {
        return remove(0);
    }

    // 刪除末尾元素
    public T removeLast() {
        return remove(size - 1);
    }

    // 從數組中刪除指定元素
    public void removeElement(T e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(String.format("Array size = %d, capacity = %d \n", size, data.length));
        builder.append('[');
        for (int i = 0; i < size; i++) {
            builder.append(data[i]);
            if (i != size - 1) {
                builder.append(", ");
            }
        }
        builder.append(']');
        return builder.toString();
    }


    // 擴容方法,時間複雜度 O(n)
    private void resize(int capacity) {
        T[] newData = (T[]) new Object[capacity];

        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

    private void checkIndex(int index) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed! Require index >=0 and index <= size.");
        }
    }

    private void checkIndexForRemove(int index) {
        if(index < 0 || index >= size) {
            throw new IllegalArgumentException("remove failed! Require index >=0 and index < size.");
        }
    }
}

到這裏,就回溯最初的問題:

從數組存儲的內存模型上來看,“下標”最確切的定義應該是“偏移(offset)”。前面也講到,如果用a來表示數組的首地址,a[0]就是偏移為0的位置,也就是首地址,a[k]就表示偏移k個type_size的位置,所以計算a[k]的內存地址只需要用這個公式:

a[k]_address = base_address + k * type_size

但是,如果數組從1開始計數,那我們計算數組元素a[k]的內存地址就會變為:

a[k]_address = base_address + (k-1)*type_size

對比兩個公式,我們不難發現,從1開始編號,每次隨機訪問數組元素都多了一次減法運算,對於CPU來說,就是多了一次減法指令。那你可以思考一下,類比一下,二維數組的內存尋址公式是怎樣的呢?有興趣的可以在評論區評論出來哦QAQ

數組作為非常基礎的數據結構,通過下標隨機訪問數組元素又是其非常基礎的編程操作,效率的優化就要盡可能做到極致。所以為了減少一次減法操作,數組選擇了從0開始編號,而不是從1開始。
不過我認為,上面解釋得再多其實都算不上壓倒性的證明,說數組起始編號非0開始不可。所以我覺得最主要的原因可能是歷史原因。

關於數組,它可以說是最基礎、最簡單的數據結構了。數組用一塊連續的內存空間,來存儲相同類型的一組數據,最大的特點就是支持隨機訪問,但插入、刪除操作也因此變得比較低效,平均情況時間複雜度為O(n)。在平時的業務開發中,我們可以直接使用編程語言提供的容器類,但是,如果是特別底層的開發,直接使用數組可能會更合適。

如果本文對你有一點點幫助,那麼請點個讚唄,謝謝~

最後,若有不足或者不正之處,歡迎指正批評,感激不盡!如果有疑問歡迎留言,絕對第一時間回復!

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術,說好了來了就是盆友喔…

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

為搶市占 傳 LG Chem 明年起腰斬電池價格

日廠有意增產搶市佔,車用電池龍頭 LG Chem 祭出激烈手段,據稱 2016 年起該公司電池售價將對半腰斬,藉此擊退對手。   消息人士透露,LG Chem 在中國生產的部分電動車電池,2016 年價格將減半,此舉是為了先發制人退敵,並因應中美兩國電動車需求大增。他說,LG Chem 將銷售更多電池,因此價格下殺不會拖累獲利。該公司表示,定價策略將保持彈性,價格視市況調整,是否降價仍須與客戶討論之後決定。   LG Chem 目前供應電動車電池給全球 30 多家車廠。該公司中國南京工廠將於明年啟用,增產不成問題,LG Chem 預估新廠年度產能為 10 萬顆電動車電池。該公司高層表示, LG Chem 長年研發材料、壓低成本,開始出現成效,明年起大型電池業務可望損益兩平,獲利提升讓他們決定降低售價。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

剖析nsq消息隊列(三) 消息傳輸的可靠性和持久化[二]diskqueue

,大概提了一下消息的持久化,--mem-queue-size 設置為 0,所有的消息將會存儲到磁盤。
總有人說nsq的持久化問題,消除疑慮的方法就是閱讀原碼做benchmark測試,個人感覺nsq還是很靠譜的。
nsq自己實現了一個先進先出的消息文件隊列是把消息保存到本地文件內,很值得分析一下他的實現過程。

整體處理邏輯

go-diskqueue 會啟動一個gorouting進行讀寫數據也就是方法ioLoop
會根據你設置的參數來進行數據的讀寫,流程圖如下

這個圖畫的也不是特別的準確 ioLoop 用的是 select 並不是if else 當有多個條件為true時,會隨機選一個進行執行

nsq 生成的數據大致如下:

xxxx.diskqueue.meta.dat 元數據保存了未讀消息的長度,讀取和存入數據的編號和讀取位置
xxxx.diskqueue.編號.dat 消息保存的文件,每一個消息的存儲:4Byte消息的長度+消息

參數說明

一些主要的參數和約束說明
這些參數的使用在後面的處理邏輯中會提到

// diskQueue implements a filesystem backed FIFO queue
type diskQueue struct {
    // run-time state (also persisted to disk)
    // 讀取數據的位置    
    readPos      int64
    // 寫入數據的位置
    writePos     int64
    // 讀取文件的編號    
    readFileNum  int64
    // 寫入文件的編號
    writeFileNum int64
    // 未處理的消息總數    
    depth        int64

    // instantiation time metadata
    // 每個文件的大小限制    
    maxBytesPerFile int64 // currently this cannot change once created
    // 每條消息的最小大小限制    
    minMsgSize      int32
    // 每條消息的最大大小限制    
    maxMsgSize      int32
    // 緩存消息有多少條後進行寫入    
    syncEvery       int64         // number of writes per fsync
    // 自動寫入消息文件的時間間隔    
    syncTimeout     time.Duration // duration of time per fsync
    exitFlag        int32
    needSync        bool

    // keeps track of the position where we have read
    // (but not yet sent over readChan)
    // 下一條消息的位置    
    nextReadPos     int64
    // 下一條消息的文件編號    
    nextReadFileNum int64

    // 讀取的文件
    readFile  *os.File
    // 寫入的文件    
    writeFile *os.File
    // 讀取的buffer    
    reader    *bufio.Reader
    // 寫入的buffer    
    writeBuf  bytes.Buffer

    // exposed via ReadChan()
    // 讀取數據的channel    
    readChan chan []byte

    //.....
}

數據

元數據

讀寫數據信息的元數據保存在xxxxx.diskqueue.meta.data文件內主要用到代碼里的字段如下
未處理的消息總數 depth
讀取文件的編號 readFileNum 讀取數據的位置 readPos
寫入文件的編號 writeFileNum 寫入數據的位置 writePos
真實數據如下

15
0,22
3,24

保存元數據信息

func (d *diskQueue) persistMetaData() error {
    // ...
    fileName := d.metaDataFileName()
    tmpFileName := fmt.Sprintf("%s.%d.tmp", fileName, rand.Int())
    // write to tmp file
    f, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE, 0600)
    // 元數據信息
    _, err = fmt.Fprintf(f, "%d\n%d,%d\n%d,%d\n",
        atomic.LoadInt64(&d.depth),
        d.readFileNum, d.readPos,
        d.writeFileNum, d.writePos)
    // 保存
    f.Sync()
    f.Close()
    // atomically rename
    return os.Rename(tmpFileName, fileName)
}

得到元數據信息

func (d *diskQueue) retrieveMetaData() error {
    // ...
    fileName := d.metaDataFileName()
    f, err = os.OpenFile(fileName, os.O_RDONLY, 0600)
    // 讀取數據並賦值
    var depth int64
    _, err = fmt.Fscanf(f, "%d\n%d,%d\n%d,%d\n",
        &depth,
        &d.readFileNum, &d.readPos,
        &d.writeFileNum, &d.writePos)
    //...
    atomic.StoreInt64(&d.depth, depth)
    d.nextReadFileNum = d.readFileNum
    d.nextReadPos = d.readPos
    return nil
}

消息數據

寫入一條數據

ioLoop 中發現有數據寫入時,會調用writeOne方法,把消息保存到文件內

        select {
        // ...
        case dataWrite := <-d.writeChan:
            count++
            d.writeResponseChan <- d.writeOne(dataWrite)
        // ...
func (d *diskQueue) writeOne(data []byte) error {
    var err error

    if d.writeFile == nil {
        curFileName := d.fileName(d.writeFileNum)
        d.writeFile, err = os.OpenFile(curFileName, os.O_RDWR|os.O_CREATE, 0600)
        // ...
        if d.writePos > 0 {
            _, err = d.writeFile.Seek(d.writePos, 0)
            // ...
        }
    }

    dataLen := int32(len(data))
    // 判斷消息的長度是否合法
    if dataLen < d.minMsgSize || dataLen > d.maxMsgSize {
        return fmt.Errorf("invalid message write size (%d) maxMsgSize=%d", dataLen, d.maxMsgSize)
    }
    d.writeBuf.Reset()
    // 寫入4字節的消息長度,以大端序保存
    err = binary.Write(&d.writeBuf, binary.BigEndian, dataLen)
    if err != nil {
        return err
    }
    // 寫入消息
    _, err = d.writeBuf.Write(data)
    if err != nil {
        return err
    }

    // 寫入到文件
    _, err = d.writeFile.Write(d.writeBuf.Bytes())
    // ...
    // 計算寫入位置,消息數量加1
    totalBytes := int64(4 + dataLen)
    d.writePos += totalBytes
    atomic.AddInt64(&d.depth, 1)
    // 如果寫入位置大於 單個文件的最大限制, 則持久化文件到硬盤
    if d.writePos > d.maxBytesPerFile {
        d.writeFileNum++
        d.writePos = 0

        // sync every time we start writing to a new file
        err = d.sync()
        // ...
    }
    return err
}

寫入完消息后,會判斷當前的文件大小是否已經已於maxBytesPerFile如果大,就持久化文件到硬盤,然後重新打開一個新編號文件,進行寫入。

什麼時候持久化文件到硬盤

調用sync()方法會持久化文件到硬盤,然後重新打開一個新編號文件,進行寫入。
有幾個地方調用會調用這個方法:

  • 一個寫入文件的條數達到了syncEvery的值時,也就是初始化時設置的最大的條數。會調用sync()
  • syncTimeout 初始化時設置的同步時間間隔,如果這個時間間隔到了,並且寫入的文件條數>0的時候,會調用sync()
  • 還有就是上面說過的writeOne方法,寫入完消息后,會判斷當前的文件大小是否已經已於maxBytesPerFile如果大,會調用sync()
  • 當讀取文件時,把整個文件讀取完時,會刪除這個文件並且會把needSync 設置為trueioLoop 會調用sync()
  • 還有就是Close的時候,會調用sync()
func (d *diskQueue) sync() error {
    if d.writeFile != nil {
        // 把數據 flash到硬盤,關閉文件並設置為 nil
        err := d.writeFile.Sync()
        if err != nil {
            d.writeFile.Close()
            d.writeFile = nil
            return err
        }
    }
    // 保存元數據信息
    err := d.persistMetaData()
    // ...
    d.needSync = false
    return nil
}

讀取一條數據

元數據保存着 讀取文件的編號 readFileNum 和讀取數據的位置 readPos
並且diskQueue暴露出了一個方法來,通過channel來讀取數據

func (d *diskQueue) ReadChan() chan []byte {
    return d.readChan
}

ioLoop里,當發現讀取位置小於寫入位置 或者讀文件編號小於寫文件編號,並且下一個讀取位置等於當前位置時才會讀取一條數據,然後放在一個外部全局變量 dataRead 里,並把 讀取的channel 賦值監聽 r = d.readChan,當外部有人讀取了消息,則進行moveForward操作

func (d *diskQueue) ioLoop() {
    var dataRead []byte
    var err error
    var count int64
    var r chan []byte
    for {
        // ...
        if (d.readFileNum < d.writeFileNum) || (d.readPos < d.writePos) {
            if d.nextReadPos == d.readPos {
                dataRead, err = d.readOne()
                if err != nil {
                    d.handleReadError()
                    continue
                }
            }
            r = d.readChan
        } else {
            r = nil
        }

        select {
        // ...
        case r <- dataRead:
            count++
            // moveForward sets needSync flag if a file is removed
            d.moveForward()
        // ...
        }
    }

// ...
}

readOne 從文件里讀取一條消息,4個bit的大小,然後讀取具體的消息。如果讀取位置大於最大文件限制,則close。在moveForward里會進行刪除操作

func (d *diskQueue) readOne() ([]byte, error) {
    var err error
    var msgSize int32
    // 如果readFile是nil,打開一個新的
    if d.readFile == nil {
        curFileName := d.fileName(d.readFileNum)
        d.readFile, err = os.OpenFile(curFileName, os.O_RDONLY, 0600)
        // ...
        d.reader = bufio.NewReader(d.readFile)
    }
    err = binary.Read(d.reader, binary.BigEndian, &msgSize)
    // ...
    readBuf := make([]byte, msgSize)
    _, err = io.ReadFull(d.reader, readBuf)
    totalBytes := int64(4 + msgSize)
    // ...
    d.nextReadPos = d.readPos + totalBytes
    d.nextReadFileNum = d.readFileNum
    // 如果讀取位置大於最大文件限制,則close。在moveForward里會進行刪除操作
    if d.nextReadPos > d.maxBytesPerFile {
        if d.readFile != nil {
            d.readFile.Close()
            d.readFile = nil
        }
        d.nextReadFileNum++
        d.nextReadPos = 0
    }
    return readBuf, nil
}

moveForward方法會查看讀取的編號,如果發現下一個編號 和當前的編號不同時,則刪除舊的文件。

func (d *diskQueue) moveForward() {
    oldReadFileNum := d.readFileNum
    d.readFileNum = d.nextReadFileNum
    d.readPos = d.nextReadPos
    depth := atomic.AddInt64(&d.depth, -1)

    // see if we need to clean up the old file
    if oldReadFileNum != d.nextReadFileNum {
        // sync every time we start reading from a new file
        d.needSync = true

        fn := d.fileName(oldReadFileNum)
        err := os.Remove(fn)
        // ...
    }
    d.checkTailCorruption(depth)

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

闖民居尋水源造成危險 澳洲擬射殺1萬駱駝

摘錄自2020年1月7日星島日報報導

澳洲山火持續多個月,令當地持續乾旱。由於有駱駝闖入居民的家園以尋找水源,南澳州原住民地區決定,明日起射殺1萬隻野生駱駝,並已獲當局批准。

根據外國媒體報道,環境部門會派出數架直升機射殺西北部約1萬隻野生駱駝,估計射殺行動維持五天,以控制其數量。報道又指,野生駱駝近期大量繁殖,數量難以控制,加上天氣乾旱,駱駝為了尋找水源,近年逐漸散布到近岸區域,亦不時闖入居民的家園,並破壞當地基建,威脅民眾安全。

澳洲現時約有120萬隻野生駱駝,每年排放相當於1噸溫室氣體,若不加以管制,駱駝數量將每九年翻一倍。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

繼 Here 地圖之後,戴姆勒可望與奧迪、BMW 等車廠結盟打造車用電池

  德國車廠戴姆勒(Daimler AG)今年 7 月才與另外 2 家德國汽車大廠奧迪(Audi)及 BMW 結盟,要以 25 億歐元競購 Nokia Here 地圖,據悉目前此 3 家車廠為最接近 Here 地圖的買家,而除了一同收購 Here 地圖業務外,未來這幾家德國車廠的合作,可能還會延伸到車用電池領域。   戴姆勒執行長 Dieter Zetsche 於 9 月 23 日在法蘭克福車展上表示,「德國車廠間有許多共通點」,因此除了 Here 地圖業務外,戴姆勒不排除與德國其他汽車大廠在其他領域上有所合作,甚至對此持開放態度,而下一步可能就是針對電池業務做結盟。   近來戴姆勒似乎頻頻釋出要與其他企業針對不同領域合作的意願,8 月底 Zetsche 才對外透露,考慮與蘋果及 Google 等公司成立合資企業,共同研發自動駕駛車。       (首圖來源:Flickr/ CC BY 2.0)

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

電動車窗開關瑕疵 豐田全球召修 650 萬車

全球最大車廠日本豐田汽車(Toyota)因為電動車窗開關瑕疵,將在全球召修 650 萬車。 豐田說,這個瑕疵有火災風險。 這是豐田最新一起召修舉動作,近幾個月來,豐田已 因為造成全球多起死亡事件的安全氣囊瑕疵而召修近 1,000 萬輛車。   豐田汽車說,這些車子的電動車窗主控開關瑕疵,可能會造成電線短路的問題,引起零件因過熱而融化,進而造成冒煙,然後起火。必須召修的車款包括 2005 年至 2010 年生產的Yaris, Corolla, Camry及RAV4 休旅車。其中約有 270 萬輛在北美售出,120 萬輛在歐洲售出。豐田汽車表示,目前還沒有接獲因此問題造成的意外或傷亡。  

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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

GitHub 發布了官方 App,還打算冰封你的代碼一千年

11 月 13 日,GitHub Universe 2019 開發者大會上,公布了大量新功能,包括髮布 GitHub 移動版、GitHub Actions 和 Packages 正式版上市、重新設計了通知體驗,以及宣布建設一個數據倉庫存儲所有公共倉庫一千年…

GitHub App

官方 App 終於來了,在手機上就可以 Review 代碼、處理 Issues、合併 PR 等常用操作。讓你無論身處何處,都可以靈活地推進工作並與團隊保持聯繫(加班)。

目前已經發布 iOS beta 版並上線 TestFlight,連黑暗模式都安排上了。

Android 版還要等段時間推出。

Actions 和 Packages 正式版

去年 Universe 大會上發布的 GitHub Actions,以及今年上半年發布的 GitHub Packages 結束了測試期。

GitHub Actions 是倉庫自帶的自動化構建與部署功能,擁有完整的 CI/CD 解決方案和本機軟件包管理,並且 GitHub Marketplace 上也提供有上千款社區開發的工作流可以使用。

GitHub Packages 是官方推出的包管理功能,實現將源代碼和包在一個地方集成權限管理和統計,託管在 GitHub 上的軟件包,不僅包括詳細信息和下載統計信息,而且還有它們的全部歷史記錄。

現在這兩款功能免費向所有公共倉庫開放,並且可以使用 Actions 將新的軟件包版本自動發布到 GitHub Packages,使用 Actions 觸發軟件包安裝,以及以最小的配置安裝託管在 GitHub Packages 或首選記錄註冊表上的軟件包和映像。通過 Actions 使用相同的 GITHUB_TOKEN 進行所有自動化程序包的上載和下載。

通知功能

以往在一些熱門開源項目中、或者大型團隊中,各種 Issues 和 PR 的通知郵件會無差別充斥在郵箱中,很難將信號和噪音區分開。

官方終於推出了通知功能來解決這個問題,設立了通知收件箱,可以對不同信息進行篩選和分類,讓你聚焦到有價值的信息中。

並且會着重結合官方 App 進行通知推送。

目前該功能是內測階段,如果想體驗可以下載 GitHub App 獲取測試資格。

代碼導航

代碼導航已於今年早些時候在特定存儲庫的有限公測中發布。今天,官方將其提供給 GitHub 上的所有 Ruby,Python 和 Go 存儲庫,並且很快就會有更多的語言。查看任何公共或私有存儲庫上的 “code” 選項卡,以獲取受支持的語言以進行嘗試。

代碼搜索

在接下來的幾個月中,官方將引入一種新的代碼搜索體驗,包括區分大小寫,特殊字符,標記化和其他特定於代碼的搜索條件。

保存你的代碼一千年

官方發布了一項偉大的計劃:將建設一個名為 GitHub Arctic Code Vault 的數據存儲庫,存儲在北極世界檔案館(AWA)中,這是一個長期的檔案設施,位於北極山永久凍土層深 250 米。

GitHub 將在 2020 年 2 月 2 日掃描所有活動公共存儲庫的快照,並將這些數據保存在 Arctic Code Vault 中。

官方博客這樣寫道:

我們與斯坦福圖書館,Long Now 基金會,Internet 檔案館,軟件遺產基金會,Piql,Microsoft Research 和 Bodleian 圖書館建立了合作夥伴關係,以保護全球的開源代碼。我們將通過跨各種數據格式和位置存儲多個副本來保護這種知識,包括一個旨在保存至少一千年的非常長期的檔案庫,稱為 GitHub Arctic Code Vault。

本文屬於原創,首發於微信公眾號「面向人生編程」,如需轉載請後台留言。

關注后回復以下信息獲取更多資源
回復【資料】獲取 Python / Java 等學習資源
回復【插件】獲取爬蟲常用的 Chrome 插件
回復【知乎】獲取最新知乎模擬登錄

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

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