數據結構 9 基礎數據結構 二叉堆 了解二叉堆的元素插入、刪除、構建二叉堆的代碼方式_網頁設計公司

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

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

是否記得我們在之前的學習中有學習到二叉樹 忘記的小夥伴們請查看:完全二叉樹的定義。

https://blogs.chaobei.xyz/archives/shuju2

二叉堆

二叉堆其實就是一個完全二叉樹 一起複習一下吧:關於二叉樹和滿二叉樹以及完全二叉樹的基本概念。

二叉樹

  • 每個節點下掛元素不超過2
  • 並且元素都是按照一定規律排列的

二叉樹規律

按照前人的總結,我們可以得出以下結論。

  • 一個深度為K 的二叉樹,最多包含節點數 2的k次方-1
  • 二叉樹指定n 層級所包含的節點數為 2的n-1次方

滿二叉樹

從字面意思我們可以理解到:這個二叉樹它是一種飽和的狀態,顧名思義稱作是滿二叉樹。

完全二叉樹

除去二叉樹的恭弘=叶 恭弘子節點,所有節點都包含有兩個節點,並且節點都是按照一定順序排列的,這樣的二叉樹被稱作是完全二叉樹

二叉堆類型

在上面我們已經提到過。二叉堆就是一種完全二叉樹、二叉樹的概念也已經了解到了。當然,現在應該分析二叉堆有有哪些性質

  • 最大堆
  • 最小堆

最大堆

最大堆的父節點元素的值都大於等於其兩個子元素的值

最小堆

反之,最小堆父節點元素的值,都小於等於其兩個子元素的值

二叉堆的堆頂部 則是這個堆序列最大或者最小的元素。

二叉堆的自我調整

二叉堆的自我調整,有以下幾種情況:

  • 新元素的插入
  • 元素的刪除
  • 構建二叉堆

我們以最上面的最小堆為例,講述如何將一個元素插入二叉堆、如何刪除一個元素、如何來構建一個二叉堆。

插入一個節點

按照上面最小堆的順序,我這裏再插入幾個其他元素方便觀察。假設這裏我插入一個元素1

元素1<元素6 進行上浮。子節點與父節點進行調換位置

元素1<元素3 進行上浮。子節點與父節點進行調換位置

元素1<元素2 進行上浮。到達堆頂部。

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

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

刪除一個元素

當前元素與子節點中最小元素進行比較、大於則交換位置下沉

假設我們刪除最頂層的元素1

二叉堆為了保證樹的結構、將二叉堆裏面尾部元素6填充到被刪除的位置。

當前元素6 與兩個子元素裏面最小元素 進行比較。大於則下沉

當前元素6 > 3 進行調換位置。元素6到達尾部。

規律:二叉堆的刪除元素和新增元素剛好是相反的

構建一個二叉堆

構建二叉堆、其實就是將一個原有的、無序的、完全二叉樹給他構建成有序的二叉堆。

假設我們來構建一個最小堆 我們這裏拿到一個無序的完全二叉樹如圖:

划重點:構建最小堆就是將非恭弘=叶 恭弘子節點進行下沉

1、操作元素5 元素5小於元素8 不進行移動

2、操作元素1 元素1小於元素5 不進行移動

3、操作元素7 省略步驟。最終結果如下:

4、操作元素3 省略步驟。最終結果如下:

至此,我們的無序完全二叉樹已經變成了一個有序的二叉最小堆

代碼實現

二叉堆雖然是一顆完全二叉樹,但是其存儲方式是順序存儲,使用的是數組、而不是鏈式指針。

我們可以發現如下規律:

  • 父元素左邊子元素位置 = 2*父元素下標 + 1
  • 父元素右邊子元素位置 = 2*父元素下標 + 2
public static void main(String[] args) {
        int[] array = {7, 1, 3, 5, 6, 4, 2, 8, 9};
        buildBinaryHeap(array);
        System.out.println(Arrays.toString(array));
    }

    public static void buildBinaryHeap(int[] array) {
        //除去恭弘=叶 恭弘子節點、將每個節點進行下沉操作
        for (int i = (array.length - 2) / 2; i >= 0; i--) {
            sinking(array, i);
        }
    }

    /**
     * 構建二叉堆、讓當前元素下沉
     *
     * @param array     被操作的數組
     * @param itemIndex 當前元素下標
     */
    public static void sinking(int[] array, int itemIndex) {
        //數組長度
        int length = array.length - 1;
        //父節點值
        int parent = array[itemIndex];

        //默認操作的是左孩子
        int childIndex = 2 * itemIndex + 1;

        while (childIndex < length) {

            //存在右邊子元素、並且右邊子元素值小於左邊
            if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
                //切換到右邊元素
                childIndex++;
            }

            //小於等於則無需交換
            if (parent <= array[childIndex]) break;

            //無需交換、只需要將子元素移動到父元素位置即可
            array[itemIndex] = array[childIndex];
            itemIndex = childIndex;

            //改變左右子元素的下標
            childIndex = 2 * itemIndex + 1;
        }
        //最終將父元素移動到指定位置即可。
        array[itemIndex] = parent;
    }

代碼示例

https://gitee.com/mrc1999/Data-structure

小結

通過本節的學習,應該需要掌握二叉堆這個重要的數據結構、如何將一個完全二叉樹構建成一個二叉堆、並且二叉堆在插入元素、和刪除元素時候如何將原來的結構保持不變的。這該是我們學習的。
下一節將繼續學習二叉堆的堆排序、我們一起加油!

參考

https://mp.weixin.qq.com/s/cq2EhVtOTzTVpNpLDXfeJg

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

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

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

線程池續:你必須要知道的線程池submit()實現原理之FutureTask!_台北網頁設計

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

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

前言

上一篇內容寫了Java中線程池的實現原理及源碼分析,說好的是實實在在的大滿足,想通過一篇文章讓大家對線程池有個透徹的了解,但是文章寫完總覺得還缺點什麼?

上篇文章只提到線程提交的execute()方法,並沒有講解線程提交的submit()方法,submit()有一個返回值,可以獲取線程執行的結果Future<T>,這一講就那深入學習下submit()FutureTask實現原理。

使用場景&示例

使用場景

我能想到的使用場景就是在并行計算的時候,例如一個方法中調用methodA()、methodB(),我們可以通過線程池異步去提交方法A、B,然後在主線程中獲取組裝方法A、B計算后的結果,能夠大大提升方法的吞吐量。

使用示例

/**
 * @author wangmeng
 * @date 2020/5/28 15:30
 */
public class FutureTaskTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService threadPool = Executors.newCachedThreadPool();

        System.out.println("====執行FutureTask線程任務====");
        Future<String> futureTask = threadPool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("FutureTask執行業務邏輯");
                Thread.sleep(2000);
                System.out.println("FutureTask業務邏輯執行完畢!");
                return "歡迎關注: 一枝花算不算浪漫!";
            }
        });

        System.out.println("====執行主線程任務====");
        Thread.sleep(1000);
        boolean flag = true;
        while(flag){
            if(futureTask.isDone() && !futureTask.isCancelled()){
                System.out.println("FutureTask異步任務執行結果:" + futureTask.get());
                flag = false;
            }
        }

        threadPool.shutdown();
    }
}

上面的使用很簡單,submit()內部傳遞的實際上是個Callable接口,我們自己實現其中的call()方法,我們通過futureTask既可以獲取到具體的返回值。

submit()實現原理

submit() 是也是提交任務到線程池,只是它可以獲取任務返回結果,返回結果是通過FutureTask來實現的,先看下ThreadPoolExecutor中代碼實現:

public class ThreadPoolExecutor extends AbstractExecutorService {
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
}

public abstract class AbstractExecutorService implements ExecutorService {
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
}

提交任務還是執行execute()方法,只是task被包裝成了FutureTask ,也就是在excute()中啟動線程後會執行FutureTask.run()方法。

再來具體看下它執行的完整鏈路圖:

上圖可以看到,執行任務並返回執行結果的核心邏輯實在FutureTask中,我們以FutureTask.run/get 兩個方法為突破口,一點點剖析FutureTask的實現原理。

FutureTask源碼初探

先看下FutureTask中部分屬性:

public class FutureTask<V> implements RunnableFuture<V> {
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    private Callable<V> callable;
    private Object outcome;
    private volatile Thread runner;
    private volatile WaitNode waiters;
}
  1. state

當前task狀態,共有7中類型。
NEW: 當前任務尚未執行
COMPLETING: 當前任務正在結束,尚未完全結束,一種臨界狀態
NORMAL:當前任務正常結束
EXCEPTIONAL: 當前任務執行過程中發生了異常。
CANCELLED: 當前任務被取消
INTERRUPTING: 當前任務中斷中..
INTERRUPTED: 當前任務已中斷

  1. callble

用戶提交任務傳遞的Callable,自定義call方法,實現業務邏輯

  1. outcome

任務結束時,outcome保存執行結果或者異常信息。

  1. runner

當前任務被線程執行期間,保存當前任務的線程對象引用

  1. waiters

因為會有很多線程去get當前任務的結果,所以這裏使用了一種stack數據結構來保存

FutureTask.run()實現原理

我們已經知道在線程池runWorker()中最終會調用到FutureTask.run()方法中,我們就來看下它的執行原理吧:

具體代碼如下:

public class FutureTask<V> implements RunnableFuture<V> {
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
}

首先是判斷FutureTaskstate狀態,必須是NEW才可以繼續執行。

然後通過CAS修改runner引用為當前線程。

接着執行用戶自定義的call()方法,將返回結果設置到result中,result可能為正常返回也可能為異常信息。這裏主要是調用set()/setException()

FutureTask.set()實現原理

set()方法的實現很簡單,直接看下代碼:

public class FutureTask<V> implements RunnableFuture<V> {
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
            finishCompletion();
        }
    }
}

call()返回的數據賦值給全局變量outcome上,然後修改state狀態為NORMAL,最後調用finishCompletion()來做掛起線程的喚醒操作,這個方法等到get()後面再來講解。

FutureTask.get()實現原理

接着看下代碼:

public class FutureTask<V> implements RunnableFuture<V> {
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
}

如果FutureTaskstateNORMAL或者COMPLETING,說明當前任務並沒有執行完成,調用get()方法會被阻塞,具體的阻塞邏輯在awaitDone()方法:

private int awaitDone(boolean timed, long nanos) throws InterruptedException {

        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING)
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

這個方法可以說是FutureTask中最核心的方法了,一步步來分析:

如果timed不為空,這說明指定nanos時間還未返回結果,線程就會退出。

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

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

q是一個WaitNode對象,是將當前引用線程封裝在一個stack數據結構中,WaitNode對象屬性如下:

 static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}

接着判斷當前線程是否中斷,如果中斷則拋出中斷異常。

下面就進入一輪輪的if... else if...判斷邏輯,我們還是採用分支的方式去分析。

分支一:if (s > COMPLETING) {

此時get()方法已經有結果了,無論是正常返回的結果,還是異常、中斷、取消等,此時直接返回state狀態,然後執行report()方法。

分支二:else if (s == COMPLETING)

條件成立,說明當前任務接近完成狀態,這裏讓當前線程再釋放cpu,進行下一輪搶佔cpu

分支三:else if (q == null)

第一次自旋執行,WaitNode還沒有初始化,初始化q=new WaitNode();

分支四:else if (!queued){

queued代表當前線程是否入棧,如果沒有入棧則進行入棧操作,順便將全局變量waiters指向棧頂元素。

分支五/六:LockSupport.park

如果設置了超時時間,則使用parkNanos來掛起當前線程,否則使用park()

經過這麼一輪自旋循環后,如果執行call()還沒有返回結果,那麼調用get()方法的線程都會被掛起。

被掛起的線程會等待run()返回結果后依次喚醒,具體的執行邏輯在finishCompletion()中。

最終stack結構中數據如下:

FutureTask.finishCompletion()實現原理

具體實現代碼如下:

private void finishCompletion() {
    for (WaitNode q; (q = waiters) != null;) {
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    LockSupport.unpark(t);
                }
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null;
                q = next;
            }
            break;
        }
    }

    done();

    callable = null;
}

代碼實現很簡單,看過get()方法后,我們知道所有調用get()方法的線程,在run()還沒有返回結果前,都會保存到一個有WaitNode構成的statck數據結構中,而且每個線程都會被掛起。

這裡是遍歷waiters棧頂元素,然後依次查詢起next節點進行喚醒,喚醒后的節點接着會往後調用report()方法。

FutureTask.report()實現原理

具體代碼如下:

private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

這個方法很簡單,因為執行到了這裏,說明當前state狀態肯定大於COMPLETING,判斷如果是正常返回,那麼返回outcome數據。

如果state是取消狀態,拋出CancellationException異常。

如果狀態都不滿足,則說明執行中出現了差錯,直接拋出ExecutionException

FutureTask.cancel()實現原理

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally {
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

cancel()方法的邏輯很簡單,就是修改state狀態為CANCELLED,然後調用finishCompletion()來喚醒等待的線程。

這裏如果mayInterruptIfRunning,就會先中斷當前線程,然後再去喚醒等待的線程。

總結

FutureTask的實現原理其實很簡單,每個方法基本上都畫了一個簡單的流程圖來方便立即。

後面還打算分享一個BlockingQueue相關的源碼解讀,這樣線程池也可以算是完結了。

在這之前可能會先分享一個SpringCloud常見配置代碼分析、最佳實踐等手冊,方便工作中使用,也是對之前看過的源碼一種總結。敬請期待!
歡迎關注:

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

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

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

我天!xx.equals(null) 是什麼騷操作??_網頁設計公司

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

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

問題背景

我的天,最近做 Code Review 看到一個同事的騷操作,他寫了一個工具類,大概是這樣的:

public static boolean isNull(Object object){
    return null == object || object.equals(null);
}

判斷空,一般不是 null == object 就夠了,object.equals(null) 是什麼騷操作?

寫程序這麼多年,第一次看這樣的寫法,當時我就提出質疑了,同事拍着胸脯和我說,有個銀行的請求參數必須得這麼寫,不然就驗證不了。

我當時還在想,這是 JDK 出的什麼新類型么,覺得還是不科學,考慮去跟下同事寫的代碼,然後用他所說的情況我親自去驗證一下。

看了下,這是個老業務系統,同事用了 json-lib 這個包,歷史的江湖確實有這個包的存在,棧長之前也用過,不過後來這玩意就沒怎麼用了,現在都是 GsonJackson 的天下了。

如下面 json-lib 例子所示:

public static void main(String[] args) {
    String jsonString = "{\"name\": \"hi\",\"sex\": \"boy\", \"age\": null}";

    JSONObject jsonObject = net.sf.json.JSONObject.fromObject(jsonString);
    Object age = jsonObject.get("age");
    
    // 輸出:null
    System.out.println("age: " + age);

    // 輸出:false
    System.out.println("age == null: " + (age == null));
    
    // 輸出:true
    System.out.println("age.equals(null): " + (age.equals(null)));
}

我天!大家看到結果了吧,問題確實也如同事所說,一定要用 object.equals(null) 寫法才行,不相信結果的大家也可以親自驗證一下。

納了悶了,這樣寫,我傳一個 null 值過去不是報空指針了么?這樣寫肯定有問題,繼續深挖!

問題分析

fromObject 方法加載 JSON 串開始源碼深入分析,找到了這個神奇解析 null 值的源碼:

原來,JSON 串中的 null 值被解析成了它內部的 JSONNull 對象,然後再看下這個 JSONNull 的 equals 方法源碼:

public boolean equals(Object object) {
    return object == null || 
           object == this || 
           object == instance || 
           object instanceof JSONObject &&
           ((JSONObject)object).isNullObject() ||
           "null".equals(object);
}

問題就出在他所用的 JSON 工具類了!!!

equals 方法被重寫了……終於揭開了 object.equals(null) 的神秘面紗……

再來看下是否有新的更新包:

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

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

最新的版本停留在 2010 年 12 月,已經是被淘汰的東西了。

另外,json-lib 在 JDK 1.7+ 有性能影響。
推薦閱讀:請不要在 JDK 7+ 中使用這個 JSON 包了

解決方案

方法1:

換掉 object.equals(null),用 JSONNull 的實例去判斷:

public static boolean isNull(Object object){
    return null == object || JSONNull.getInstance().equals(object);
}

方法2:

換掉 json-lib 庫,用主流的 GsonJackson

具體看下這篇:Java常用的幾個Json庫,性能強勢對比,另外 FastJson 也不建議用了,漏洞比較多。

這個由於是老系統,太多業務使用了這個庫,換掉的開發、測試成本和風險比較大,暫時考慮先用方案1先解決這個問題。

關注Java技術棧微信公眾號,棧長將繼續分享好玩的 Java 技術,公眾號第一時間推送,在公眾號後台回復:Java,可以獲取歷史 Java 教程,都是乾貨。

推薦去我的博客閱讀更多:

1.Java JVM、集合、多線程、新特性系列教程

2.Spring MVC、Spring Boot、Spring Cloud 系列教程

3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

4.Java、後端、架構、阿里巴巴等大廠最新面試題

覺得不錯,別忘了點贊+轉發哦!

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

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

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

手把手教你學numpy,從此數據處理不再慌【三】_網頁設計

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

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是numpy專題的第三篇,我們來聊聊numpy當中的索引。

上篇的末尾其實我們簡單地提到了索引,但是沒有過多深入。沒有過多深入的原因也很簡單,因為numpy當中關於索引的用法實在是很多,並不是我們想的那樣用一個下標去獲取數據就完事了。

所以我整理了一下相關的用法,把關於索引的使用簡單分成了幾類,我們一個一個來看。

切片索引

切片我們都熟悉,用冒號將兩個數隔開,表示一個區間的上界和下界。通過這種方式訪問這個區間內的所有元素。

這一點我們之前介紹過,我們簡單回顧一下。

這是一維數組的切片,既然一維數組可以切片,那麼同樣高維數組也可以切片。我們來看一個二維的數組的切片:

我們生成了一個3 x 4的二維數組,然後通過切片獲取了它的1-2數據。由於我們是對行切片,默認保留這一行的所有數據。

如果我們並不需要所有數據,而是只需要某一列的固定數據,可以寫成這樣:

這一行代碼的意思是對於行我們獲取1-3行固定第二列的數據,我們用表格表示的話大概是下面這個樣子:

我們也可以對兩個維度同時切片,這樣可以得到更加複雜的數據:

這樣切片獲得的數據大概是這樣的:

也就是說在numpy的數組當中各個維度是分開的,每一個維度都支持切片。我們可以根據我們的需要切片或者是固定下標來獲取我們想要的切片。

bool型索引

numpy當中還有一個非常好用的索引方式叫做bool型索引。前文介紹廣播的時候曾經介紹過,當我們將兩個大小不一致的數組進行計算的時候,numpy會自動幫我們將它們廣播成大小一致的情況再進行運算。

而邏輯判斷其實也是一種運算,所以如果我們將邏輯運算應用在numpy數組上的話同樣會得到一個numpy數組,只不過是bool類型的numpy數組。

我們來看一個例子:

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

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

我們創建了一個numpy的數組,然後將它和整數4進行比較,numpy會將這個運算廣播到其中每一個元素當中,然後返回得到一個bool類型的numpy數組。

這個bool類型的數組可以作為索引,傳入另外一個數組當中,只有bool值為true的行才會被保留。

我們發現只有第4行和第6行的數據被保留了,也就是bool值為true的行號被保留了。這是非常有用的數據獲取方式,我們可以直接將判斷條件放入索引當中進行數據的過濾,如果應用熟練了會非常方便。

再舉個例子,假如我們要根據二維數據的第一列的數據進行過濾,僅僅保留第一列數據大於0.5的。如果按照傳統的方法我們需要用一個循環去過濾,但是使用bool類型索引,我們可以只需要一行搞定:

arr[arr[:, 0] > 0.5]

如果有多個條件,我們可以用位運算的與或非進行連接。在Python當中位運算的與或非分別用符號&, |, ~表示。

舉個例子,比如我們想要篩選出arr數組當中第1列大於0.5,並且第二列小於0.5的數據,我們可以寫成這樣:

arr[(arr[:, 0] > 0.5) & (arr[:, 1] < 0.5)]

如果我們想求這個條件的相反條件,我們當然可以將判斷條件反過來寫,但是也可以通過~符號直接取反:

arr[~((arr[:, 0] > 0.5) & (arr[:, 1] < 0.5))]

花式索引

除了bool索引之外,numpy當中還支持一種花式索引。

所謂的花式索引,意思是說支持將另外一個數組當做是索引來訪問數據

舉個很簡單的例子:

從上面的例子我們可以看出來,我們把idx這個數組當中的值當做了索引進行了數據的訪問。並且有重複值也沒有關係,numpy不會進行去重。

通過數組訪問數據有什麼用呢?其實非常有用,在我們做機器學習的過程當中,我們經常涉及到一個採樣的問題。我們每次訓練並不是全量的數據,否則非常慢,有時候甚至是不可能完成的,因為數據量太大了。我們往往是抽取出一批數據作為一個batch來訓練的,這個在之前批量梯度下降的文章當中曾經提到過。

那麼一個batch的數據是怎麼抽取的呢?就是這樣抽取的,我們會調用np中的一個函數叫做choice,我們用它來從所有樣本的下標當中抽取我們指定數量的下標。

有了下標數組之後,我們用一下花式索引就可以拿到對應的全部數據了,如果你看過大牛寫的深度學習的代碼,裏面幾乎都是這樣實現的。

總結

今天關於numpy當中索引的使用和介紹就到這裏,僅僅看介紹可能感受並不明顯。但如果上手用numpy做過一次數據處理和實現過機器學習的模型,相信一定可以感受到它的易用性和強大的功能。索引這個功能非常常用,也非常重要,在後序的pandas庫當中同樣沿用了numpy中對於索引的設定和功能。因此這既是重要的基本功,也是為後面的學習打基礎。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

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

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

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

重學 Java 設計模式:實戰單例模式_貨運

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

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

作者:小傅哥
博客:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言

5個創建型模式的最後一個

在設計模式中按照不同的處理方式共包含三大類;創建型模式結構型模式行為模式,其中創建型模式目前已經介紹了其中的四個;工廠方法模式抽象工廠模式生成器模式原型模式,除此之外還有最後一個單例模式

掌握了的知識才是自己的

在本次編寫的重學 Java 設計模式的編寫中盡可能多的用各種場景案例還介紹設計的使用,包括我們已經使用過的場景;各種類型獎品發放多套Redis緩存集群升級裝修公司報價清單百份考卷題目與答案亂序,通過這些場景案例的實踐感受設計模式的思想。但這些場景都是作者通過經驗分離出來的,還並不是讀者的知識,所以你如果希望可以融會貫通的掌握那麼一定要親力親為的操作,事必躬親的完成。

書不是看的是用的

在這裏還是想強調一下學習方法,總有很多小夥伴對學習知識有疑惑,明明看了、看的時候也懂了,但到了實際使用的時候卻用不上。或者有時候在想是不要是有更加生動的漫畫或者什麼對比會好些,當然這些方式可能會加快一個新人對知識的理解速度。但只要你把學習視頻當電影看、學習書籍當故事看,就很難掌握這項技術棧。只有你把它用起來,逐字逐句的深挖,一點點的探求,把各項遇到的盲點全部掃清,才能讓你真的掌握這項技能。

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程1個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)

三、單例模式介紹

單例模式可以說是整個設計中最簡單的模式之一,而且這種方式即使在沒有看設計模式相關資料也會常用在編碼開發中。

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

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

因為在編程開發中經常會遇到這樣一種場景,那就是需要保證一個類只有一個實例哪怕多線程同時訪問,並需要提供一個全局訪問此實例的點。

綜上以及我們平常的開發中,可以總結一條經驗,單例模式主要解決的是,一個全局使用的類頻繁的創建和消費,從而提升提升整體的代碼的性能。

四、案例場景

本章節的技術所出現的場景非常簡單也是我們日常開發所能見到的,例如;

  1. 數據庫的連接池不會反覆創建
  2. spring中一個單例模式bean的生成和使用
  3. 在我們平常的代碼中需要設置全局的的一些屬性保存

在我們的日常開發中大致上會出現如上這些場景中使用到單例模式,雖然單例模式並不複雜但是使用面卻比較廣。

五、7種單例模式實現

單例模式的實現方式比較多,主要在實現上是否支持懶漢模式、是否線程安全中運用各項技巧。當然也有一些場景不需要考慮懶加載也就是懶漢模式的情況,會直接使用static靜態類或屬性和方法的方式進行處理,供外部調用。

那麼接下來我們就通過實現不同方式的實現進行講解單例模式。

0. 靜態類使用

public class Singleton_00 {

    public static Map<String,String> cache = new ConcurrentHashMap<String, String>();
    
}
  • 以上這種方式在我們平常的業務開發中非常場常見,這樣靜態類的方式可以在第一次運行的時候直接初始化Map類,同時這裏我們也不需要到延遲加載在使用。
  • 在不需要維持任何狀態下,僅僅用於全局訪問,這個使用使用靜態類的方式更加方便。
  • 但如果需要被繼承以及需要維持一些特定狀態的情況下,就適合使用單例模式。

1. 懶漢模式(線程不安全)

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {
    }

    public static Singleton_01 getInstance(){
        if (null != instance) return instance;
        return new Singleton_01();
    }

}
  • 單例模式有一個特點就是不允許外部直接創建,也就是new Singleton_01(),因此這裡在默認的構造函數上添加了私有屬性 private
  • 目前此種方式的單例確實滿足了懶加載,但是如果有多個訪問者同時去獲取對象實例你可以想象成一堆人在搶廁所,就會造成多個同樣的實例並存,從而沒有達到單例的要求。

2. 懶漢模式(線程安全)

public class Singleton_02 {

    private static Singleton_02 instance;

    private Singleton_02() {
    }

    public static synchronized Singleton_02 getInstance(){
        if (null != instance) return instance;
        return new Singleton_02();
    }

}
  • 此種模式雖然是安全的,但由於把鎖加到方法上后,所有的訪問都因需要鎖佔用導致資源的浪費。如果不是特殊情況下,不建議此種方式實現單例模式。

3. 餓漢模式(線程安全)

public class Singleton_03 {

    private static Singleton_03 instance = new Singleton_03();

    private Singleton_03() {
    }

    public static Singleton_03 getInstance() {
        return instance;
    }

}
  • 此種方式與我們開頭的第一個實例化Map基本一致,在程序啟動的時候直接運行加載,後續有外部需要使用的時候獲取即可。
  • 但此種方式並不是懶加載,也就是說無論你程序中是否用到這樣的類都會在程序啟動之初進行創建。
  • 那麼這種方式導致的問題就像你下載個遊戲軟件,可能你遊戲地圖還沒有打開呢,但是程序已經將這些地圖全部實例化。到你手機上最明顯體驗就一開遊戲內存滿了,手機卡了,需要換了。

4. 使用類的內部類(線程安全)

public class Singleton_04 {

    private static class SingletonHolder {
        private static Singleton_04 instance = new Singleton_04();
    }

    private Singleton_04() {
    }

    public static Singleton_04 getInstance() {
        return SingletonHolder.instance;
    }

}
  • 使用類的靜態內部類實現的單例模式,既保證了線程安全有保證了懶加載,同時不會因為加鎖的方式耗費性能。
  • 這主要是因為JVM虛擬機可以保證多線程併發訪問的正確性,也就是一個類的構造方法在多線程環境下可以被正確的加載。
  • 此種方式也是非常推薦使用的一種單例模式

5. 雙重鎖校驗(線程安全)

public class Singleton_05 {

    private volatile static Singleton_05 instance;

    private Singleton_05() {
    }

    public static Singleton_05 getInstance(){
       if(null != instance) return instance;
       synchronized (Singleton_05.class){
           if (null == instance){
               instance = new Singleton_05();
           }
       }
       return instance;
    }

}
  • 雙重鎖的方式是方法級鎖的優化,減少了部分獲取實例的耗時。
  • 同時這種方式也滿足了懶加載。
  • volatile關鍵字會強制的保證線程的可見性,而不加這個關鍵字,JVM也會儘力去保證可見性,但如果CPU一直處於繁忙狀態就不確定了。

6. CAS「AtomicReference」(線程安全)

public class Singleton_06 {

    private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();

    private static Singleton_06 instance;

    private Singleton_06() {
    }

    public static final Singleton_06 getInstance() {
        for (; ; ) {
            Singleton_06 instance = INSTANCE.get();
            if (null != instance) return instance;
            INSTANCE.compareAndSet(null, new Singleton_06());
            return INSTANCE.get();
        }
    }

    public static void main(String[] args) {
        System.out.println(Singleton_06.getInstance()); // org.itstack.demo.design.Singleton_06@2b193f2d
        System.out.println(Singleton_06.getInstance()); // org.itstack.demo.design.Singleton_06@2b193f2d
    }

}
  • java併發庫提供了很多原子類來支持併發訪問的數據安全性;AtomicIntegerAtomicBooleanAtomicLongAtomicReference
  • AtomicReference 可以封裝引用一個V實例,支持併發訪問如上的單例方式就是使用了這樣的一個特點。
  • 使用CAS的好處就是不需要使用傳統的加鎖方式保證線程安全,而是依賴於CAS的忙等算法,依賴於底層硬件的實現,來保證線程安全。相對於其他鎖的實現沒有線程的切換和阻塞也就沒有了額外的開銷,並且可以支持較大的併發性。
  • 當然CAS也有一個缺點就是忙等,如果一直沒有獲取到將會處於死循環中。

7. Effective Java作者推薦的枚舉單例(線程安全)

public enum Singleton_07 {

    INSTANCE;
    public void test(){
        System.out.println("hi~");
    }

}

約書亞·布洛克(英語:Joshua J. Bloch,1961年8月28日-),美國著名程序員。他為Java平台設計並實作了許多的功能,曾擔任Google的首席Java架構師(Chief Java Architect)。

  • Effective Java 作者推薦使用枚舉的方式解決單例模式,此種方式可能是平時最少用到的。
  • 這種方式解決了最主要的;線程安全、自由串行化、單一實例。

調用方式

@Test
public void test() {
    Singleton_07.INSTANCE.test();

這種寫法在功能上與共有域方法相近,但是它更簡潔,無償地提供了串行化機制,絕對防止對此實例化,即使是在面對複雜的串行化或者反射攻擊的時候。雖然這中方法還沒有廣泛採用,但是單元素的枚舉類型已經成為實現Singleton的最佳方法。

但也要知道此種方式在存在繼承場景下是不可用的。

六、總結

  • 雖然只是一個很平常的單例模式,但在各種的實現上真的可以看到java的基本功的體現,這裏包括了;懶漢、餓漢、線程是否安全、靜態類、內部類、加鎖、串行化等等。
  • 在平時的開發中如果可以確保此類是全局可用不需要做懶加載,那麼直接創建並給外部調用即可。但如果是很多的類,有些需要在用戶觸發一定的條件后(遊戲關卡)才显示,那麼一定要用懶加載。線程的安全上可以按需選擇。
  • 建議在學習的過程中一定要加以實踐,否則很難完完整整的掌握一整套的知識體系。例如案例中的出現的Effective Java一書也非常建議大家閱讀。另外推薦下這位大神的Github:https://github.com/jbloch

七、推薦閱讀

  • 重學 Java 設計模式:實戰原型模式-模擬考試試卷亂序題目和答案
  • Java開發架構篇:初識領域驅動設計DDD落地
  • Java開發架構篇:DDD模型領域層決策規則樹服務設計
  • Java開發架構篇:領域驅動設計架構基於SpringCloud搭建微服務
  • 源碼分析(面試常問題目) | Mybatis接口沒有實現類為什麼可以執行增刪改查
  • 講道理,只要你是一個愛折騰的程序員,畢業找工作真的不需要再花錢培訓!

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

※回頭車貨運收費標準

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

一次FGC導致CPU飆高的排查過程_網頁設計公司

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

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

    今天測試團隊反饋說,服務A的響應很慢,我在想,測試環境也會慢?於是我自己用postman請求了一下接口,真的很慢,竟然要2s左右,正常就50ms左右的。

    於是去測試服務器看了一下,發現服務器負載很高,並且該服務A佔了很高的cpu。先用top命令,看了load average,發現都到了1.5左右(雙核cpu)了,並且有一個java進程(20798)佔用cpu一直很高,如下圖:

    於是,用命令jps -l看了一下java的20798,剛好就是服務A。

    究竟服務A在跑什麼,畢竟是測試環境。於是使用top -Hp 20798看一下是哪個線程在跑,如下圖:

    

    發現線程20840佔用cpu非常高,其他幾乎都是0。通過以下命令輸出該線程id(20840)的16進制:

printf "%x\n" 20840

  

輸出如下:

    線程id(20840)的16進制是5186。

    然後使用以下命令打印出該線程的堆棧信息:

jstack -l 20798 | grep -A 20 5168

  

    輸入如下:

    發現佔用cpu的進程是jvm的GC線程,於是猜測是不是由於一直在進行FGC導致cpu飆高,於是使用以下命令看下FGC的頻率和耗時:

jstat -gc 20798 1000

  

輸出如下:

    發現,果然是不斷地在進行着FGC,並且每次FGC的時間一直在升高。是什麼導致一直都在FGC呢?是有大對象一直在創建,回收不了?於是使用以下命令看下heap中的對象情況:

jmap -histo:live 20798 | head -20

  

輸出如下:

    發現一個業務類對象竟然有150w+個,並且佔用了264M的堆大小,什麼情況,並且這150w+個對象還是存活的(注意jmap使用的時候,已經帶上了:live選項,只輸出存活的對象),嚇我一跳。於是趕緊使用以下命令打出線程堆棧來看一下:

jstack -l 20798 > jstack_tmp.txt

  

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

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

輸出如下:

然後使用如下命令在輸出的線程堆棧中根據對象類查找一下:

grep -C 30 'omments' jstack_tmp.txt

  

輸出如下:

    猜測是由於一下次從db load出了太多的CommentsEntity。

    於是使用以下命令dump出heapdump出來重複確認一下:

jmap -dump:live,format=b,file=news_busy_live.hprof 20798

  

    把heapdump文件news_busy_live.hprof下載到windows本地,使用mat工具進行分析,第一次打開發現打不開,畢竟news_busy_live.hprof有3G那麼大,mat直接報OOM打不開,發現mat的配置文件MemoryAnalyzer.ini裏面的配置-Xmx1024m,heap size才1G,太小了,於是改成-Xmx4096m,保存,重新打開mat,再打開news_busy_live.hprof文件即可,如下圖:

    發現mat已經幫我們分析出了內存泄漏的可以對象,233w+個對象(前面通過jmap命令輸出的150W+個,是後面為了寫文章而專門重現的操作,這裏的233w+個是當時真的出問題的時候dump出來的heap dump文件),太恐怖了。

    通過以下操作,查看

點擊exclude all ….,因為弱引用,軟引用,虛引用等都可以被GC回收的,所以exclude,輸出如下:

    發現一共有6個線程引用了那233w+個對象,於是去前面dump出來的線程堆棧跟蹤以下這幾個線程的情況,發現堆棧裏面剛好這幾個線程也是在處理comments相關的邏輯,這個是剛好碰巧,一般線程id都對不上的,畢竟線程處理完之後就釋放了的。所以我們還是看回前麵線程堆棧的信息,這裏貼出根據關鍵字”omment”搜索出來的線程堆棧的信息,如下:

"XNIO-5 task-77" #248 prio=5 os_prio=0 tid=0x00007fc4511be800 nid=0x8f7 runnable [0x00007fc3e5af2000]   java.lang.Thread.State: RUNNABLE       ...        at cn.xxxxxx.news.commons.redis.RedisUtil.setZSet(RedisUtil.java:1080)        at cn.xxxxxx.news.service.impl.CommentsServiceV2Impl.setCommentIntoRedis(CommentsServiceV2Impl.java:1605)        at cn.xxxxxx.news.service.impl.CommentsServiceV2Impl.loadCommentsFromDB(CommentsServiceV2Impl.java:386)        ...        at cn.xxxxxx.xxxs.controller.vxxx.xxxxController.getxxxxxx(NewsContentController.java:404)        at cn.xxxxxx.xxx.controller.vxxx.xxxxxController$$FastClassBySpringCGLIB$$e7968481.invoke(<generated>)        ...        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)        at java.lang.Thread.run(Thread.java:745)​   Locked ownable synchronizers:        - <0x00000000f671ecd0> (a java.util.concurrent.ThreadPoolExecutor$Worker)​

  

    

    從上面的堆棧信息,結合前面的猜測(猜測是一次性從db load出太多的CommentsEntity),猜測應該是函數loadCommentsFromDB一次性從db load出太多CommentsEntity了。於是看了一下業務代碼,發現load出來的commentsEntity會放到redis的某一個zset,於是使用redis destopmanger看一下這個zset的數據,發現這個zset有22w的數據,從中找出幾條,發現對應的newsPk都是同一個,根據newsPk在db中找一下該newsPk的comments總記錄,發現該newsPk的comments記錄數是38w+條,那就是這個問題了,一次性從db中load了38w+的數據到內存。

    一次性load那麼多數據到內存,這肯定是一個慢查詢,不管是db還是網絡io,都肯定很慢。然後發現業務代碼還會有一個for循環,把這個CommentsEntityList遍歷一遍,一條一條放到redis,這也是一個非常慢的過程。

    然後我去看了服務A的access log,發現在短時間內,請求了該newsPk多次數據,所以就導致了jvm的heap空間不夠,然後出現不斷FGC的現象,並且該newsPk的請求,由於超時,都在網關超時返回了。

    為了驗證這個問題,我把相關的redis緩存刪除,然後調用該newsPk的接口獲取數據,發現很慢,並且cpu立刻飈上去了,然後調多幾次,並且不斷地進行FGC,至此已經復現了該問題,和猜測的一樣。等數據load到redis之後,再訪問該接口,就很正常沒問題。

    上面發現問題的代碼,找時間做一下優化才行,先重啟服務A,讓服務可用先。

 

                    歡迎關注微信公眾號“ismallboy”,請掃碼並關注以下公眾號,並在公眾號下面回復“FGC”,獲得本文最新內容。

                                                           

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

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

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

循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理,循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理_包裝設計

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

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

在我們一般開發的系統界面裏面,列表頁面是一個非常重要的綜合展示界面,包括有條件查詢、列表展示和分頁處理,以及對每項列表內容可能進行的轉義處理,本篇隨筆介紹基於Vue +Element基礎上實現表格列表頁面的查詢,列表展示和字段轉義處理。

在前面隨筆《循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理》介紹了一個對產品列表的卡片式圖片分頁展示處理,其中涉及到了對於Element 組件分頁控件的操作,本篇隨筆繼續深化這一組件的使用,結合表格展示來處理效果展示。

1、列表查詢界面效果

在介紹任何代碼處理邏輯之前,我們先來做一個感官的認識,貼上一個效果圖,在逐一介紹其中處理的步驟和注意事項。

 常規的列表展示界面,一般分為幾個區域,一個是查詢區域,一個是列表展示區域,一個是底部的分頁組件區域。查詢區域主要針對常規條件進行布局,以及增加一些全局或者批量的操作,如導入、導出、添加、批量添加、批量刪除等按鈕;而其中主體的列表展示區域,是相對比較複雜一點的地方,需要對各項數據進行比較友好的展示,可以結合Tag,圖標,按鈕等界面元素來展示,其中列表一般後面會包括一些對單行記錄處理的操作,如查看、編輯、刪除的操作,如果是批量刪除,可以放到頂部的按鈕區域。

 

2、查詢區域的處理

查詢區域一般的界面效果如下所示,除了包含一些常用的查詢條件,並增加一些常規的處理按鈕,如查詢、重置、新增、批量刪除、導入、導出等按鈕。

  對於查詢區域來說,它也是一個表單的處理,因此也需要添加一一個對應的對象來承載表單的數據,在data裏面增加一個searchForm的模型對象,以及一個用於分頁查詢的pageinfo對象,如下代碼所示。

export default {
  data() {
    return {
      listLoading: true,
      pageinfo: {
        pageindex: 1,
        pagesize: 10,
        total: 0
      },
      searchForm: {
        ProductNo: '',
        BarCode: '',
        ProductType: '',
        ProductName: '',
        Status: 0
      },

視圖模板代碼如下所示

    <el-form ref="searchForm" :model="searchForm" label-width="80px">
      <el-row>
        <el-col :span="6">
          <el-form-item label="產品編號" prop="ProductNo">
            <el-input v-model="searchForm.ProductNo" />
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="產品名稱" prop="ProductName">
            <el-input v-model="searchForm.ProductName" />
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="商品類型" prop="ProductType">
            <el-select v-model="searchForm.ProductType" filterable clearable placeholder="請選擇">
              <el-option
                v-for="(item, key) in typeList"
                :key="key"
                :label="item.value"
                :value="item.key"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="狀態" prop="Status">
            <el-select v-model="searchForm.Status" filterable clearable placeholder="請選擇">
              <el-option
                v-for="item in Status"
                :key="item.Value"
                :label="item.Text"
                :value="item.Value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <el-row style="float:right;padding-bottom:10px">
      <el-button icon="el-icon-search" type="primary" round @click="search()">查詢</el-button>
      <el-button icon="el-icon-refresh-left" type="warning" round plain @click="resetForm('searchForm')">重置</el-button>
      <el-button icon="el-icon-document-add" type="success" round @click="showAdd()">新增</el-button>
      <el-button icon="el-icon-document-remove" type="danger" round @click="BatchDelete()">批量刪除</el-button>
      <el-button icon="el-icon-upload2" type="danger" plain="" round @click="showImport()">導入</el-button>
    </el-row>

其中產品類型的是下拉列表,我們通過在data區域獲取一個對象,並在此遍歷可以展示字典內容,如果我們花點時間,可以把這些下拉列表統一按照一個常規的處理模式,定義一個字典組件的方式實現,簡單賦予一個字典類型的Prop值,就可以綁定下拉列表了,這個稍後在細講。

在Vue的腳本處理邏輯裏面,我們可以在Created聲明周期裏面,通過API獲取數據,綁定在模型上,界面就會自動進行更新了,處理過程代碼如下所示。

  created() {
    // 獲取產品類型,用於綁定字典等用途
    GetProductType().then(data => {
      if (data) {
        data.forEach(item => {
          this.productTypes.set(item.id, item.name)
          this.typeList.push({ key: item.id, value: item.name })
        })
      }
    });

    // 獲取列表信息
    this.getlist()
  },
  methods: {
    getlist() { // 構造常規的分頁查詢條件
      var param = {
        type: this.producttype === 'all' ? '' : this.producttype,
        pageindex: this.pageinfo.pageindex,
        pagesize: this.pageinfo.pagesize
      };

      // 把SearchForm的條件加入到param裏面,進行提交查詢
      param.type = this.searchForm.ProductType // 轉換為對應屬性
      Object.assign(param, this.searchForm); // 獲取產品列表,綁定到模型上,並修改分頁數量
      this.listLoading = true
      GetProductList(param).then(data => {
        this.productlist = data.list
        this.pageinfo.total = data.total_count
        this.listLoading = false
      })
    },

其中 Object.assign(param, this.searchForm); 語句處理,是把獲得的查詢條件,覆蓋原來對象裏面的屬性,從而實現查詢條件的變量賦值。

獲得列表數據,就是介紹如何展示表格列表數據的過程了,表格界面效果如下所示。

  先定義一個表格頭部,類似HTML裏面的<table>的標籤,指定樣式和一些常規的操作函數,如下代碼所示。

    <el-table
      v-loading="listLoading"
      :data="productlist"
      border
      fit
      stripe
      highlight-current-row
      :header-cell-style="{background:'#eef1f6',color:'#606266'}"
      @selection-change="selectionChange"
      @row-dblclick="rowDbclick"
    >

具體的屬性可以參考下Element組件關於表格控件的屬性了,在表格列裏面,我們主要關注它的data綁定即可。

接着定義一列複選框選擇的列,用於批量處理的勾選,如批量刪除操作。

  <el-table-column type="selection" width="55"/>

接着就是根據返回JSON屬性,逐一進行內容轉換為表格列的展示過程了,如下所示。

  <el-table-column label="商品編號" width="80">
    <template slot-scope="scope">
      {{ scope.row.ProductNo }}
    </template>
  </el-table-column>

我們如果需要在显示裏面增加處理效果,一般在template裏面修改展示效果即可,如下是單位的處理,增加一個tag標誌強調下。

  <el-table-column align="center" label="單位" width="80">
    <template slot-scope="scope">
      <el-tag type="" effect="plain"> {{ scope.row.Unit }}</el-tag>
    </template>
  </el-table-column>

 而對於一些需要判斷處理的效果,我們可以對內容進行判斷輸出,如下狀態所示。

  <el-table-column label="狀態" width="80">
    <template slot-scope="scope">
      <el-tag v-if="scope.row.Status==0" type="" effect="dark">正常</el-tag>
      <el-tag v-else-if="scope.row.Status==1" type="success" effect="dark">推薦</el-tag>
      <el-tag v-else-if="scope.row.Status==2" type="danger" effect="dark">停用</el-tag>
    </template>
  </el-table-column>

 另外,對於一些常見的日期處理,我們可以使用Formatter,Filter等手段進行內容的轉義處理,可以去掉後面的時間部分。

<el-table-column align="center" label="創建日期" width="120" prop="CreateTime" :formatter="dateFormat" />

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

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

  dataFormat就是一個轉義函數,函數代碼如下所示。

    dateFormat(row, column, cellValue) {
      return cellValue ? fecha.format(new Date(cellValue), 'yyyy-MM-dd') : ''
    },

使用的時候,需要在頂部引入一個類庫即可

import * as fecha from 'element-ui/lib/utils/date'

對於類似需要進行字典轉義的操作,我們可以使用Formatter的方式轉義,如增加一個函數來解析對應的值為中文信息

  效果可以使用Formatter來轉義

    productTypeFormat(row, column, cellValue) {
      var display = this.productTypes.get(cellValue)
      return display || ''
    },

也可以使用Filter模式來進行處理。

這裏介紹使用Filter的操作處理,首先在界面HTML代碼裏面增加對應的操作,如下代碼所示。

  <el-table-column align="center" label="商品類型" width="120" prop="ProductType">
    <template slot-scope="scope">
      <el-tag type="danger">  {{ scope.row.ProductType | productTypeFilter }}</el-tag>
    </template>
  </el-table-column>

Filter其實就是一個 | 過濾符號,以及接着一個過濾函數處理即可。

export default {
  filters: {
    productTypeFilter: function(value) {
      if (!value) return ''

      var display = that.productTypes.get(value)
      return display || ''
    }
  },

值得注意的是,Filter本身不能引用data裏面的屬性列表進行轉義的需要,如果需要,那麼需要在beforeCreate的鈎子函數裏面記錄this的引用,如下代碼所示。

  對於操作按鈕,我們需要增加一行來显示幾個按鈕即可,如果需要權限控制,可以再根據權限集合判斷一下可操作權限即可。

      <el-table-column label="操作" width="140">
        <template scope="scope">
          <el-row>
            <el-tooltip effect="light" content="查看" placement="top-start">
              <el-button icon="el-icon-search" type="success" circle size="mini" @click="showView(scope.row.ID)" />
            </el-tooltip>
            <el-tooltip effect="light" content="編輯" placement="top-start">
              <el-button icon="el-icon-edit" type="primary" circle size="mini" @click="showEdit(scope.row.ID)" />
            </el-tooltip>
            <el-tooltip effect="light" content="刪除" placement="top-start">
              <el-button icon="el-icon-delete" type="danger" circle size="mini" @click="showDelete(scope.row.ID)" />
            </el-tooltip>
          </el-row>
        </template>
      </el-table-column>

這裏showView/showEdit/ShowDelete主要就是做一些彈出層前的處理操作,我們在data項裏面定義了幾個變量,用來確定是那個操作显示的需要。

      isAdd: false,
      isEdit: false,
      isView: false,
      isImport: false,

例如對應編輯操作,我們是需要通過API處理類,獲取後端數據,並賦值給編輯框的表單對象上,進行展示即可。

    showEdit(id) {
      // 通過ID參數,使用API類獲取數據后,賦值給對象展示
      var param = { id: id }
      GetProductDetail(param).then(data => {
        Object.assign(this.editForm, data);
      })
      this.isEdit = true
    },

對於查看處理,我們除了在每行按鈕上可以單擊進行查看指定行記錄外,我們雙擊指定的行,也應該彈出對應的查看記錄界面

    rowDbclick(row, column) {
      var id = row.ID
      this.showView(id);
    },

這個就是表格定義裏面的一些處理事件

    <el-table
      v-loading="listLoading"
      :data="productlist"
      border
      fit
      stripe
      highlight-current-row
      :header-cell-style="{background:'#eef1f6',color:'#606266'}"
      @selection-change="selectionChange" @row-dblclick="rowDbclick"
    >

每個對話框的:visible的屬性值,則是確定哪個模態對話框的显示和隱藏。

 而對於刪除操作,我們只需要確認一下,然後提交遠端處理,返回正常結果,就提示用戶刪除成功即可。如下邏輯代碼所示。

    showDelete(id) {
      this.$confirm('您確認刪除選定的記錄嗎?', '操作提示',
        {
          type: 'warning' // success,error,info,warning
          // confirmButtonText: '確定',
          // cancelButtonText: '取消'
        }
      ).then(() => {
        // 刪除操作處理代碼

        this.$message({
          type: 'success',
          message: '刪除成功!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消刪除'
        });
      });
    },

以上就是常規表格列表頁面查詢、列表展示、字段轉義的一些常規操作,以及對新增、編輯、查看、刪除操作的一些常規處理,通過對這些模型的操作,減少了我們以往重新獲取對應DOM的繁瑣操作,是的數據的操作處理,變得方便了很多。

列出以下前面幾篇隨筆的連接,供參考:

循序漸進VUE+Element 前端應用開發(1)— 開發環境的準備工作

循序漸進VUE+Element 前端應用開發(2)— Vuex中的API、Store和View的使用

循序漸進VUE+Element 前端應用開發(3)— 動態菜單和路由的關聯處理

循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理

 

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

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

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

實用教程丨使用K3s和MySQL運行Rancher 2.4_台中搬家

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

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

本文轉自Rancher Labs

簡 介

本文將介紹在高可用K3s Kubernetes集群上安裝Rancher 2.4的過程並針對MySQL利用Microsoft Azure數據庫的優勢,該數據庫消除了對etcd的依賴,併為我們提供了Azure在這一服務中的所有其他功能。

在本文中,你將了解到只使用Azure Cloud Shell如何部署基礎架構以支持此方式。使用Cloud Shell的好處是零基礎架構即可上手——僅需訪問Azure門戶即可。並且許多所需的CLI功能已經預先安裝好,從而大大減少了完成安裝所需的工作量。

你部署完成基礎架構后,你將了解如何使用K3s在一個Kubernetes集群上部署Rancher 2.4。在Rancher 2.4中,我們已經添加了新的部署支持模式:在兩個節點上的Rancher 2.4運行帶有外部數據庫的K3s。使用這一模式的好處之一是我們可以將節點視為短暫的。由於K3s支持外部MySQL數據庫,因此我們可以做到這一點。

K3s是一個輕量的Kubernetes發行版,它比Rancher Kubernetes Engine(RKE)更先進,並且具有以下增強功能:

  1. 嵌入式SQLite數據庫替換了etcd,成為默認的數據存儲,它還支持外部數據存儲,例如PostgreSQL、MySQL和etcd。(本文中我們將使用MySQL)

  2. 我們添加了簡單但功能強大的“開箱即用”的功能,例如本地存儲程序、服務負載均衡器、Helm controller以及Traefik controller。

  3. 所有Kubernetes控制平面組件的操作都封裝在單個二進制文件和進程中。這使K3s可以自動化和管理複雜的集群操作,例如分發證書。

  4. 我們移除了in-tree雲提供程序和存儲插件

  5. 我們已將外部依賴性降到最低(僅需要現代內核和cgroup掛載)。K3s軟件包需要依賴項,包括:Containerd、Flannel、CoreDNS和主機實用程序(iptables、socat等)

如果你是第一次嘗試使用Rancher,可以考慮這種部署模式。這很有可能在之後成為部署Rancher的首選方法,提前了解總是好的——尤其是在Azure運行數據中心時。

前期準備

為了完成以下內容,你需要提前準備:

  • Microsoft賬號:Microsoft的登錄憑證。可以是你的Azure Active Directory憑據,也可以是普通的Outlook賬戶。

  • 訪問一個Azure訂閱:可以是免費試用/隨用隨付/也可以是企業訂閱(https://azure.microsoft.com/en-us/free/ )

  • 訪問Azure門戶(https://portal.azure.com/#home )

架 構

以下圖片展示了將要在Azure中創建的資源:

這兩個節點將放在單個子網(subnet)中的自己的vNet上。這些將在Azure負載平衡器的前面。MySQL數據庫將從外部的vNet提供,vNet由Microsoft託管。然後通過連接到子網的單個網絡安全組(NSG)保護節點。

Azure Cloud Shell

我們將只使用Azure Cloud Shell來配置在Azure中的K3s上運行Rancher所需的所有元素。在門戶中,單擊右上角的“Azure Cloud Shell”按鈕。該圖標中有大於符號和下劃線。

Azure網絡

資源組

在Azure中,所有資源需要歸屬於某個資源組,所以我們得先創建資源組。我們將設置默認區域和資源組,以確保我們所有的資源都會被創建到正確的位置。

請注意:我使用eastus2作為我的區域,但你可以根據自身需要進行更改。

az group create -l eastus2  -n RancherK3sResourceGroup
az configure --defaults location=eastus2 group=RancherK3sResourceGroup

Vnet、公共IP和網絡安全組(NSG)

這些命令完成后,將在資源組內部創建網絡組件。其中包括帶有默認子網的vNet,我們稍後將創建的兩個虛擬機(VM)的兩個公共IP,以及一個網絡安全組(NSG)。

az network vnet create --resource-group RancherK3sResourceGroup --name RancherK3sVnet --subnet-name RancherK3sSubnet

az network public-ip create --resource-group RancherK3sResourceGroup --name RancherK3sPublicIP1 --sku standard

az network public-ip create --resource-group RancherK3sResourceGroup --name RancherK3sPublicIP2 --sku standard

az network nsg create --resource-group RancherK3sResourceGroup --name RancherK3sNSG1

az network nsg rule create -g RancherK3sResourceGroup --nsg-name RancherK3sNSG1 -n NsgRuleSSH --priority 100 \
--source-address-prefixes '*' --source-port-ranges '*' \
--destination-address-prefixes '*' --destination-port-ranges 22 --access Allow \
--protocol Tcp --description "Allow SSH Access to all VMS."

Azure負載均衡器

我們在兩個VM上安裝K3s之後,我們需要一個負載均衡器來提供彈性並防止VM故障。

首先,為負載均衡器創建一個公共IP

az network public-ip create --resource-group RancherK3sResourceGroup --name RancherLBPublicIP --sku standard

接下來,使用健康的探針(probe)創建負載均衡器

az network lb create \
--resource-group RancherK3sResourceGroup \
--name K3sLoadBalancer \
--sku standard \
--public-ip-address RancherLBPublicIP \
--frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool

az network lb probe create \
--resource-group RancherK3sResourceGroup \
--lb-name K3sLoadBalancer \
--name myHealthProbe \
--protocol tcp \
--port 80

負載均衡器創建完成后,更新NSG。添加80和443端口,用於訪問Rancher Server,再添加一個6443端口,用於訪問K3s的Kubernetes API。

az network nsg rule create \
--resource-group RancherK3sResourceGroup \
--nsg-name RancherK3sNSG1 \
--name myNetworkSecurityGroupRuleHTTP \
--protocol tcp \
--direction inbound \
--source-address-prefix '*' \
--source-port-range '*' \
--destination-address-prefix '*' \
--destination-port-range 80 443 6443 \
--access allow \
--priority 200

現在以三個規則的形式添加負載均衡器配置。你需要一個用於端口80的規則和一個用於端口443的規則,以分散兩個VM上Rancher Server的負載。第三條規則用於端口6443,該端口可訪問在每個VM上運行的Kubernetes API。

az network lb rule create \
--resource-group RancherK3sResourceGroup \
--lb-name K3sLoadBalancer \
--name myHTTPRule \
--protocol tcp \
--frontend-port 80 \
--backend-port 80 \
--frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool \
--probe-name myHealthProbe

az network lb rule create \
--resource-group RancherK3sResourceGroup \
--lb-name K3sLoadBalancer \
--name myHTTPSRule \
--protocol tcp \
--frontend-port 443 \
--backend-port 443 \
--frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool \
--probe-name myHealthProbe

az network lb rule create \
--resource-group RancherK3sResourceGroup \
--lb-name K3sLoadBalancer \
--name myHTTPS6443Rule \
--protocol tcp \
--frontend-port 6443 \
--backend-port 6443 \
--frontend-ip-name myFrontEnd \
--backend-pool-name myBackEndPool \
--probe-name myHealthProbe

Azure數據庫即服務(DaaS)

使用K3s作為Kubernetes發行版的好處之一是它支持etcd的替代版本,在本例中,我們將使用Azure數據庫中的MySQL作為數據庫。

要創建MySQL數據庫,請運行以下CLI命令。

首先讓我們為數據庫服務器的名稱創建一個變量,這樣可以讓運行後續命令更加容易。注意數據庫服務器的名稱在整個Azure必須是唯一的,否則你將會在創建時出錯。

K3smysqlserver=<unique-myslq-server-name>

創建你的MySQL 服務器。如果名稱不是唯一的,將显示錯誤。如果是,那麼使用新名稱更新變量,然後再次運行此命令。

az mysql server create --resource-group RancherK3sResourceGroup --name $K3smysqlserver --admin-user myadmin --admin-password Password1 --sku-name GP_Gen5_2 --version 5.7

創建防火牆規則以允許所有的Azure IP可以訪問你的數據庫服務器。

az mysql server firewall-rule create --resource-group RancherK3sResourceGroup --server $K3smysqlserver --name "AllowAllWindowsAzureIps" --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0

為現有的子網添加service endpoint。

az network vnet subnet update --vnet-name RancherK3sVnet --name RancherK3sSubnet --service-endpoints "Microsoft.Sql"

將vnet規則添加到數據庫訪問。

az mysql server vnet-rule create --server $K3smysqlserver --name MyK3sVNetRule \
-g RancherK3sResourceGroup --subnet RancherK3sSubnet --vnet-name RancherK3sVnet

為數據庫通信禁用TLS

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

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

az mysql server update --resource-group RancherK3sResourceGroup --name $K3smysqlserver --ssl-enforcement Disabled

在Azure Cloud Shell中已經安裝好MySQL CLI工具了。下一步是連接到MySQL服務器並創建一個數據庫。

連接到新的MySQL服務器。

mysql --host $K3smysqlserver.mysql.database.azure.com --user myadmin@$K3smysqlserver -p

檢查狀態,確保MySQL正在運行。

status

創建一個空的數據庫。

CREATE DATABASE kubernetes;

SHOW DATABASES;

exit

Azure虛擬機

接下來,我們將創建2個虛擬機並在它們上面安裝K3s。

網絡接口

創建所有網絡元素后,我們可以為VM創建網絡接口卡(NIC)。

az network nic create --resource-group RancherK3sResourceGroup --name nic1 --vnet-name RancherK3sVnet --subnet RancherK3sSubnet --network-security-group RancherK3sNSG1 --public-ip-address RancherK3sPublicIP1 --lb-name K3sLoadBalancer --lb-address-pools myBackEndPool

az network nic create --resource-group RancherK3sResourceGroup --name nic2 --vnet-name RancherK3sVnet --subnet RancherK3sSubnet --network-security-group RancherK3sNSG1 --public-ip-address RancherK3sPublicIP2 --lb-name K3sLoadBalancer --lb-address-pools myBackEndPool

創建虛擬機

要創建2個虛擬機,首先需要使用我們的cloud-init配置創建一個文本文件。這將部署Docker、添加ubuntu用戶到docker組並安裝K3s。

cat << EOF > cloud-init.txt
#cloud-config
package_upgrade: true
packages:
  - curl
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - curl https://releases.rancher.com/install-docker/18.09.sh | sh
  - sudo usermod -aG docker ubuntu
  - curl -sfL https://get.k3s.io | sh -s - server --datastore-endpoint="mysql://myadmin@$K3smysqlserver:Password1@tcp($K3smysqlserver.mysql.database.azure.com:3306)/kubernetes"
EOF

部署虛擬機。

az vm create \
--resource-group RancherK3sResourceGroup \
--name K3sNode1 \
--image UbuntuLTS \
--nics nic1 \
--admin-username ubuntu \
--generate-ssh-keys \
--custom-data cloud-init.txt


az vm create \
--resource-group RancherK3sResourceGroup \
--name K3sNode2 \
--image UbuntuLTS \
--nics nic2 \
--admin-username ubuntu \
--generate-ssh-keys \
--custom-data cloud-init.txt

檢查Kubernetes是否正在運行

作為VM配置的一部分,K3s應該已經安裝完畢。讓我們連接到第一個VM並確認K3s是否正在運行。

ssh ubuntu@<publicIPofNode1>

兩個VM應該都在節點列表上。如果第一次沒有成功,那麼需要給它幾分鐘的時間來運行cloud-init腳本。它可能需要花費一些時間來部署Docker和K3s。

sudo k3s kubectl get nodes

輸出為:

ubuntu@ip-172-31-60-194:~$ sudo k3s kubectl get nodes
NAME               STATUS   ROLES    AGE    VERSION
ip-172-31-60-194   Ready    master   44m    v1.17.2+k3s1
ip-172-31-63-88    Ready    master   6m8s   v1.17.2+k3s1

測試集群Pod的健康狀態:

sudo k3s kubectl get pods --all-namespaces

保存並開始使用kubeconfig文件

在連接到我們其中之一的節點的同時,我們需要獲取集群的kubeconfig內容。使用以下命令將內容輸出到屏幕,然後將其複製到剪貼板。

sudo cat /etc/rancher/k3s/k3s.yaml

將其粘貼到文本編輯器中,以便我們可以進行更改,然後再將其添加到我們正在處理的Azure Cloud Shell會話中。

更新server:使用負載均衡器的外部URL。你可以使用xip.io服務為你提供可解析的完全限定域名。請參見下面的屏幕截圖。

例如:

https://rancher. .xip.io:6443>

注意:需要將截屏中的示例替換為你的負載均衡器的公共IP。

現在,在/.kube文件夾中創建一個名為config的文件,並將更新的內容粘貼到該文件中。

首先,從node1開始解除連接。

exit

現在創建新的目錄並編輯文件,粘貼到已經更新的內容中。

mkdir ~/.kube
vi ~/.kube/config

檢查kubectl是否正在工作並能否與集群交互。現在Kubectl和Helm已經在Azure Cloud Shell中安裝完畢。

kubectl get pods --all-namespaces

安裝Rancher

添加Rancher Helm Repo

helm repo add rancher-latest https://releases.rancher.com/server-charts/latest

創建cattle-system命名空間

kubectl create namespace cattle-system

分別安裝CustomResourceDefinition資源

kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.12/deploy/manifests/00-crds.yaml

cert-manager創建命名空間

kubectl create namespace cert-manager

添加Jetstack Helm代碼庫

helm repo add jetstack https://charts.jetstack.io

更新你的本地Helm chart代碼庫緩存

helm repo update

安裝cert-manager Helm chart

helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v0.12.0

檢查Cert-Manager是否正在運行,確保所有的pod都正在運行。

kubectl get pods --namespace cert-manager

使用自簽名證書安裝Rancher。確保你使用Rancher Server的URL設置了主機名。在本文中,我們利用xip.io服務。在Rancher URL中使用Azure 負載均衡器的公共IP地址。

helm install rancher rancher-latest/rancher \
--namespace cattle-system \
--set hostname=rancher.<LoadBalancerPublicIP>.xip.io

等待Rancher部署……

kubectl -n cattle-system rollout status deploy/rancher

三個副本全部roll out之後,請點擊Rancher server deployment的URL,如下所示:

清 理

在Azure中創建資源會產生費用,因此請確保在完成操作后刪除資源組。

az group delete --name RancherK3sResourceGroup

結 論

在本文中,我們提供了一種快速簡便的方法使用Rancher對Azure中的容器化工作負載進行多集群管理。通過使用K3s,我們不僅能夠非常快速地啟動並運行,而且移除了etcd同時避免了在生產環境中運行它會產生的一些麻煩。通過使用Azure Cloud Shell,身份驗證變得十分容易,並且可以“開箱即用”地使用我們所需的所有工具。

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

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

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

Netty源碼學習系列之2-NioEventLoopGroup的初始化_台中搬家公司

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

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

前言

    NioEventLoopGroup是netty對Reactor線程組這個抽象概念的具體實現,其內部維護了一個EventExecutor數組,而NioEventLoop就是EventExecutor的實現(看名字也可發現,一個是NioEventLoopGroup,一個是NioEventLoop,前者是集合,後者是集合中的元素)。一個NioEventLoop中運行着唯一的一個線程即Reactor線程,這個線程一直執行NioEventLoop的run方法。這個run方法就是netty的核心方法,其重要性可以類比於Spring中的refresh方法。

    下面是從百度上隨便找的一篇netty文章的線程模型圖(詳見文章https://www.cnblogs.com/luoxn28/p/11875340.html),此處引用是為方便在頭腦中產生一個整體印象,結合圖下面的代碼進行各個概念的歸位。圖中綠色的Reactor Thread就是上文說的NioEventLoopGroup,對應下面代碼中的boss變量,負責處理客戶端的連接事件,它其實也是一個池(因為內部維護的是一個數組);藍色的Reactor Thread Pool也是NioEventLoopGroup,對應下面代碼中的worker變量,負責處理客戶端的讀寫事件

 

 

     注:上圖是Reactor多線程模型,而下面的代碼示例是主從多線程模型,區別是只要將代碼boss中的參數2改成1,示例代碼就成了多線程模型,細細品味一下。

 1 public class NettyDemo1 {
 2     // netty服務端的一般性寫法
 3     public static void main(String[] args) {
 4         EventLoopGroup boss = new NioEventLoopGroup(2);
 5         EventLoopGroup worker = new NioEventLoopGroup();
 6         try {
 7             ServerBootstrap bootstrap = new ServerBootstrap();
 8             bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
 9                     .option(ChannelOption.SO_BACKLOG, 100)
10                     .childHandler(new ChannelInitializer<SocketChannel>() {
11                         @Override
12                         protected void initChannel(SocketChannel socketChannel) throws Exception {
13                             ChannelPipeline pipeline = socketChannel.pipeline();
14                             pipeline.addLast(new StringDecoder());
15                             pipeline.addLast(new StringEncoder());
16                             pipeline.addLast(new NettyServerHandler());
17                         }
18                     });
19             ChannelFuture channelFuture = bootstrap.bind(90);
20             channelFuture.channel().closeFuture().sync();
21         } catch (Exception e) {
22             e.printStackTrace();
23         } finally {
24             boss.shutdownGracefully();
25             worker.shutdownGracefully();
26         }
27     }
28 }

    以上部分是博主對netty的一個概括性總結,以將概念和其實現連接起來,方便建立一個初始的總體認識,下面進入EventLoopGroup的初始化。

一、EventLoopGroup初始化

1、NioEventLoopGroup構造器

    順着有參和無參的構造方法進去,發現無參的構造器將線程數賦值0繼續調了有參的構造器,而有參的構造器將線程池executor參數賦值null繼續調重載構造器

1 public NioEventLoopGroup() {
2         this(0);
3     }
1 public NioEventLoopGroup(int nThreads) {
2         this(nThreads, (Executor) null);
3     }
1 public NioEventLoopGroup(int nThreads, Executor executor) {
2         this(nThreads, executor, SelectorProvider.provider());
3     }

    因為博主是在筆記本電腦調試的,故此時的selectorProvider是WindowsSelectorProvider,然後又加了一個參數DefaultSelectStrategyFactory單例對象:

1 public NioEventLoopGroup(
2             int nThreads, Executor executor, final SelectorProvider selectorProvider) {
3         this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
4     }

    然後調父類的構造器,在末尾增加一個參數RejectedExecutionHandler單例對象:

1 public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
2                              final SelectStrategyFactory selectStrategyFactory) {
3         super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
4     }

 

2、MultithreadEventLoopGroup構造器

    在該構造器中,對線程數參數進行了處理,如果是0(對應上面NioEventLoopGroup的無參構造器),則將線程數設置為默認值,默認值取的是CPU核數*2,8核處理器對應16個線程;如果不是0,則以指定的線程數為準。同時,將executor後面的參數變為數組的形式,對應上面可以知道args中有三個元素:WindowsSelectorProvider、DefaultSelectStrategyFactory、RejectedExecutionHandler。

1 protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
2         super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
3     }

 

3、MultithreadEventExecutorGroup構造器

    此構造器又在args數組前面加了一個單例對象DefaultEventExecutorChooserFactory,用於從NioEventLoopGroup的數組中選取一個NioEventLoop。

1 protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
2         this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
3     }

    下面才是最終的核心構造器方法,結合註釋應該比較好理解。其中最重要的是第3步和第4步,下面着重講解這兩步。

 1 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
 2                                             EventExecutorChooserFactory chooserFactory, Object... args) {
 3         // 1.對線程數進行校驗
 4         if (nThreads <= 0) {
 5             throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
 6         }
 7         // 2.給線程池參數賦值,從前面追蹤可知,若未賦值,executor一直是null,後續用於創建NioEventLoop中的啟動線程,所以這玩意就是一個線程工廠
 8         if (executor == null) {
 9             executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
10         }
11         // 3.給children循環賦值,newChild方法是重點,後續會講解 ***
12         children = new EventExecutor[nThreads];
13         for (int i = 0; i < nThreads; i ++) {
14             boolean success = false;
15             try {
16                 children[i] = newChild(executor, args);
17                 success = true;
18             } catch (Exception e) {
19                 // TODO: Think about if this is a good exception type
20                 throw new IllegalStateException("failed to create a child event loop", e);
21             } finally {
22                 // 省略掉未創建成功后的資源釋放處理
23             }
24         }
25         // 4.完成chooser選擇器的賦值,此處是netty一個小的優化點,後續會講解 **
26         chooser = chooserFactory.newChooser(children);
27         // 5.給數組中每一個成員設置監聽器處理
28         final FutureListener<Object> terminationListener = new FutureListener<Object>() {
29             @Override
30             public void operationComplete(Future<Object> future) throws Exception {
31                 if (terminatedChildren.incrementAndGet() == children.length) {
32                     terminationFuture.setSuccess(null);
33                 }
34             }
35         };
36 
37         for (EventExecutor e: children) {
38             e.terminationFuture().addListener(terminationListener);
39         }
40         // 6.設置一個只讀的set集合
41         Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
42         Collections.addAll(childrenSet, children);
43         readonlyChildren = Collections.unmodifiableSet(childrenSet);
44     }

 

3.1)、第4步chooser的賦值

    由上面構造器調用過程可知,chooserFactory對應DefaultEventExecutorChooserFactory對象,該對象的newChooser方法如下:

1 public EventExecutorChooser newChooser(EventExecutor[] executors) {
2         if (isPowerOfTwo(executors.length)) {
3             return new PowerOfTwoEventExecutorChooser(executors);
4         } else {
5             return new GenericEventExecutorChooser(executors);
6         }
7     }

    邏輯比較簡單,判斷數組的長度是不是2的N次冪,如果是,返回PowerOfTwoEventExecutorChooser對象,如果不是則返回GenericEventExecutorChooser對象。這二者有什麼區別,netty設計者為什麼要這麼做呢?如果對HashMap的實現原理有深入了解的園友應該不難想到,如果一個數X是2的N次冪,那麼用任意一個數Y對X取模可以用Y&(X-1)來高效的完成,這樣做比直接%取模快了好幾倍,這也是HashMap用2次冪作為數組長度的主要原因。這裡是同樣的道理,如下代碼所示,這兩個chooser類都很簡單,內部維護了一個原子遞增對象,每次調用next方法都加1,然後用這個數與數組長度取模,得到要對應下標位置的元素。而如果數組長度剛好是2次冪,用PowerOfTwoEventExecutorChooser就會提高效率,如果不是那也沒辦法,走%取模就是了。netty這種對效率提升的處理,是否在平時的CRUD中也能套用一下呢?

 1 private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
 2         private final AtomicInteger idx = new AtomicInteger();
 3         private final EventExecutor[] executors;
 4 
 5         PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
 6             this.executors = executors;
 7         }
 8 
 9         @Override
10         public EventExecutor next() {
11             return executors[idx.getAndIncrement() & executors.length - 1];
12         }
13     }
14 
15     private static final class GenericEventExecutorChooser implements EventExecutorChooser {
16         private final AtomicInteger idx = new AtomicInteger();
17         private final EventExecutor[] executors;
18 
19         GenericEventExecutorChooser(EventExecutor[] executors) {
20             this.executors = executors;
21         }
22 
23         @Override
24         public EventExecutor next() {
25             return executors[Math.abs(idx.getAndIncrement() % executors.length)];
26         }
27     }

 

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

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

3.2)、第3步newChild方法的邏輯

    該方法的實現在NioEventLoopGroup中,由於args長度為3,所以queueFactory為null(暫時未發現哪裡的實現args參數長度會是4,或許只是為後續擴展用,如果園友對args長度為4的場景有了解的還請留言指教)。然後調用了NioEventLoop的構造器,下面進入NioEventLoop的初始化。

1 protected EventLoop newChild(Executor executor, Object... args) throws Exception {
2         EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
3         return new NioEventLoop(this, executor, (SelectorProvider) args[0],
4             ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
5     }

    執行完上述初始化方法后NioEventLoopGroup的快照圖如下,最重要的就兩個屬性:child和chooser。

 

 

 

二、NioEventLoop的初始化

1、NioEventLoop的構造器

    到這裏,有必要將此構造器的入參再梳理一遍。parent即上面的NioEventLoopGroup對象,executor是在MultithreadEventExecutorGroup中初始化的ThreadPerTaskExecutor,selectorProvider是WindowsSelectorProvider,strategy是DefaultSelectStrategyFactory,rejectedExecutionHandler是RejectedExecutionHandler,queueFactory是null。

 1 NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
 2                  SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
 3                  EventLoopTaskQueueFactory queueFactory) {
 4         super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
 5                 rejectedExecutionHandler);
 6         if (selectorProvider == null) {
 7             throw new NullPointerException("selectorProvider");
 8         }
 9         if (strategy == null) {
10             throw new NullPointerException("selectStrategy");
11         }
12         provider = selectorProvider;
13         final SelectorTuple selectorTuple = openSelector();
14         selector = selectorTuple.selector;// netty封裝的selector
15         unwrappedSelector = selectorTuple.unwrappedSelector;// java NIO原生的selector
16         selectStrategy = strategy;
17     }

    可以看到只是做了一些賦值,其中newTaskQueue方法創建的是MpscUnboundedArrayQueue隊列(多生產單消費無界隊列,mpsc是multi provider single consumer的首字母縮寫,即多個生產一個消費),繼續追查父類構造方法。

 

2、SingleThreadEventLoop構造器

    調用父類構造器,給tailTasks賦值。

1 protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
2                                     boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
3                                     RejectedExecutionHandler rejectedExecutionHandler) {
4         super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
5         tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
6     }

 

3、SingleThreadEventExecutor構造器

    在該構造方法中完成了剩餘變量的賦值,其中有兩個變量很重要:executor和taskQueue。前者負責創建Reactor線程,後者是實現串行無鎖化的任務隊列。

 1 protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
 2                                         boolean addTaskWakesUp, Queue<Runnable> taskQueue,
 3                                         RejectedExecutionHandler rejectedHandler) {
 4         super(parent);
 5         this.addTaskWakesUp = addTaskWakesUp;
 6         this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
 7         this.executor = ThreadExecutorMap.apply(executor, this);
 8         this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
 9         rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
10     }

    NioEventLoopGroup的對象引用最終記錄在了AbstractEventExecutor中:

1 protected AbstractEventExecutor(EventExecutorGroup parent) {
2         this.parent = parent;
3     }

    NioeventLoop初始化完成之後的對象快照如下,左邊是子類,右邊是父類:

 

 

小結

    本文詳細講述了netty中Reactor線程組概念模型的實現類 — NioEventLoopGroup的實例化過程。NioEventLoopGroup和其內部數組元素NioEventLoop是netty通信框架的基石,相信本文的內容對初學netty的園友有一點幫助。

    下篇將研究ServerBootstrap的初始化過程,敬請期待。

 

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

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

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

環境減災二號01組A、B星成功發射!環境衛星家族又添新成員_租車

※超省錢租車方案

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

2020-09-30

2020-09-30
分享到:
[打印]
字號:[大] [中] [小]   9月27日11時23分,我國在太原衛星發射中心用長征四號乙運載火箭,以一箭雙星方式成功發射環境減災二號01組A、B星。
  環境減災二號01組A、B星是國家民用空間基礎設施中長期發展規劃(2015-2025年)支持立項,由生態環境部與應急管理部共同牽頭研製建設的光學對地遙感衛星,用於接替已在軌運行12年的環境減災一號A、B衛星,可滿足國家生態環境部、應急管理部應用需求,併兼顧國土資源、水利、農業、林業、地震等行業需求。 環境減災二號01組A、B星搭載長征四號乙火箭點火起飛   環境減災二號01組A、B星採用了太陽同步回歸軌道,兩顆衛星技術狀態相同,設計壽命為5年,每顆衛星配置了4類光學載荷,包括16m相機、高光譜成像儀、紅外相機、大氣校正儀。其中,16m相機載荷由四台可見光CCD相機組成,通過視場拼接可提供幅寬為800km的多光譜圖像;高光譜成像儀幅寬為96km;紅外相機幅寬為720km;大氣校正儀可在軌同步獲取與16m相機同視場的大氣多譜段信息,進行大氣輻射校正。 環境減災二號01組A、B星載荷配置示意圖 環境減災二號01組A、B星拍攝示意圖   環境減災二號01組A、B星發射成功后可實現同軌組網觀測,

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

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

大幅提高我國中分辨率可見光、紅外及高光譜遙感數據獲取能力,滿足區域生態、環境空氣、地表水環境、城市環境、環境災害、全球環境變化等大範圍、全天候、全天時、動態的監測與評估的遙感數據需要,為實現科學治污、精準治污、依法治污提供重要技術支撐,使我國生態環境監測治理能力更加科學化、現代化。 雙星同軌組網觀測示意圖   環境減災二號01組A、B星成功發射,對提升生態環境遙感監測業務化能力極為重要。在大氣環境遙感監測方面,可針對秸稈焚燒和沙塵污染進行遙感動態監測,實現大氣環境污染的遙感精細化監測應用,為大氣污染防治以及空氣環境質量預警預報提供技術支持;在水環境遙感監測方面,可對恭弘=叶 恭弘綠素a、懸浮物等水質參數,溢油、赤潮等近海環境事件,核電廠、火電廠溫排水等水體熱污染開展動態監測;在生態環境監測方面,可為生態系統監測與評估、生態環境關鍵參數生產、自然保護區綜合監管、礦產資源開發監測評估和城市群生態環境監測等生態管理核心業務提供重要數據支撐,實現生態環境信息提取、分析和綜合應用的業務化運行。 衛星研製團隊部分成員與衛星合影   環境減災二號衛星在為生態環境保護等多個領域提供服務的同時,也將不斷在衛星觀測精度、空間分辨率和時間分辨率等方面繼續提升能力。待“十四五”期間環境減災“4+4”衛星星座建成后,將形成全天時、全天候、動態實時的中分辨率全球生態環境監測能力,為實現“高精度、全方位、短周期”的生態環境遙感監測目標提供更為強力的支撐。

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

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