現代轉攻新能源汽車 計畫2020年之前推26款新車

日前,據海外媒體報導,現代汽車集團將在2020年之前推出26款新能源汽車,其中包括旗下現代和起亞兩大品牌。現代汽車集團此舉意在搶佔新能源汽車市場的領先地位,爭取在2020年達到僅次於豐田的行業第二水準。

現代汽車集團計畫推出的26款新車型包括12款混動車型、6款插電式混動車型、2款純電動車型以及2款氫燃料電池車型等。其中,起亞將推出包括Niro混動版、凱尊(參配、圖片、詢價) 混動版、SoulEV在內的11款新車,而現代則會推出插電式混動版、純電動版IONIQ與豐田普銳斯一決高下。據現代汽車集團預估,到2020年現代汽車集團在新能源汽車市場的年銷量有望達到30萬台。

為了節省新能源汽車的開發成本,現代汽車集團相關負責人透露,旗下新能源車型將採用共用零部件策略。雖然每款車的動力系統在動力輸出上可能有所不同,但電動機直徑、核心部件等將會遵循統一化標準。

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

【從今天開始好好學數據結構04】程序員你心中就沒點“樹”嗎?

目錄

前面我們講的都是線性表結構,棧、隊列等等。今天我們講一種非線性表結構,樹。樹這種數據結構比線性表的數據結構要複雜得多,內容也比較多,首先我們先從樹(Tree)開始講起。
@

樹(Tree)

樹型結構是一種非線性結構,它的數據元素之間呈現分支、分層的特點。

1.樹的定義

樹(Tree)是由n(n≥0)個結點構成的有限集合T,當n=0時T稱為空樹;否則,在任一非空樹T中:
(1)有且僅有一個特定的結點,它沒有前驅結點,稱其為根(Root)結點;
(2)剩下的結點可分為m(m≥0)個互不相交的子集T1,T2,…,Tm,其中每個子集本身又是一棵樹,並稱其為根的子樹(Subtree)。

注意:樹的定義具有遞歸性,即“樹中還有樹”。樹的遞歸定義揭示出了樹的固有特性

2.什麼是樹結構

什麼是“樹”?再好的定義,都沒有圖解來的直觀。所以我在圖中畫了幾棵“樹”。你來看看,這些“樹”都有什麼特徵?

你有沒有發現,“樹”這種數據結構真的很像我們現實生活中的“樹”

3.為什麼使用樹結構

在有序數組中,可以快速找到特定的值,但是想在有序數組中插入一個新的數據項,就必須首先找出新數據項插入的位置,然後將比新數據項大的數據項向後移動一位,來給新的數據項騰出空間,刪除同理,這樣移動很費時。顯而易見,如果要做很多的插入和刪除操作和刪除操作,就不該選用有序數組。另一方面,鏈表中可以快速添加和刪除某個數據項,但是在鏈表中查找數據項可不容易,必須從頭開始訪問鏈表的每一個數據項,直到找到該數據項為止,這個過程很慢。 樹這種數據結構,既能像鏈表那樣快速的插入和刪除,又能想有序數組那樣快速查找

4.樹的常用術語

結點——包含一個數據元素和若干指向其子樹的分支
度——結點擁有的子樹個數
樹的度——該樹中結點的最大度數
恭弘=叶 恭弘子——度為零的結點
分支結點(非終端結點)——度不為零的結點
孩子和雙親——結點的子樹的根稱為該結點的孩子,相應地,該結點稱為孩子的雙親
兄弟——同一個雙親的孩子
祖先和子孫——從根到該結點所經分支上的所有結點。相應地,以某一結點為根的子樹中的任一結點稱為該結點的子孫。
結點的層次——結點的層次從根開始定義,根結點的層次為1,其孩子結點的層次為2,……
堂兄弟——雙親在同一層的結點
樹的深度——樹中結點的最大層次
有序樹和無序樹——如果將樹中每個結點的各子樹看成是從左到右有次序的(即位置不能互換),則稱該樹為有序樹;否則稱為無序樹。
森林——m(m≥0)棵互不相交的樹的有限集合

到這裏,樹就講的差不多了,接下來講講二叉樹(Binary Tree)

二叉樹(Binary Tree)

樹結構多種多樣,不過我們最常用還是二叉樹,我們平時最常用的樹就是二叉樹。二叉樹的每個節點最多有兩個子節點,分別是左子節點和右子節點。二叉樹中,有兩種比較特殊的樹,分別是滿二叉樹和完全二叉樹。滿二叉樹又是完全二叉樹的一種特殊情況。

1.二叉樹的定義和特點

二叉樹的定義:
二叉樹(Binary Tree)是n(n≥0)個結點的有限集合BT,它或者是空集,或者由一個根結點和兩棵分別稱為左子樹和右子樹的互不相交的二叉樹組成 。
————————————
二叉樹的特點:
每個結點至多有二棵子樹(即不存在度大於2的結點);二叉樹的子樹有左、右之分,且其次序不能任意顛倒。

2.幾種特殊形式的二叉樹

1、滿二叉樹
定義:深度為k且有2k-1個結點的二叉樹,稱為滿二叉樹。
特點:每一層上的結點數都是最大結點數
2、完全二叉樹
定義:
深度為k,有n個結點的二叉樹當且僅當其每一個結點都與深度為k的滿二叉樹中編號從1至n的結點一一對應時,稱為完全二叉樹
特點:
特點一 : 恭弘=叶 恭弘子結點只可能在層次最大的兩層上出現;
特點二 : 對任一結點,若其右分支下子孫的最大層次為l,則其左分支下子孫的最大層次必為l 或l+1

建議看圖對應文字綜合理解

代碼創建二叉樹

首先,創建一個節點Node類

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
    //節點的權
    int value;
    //左兒子(左節點)
    Node leftNode;
    //右兒子(右節點)
    Node rightNode;
    //構造函數,初始化的時候就給二叉樹賦上權值
    public Node(int value) {
        this.value=value;
    }
    
    //設置左兒子(左節點)
    public void setLeftNode(Node leftNode) {
        this.leftNode = leftNode;
    }
    //設置右兒子(右節點)
    public void setRightNode(Node rightNode) {
        this.rightNode = rightNode;
    }

接着創建一個二叉樹BinaryTree 類

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {
    //根節點root
    Node root;
    
    //設置根節點
    public void setRoot(Node root) {
        this.root = root;
    }
    
    //獲取根節點
    public Node getRoot() {
        return root;
    }
}

最後創建TestBinaryTree 類(該類主要是main方法用來測試)來創建一個二叉樹

package demo5;
public class TestBinaryTree {

    public static void main(String[] args) {
        //創建一顆樹
        BinaryTree binTree = new BinaryTree();
        //創建一個根節點
        Node root = new Node(1);
        //把根節點賦給樹
        binTree.setRoot(root);
        //創建一個左節點
        Node rootL = new Node(2);
        //把新創建的節點設置為根節點的子節點
        root.setLeftNode(rootL);
        //創建一個右節點
        Node rootR = new Node(3);
        //把新創建的節點設置為根節點的子節點
        root.setRightNode(rootR);
        //為第二層的左節點創建兩個子節點
        rootL.setLeftNode(new Node(4));
        rootL.setRightNode(new Node(5));
        //為第二層的右節點創建兩個子節點
        rootR.setLeftNode(new Node(6));
        rootR.setRightNode(new Node(7));
    }

}

下面將會講的遍歷、查找節點、刪除節點都將圍繞這三個類開展

不難看出創建好的二叉樹如下(畫的不好,還望各位見諒):

3.二叉樹的兩種存儲方式

二叉樹既可以用鏈式存儲,也可以用數組順序存儲。數組順序存儲的方式比較適合完全二叉樹,其他類型的二叉樹用數組存儲會比較浪費存儲空間,所以鏈式存儲更合適。

我們先來看比較簡單、直觀的鏈式存儲法

接着是基於數組的順序存儲法(該例子是一棵完全二叉樹)

上面例子是一棵完全二叉樹,所以僅僅“浪費”了一個下標為0的存儲位置。如果是非完全二叉樹,則會浪費比較多的數組存儲空間,如下。

還記得堆和堆排序嗎,堆其實就是一種完全二叉樹,最常用的存儲方式就是數組。

4.二叉樹的遍歷

前面我講了二叉樹的基本定義和存儲方法,現在我們來看二叉樹中非常重要的操作,二叉樹的遍歷。這也是非常常見的面試題。

經典遍歷的方法有三種,前序遍歷中序遍歷後序遍歷

前序遍歷是指,對於樹中的任意節點來說,先打印這個節點,然後再打印它的左子樹,最後打印它的右子樹。

中序遍歷是指,對於樹中的任意節點來說,先打印它的左子樹,然後再打印它本身,最後打印它的右子樹。

後序遍歷是指,對於樹中的任意節點來說,先打印它的左子樹,然後再打印它的右子樹,最後打印這個節點本身。

我想,睿智的你已經想到了二叉樹的前、中、後序遍歷就是一個遞歸的過程。比如,前序遍歷,其實就是先打印根節點,然後再遞歸地打印左子樹,最後遞歸地打印右子樹。

在之前創建好的二叉樹代碼之上,我們來使用這三種方法遍歷一下~

依舊是在Node節點類上添加方法:可以看出遍歷方法都是用的遞歸思想

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
//===================================開始 遍歷========================================
    //前序遍歷
    public void frontShow() {
        //先遍歷當前節點的內容
        System.out.println(value);
        //左節點
        if(leftNode!=null) {
            leftNode.frontShow();
        }
        //右節點
        if(rightNode!=null) {
            rightNode.frontShow();
        }
    }

    //中序遍歷
    public void midShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.midShow();
        }
        //當前節點
        System.out.println(value);
        //右子節點
        if(rightNode!=null) {
            rightNode.midShow();
        }
    }

    //後序遍歷
    public void afterShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.afterShow();
        }
        //右子節點
        if(rightNode!=null) {
            rightNode.afterShow();
        }
        //當前節點
        System.out.println(value);
    }

}

然後依舊是在二叉樹BinaryTree 類上添加方法,並且添加的方法調用Node類中的遍歷方法

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {

    public void frontShow() {
        if(root!=null) {
            //調用節點類Node中的前序遍歷frontShow()方法
            root.frontShow();
        }
    }

    public void midShow() {
        if(root!=null) {
            //調用節點類Node中的中序遍歷midShow()方法
            root.midShow();
        }
    }

    public void afterShow() {
        if(root!=null) {
            //調用節點類Node中的後序遍歷afterShow()方法
            root.afterShow();
        }
    }

}

依舊是在TestBinaryTree類中測試

package demo5;

public class TestBinaryTree {

    public static void main(String[] args) {
        //前序遍歷樹
        binTree.frontShow();
        System.out.println("===============");
        //中序遍歷
        binTree.midShow();
        System.out.println("===============");
        //後序遍歷
        binTree.afterShow();
        System.out.println("===============");
        //前序查找
        Node result = binTree.frontSearch(5);
        System.out.println(result);
        
}

如果遞歸理解的不是很透,我可以分享一個學習的小方法:我建議各位可以這樣斷點調試,一步一步調,思維跟上,仔細推敲每一步的運行相信我,你會重新認識到遞歸!(像下面這樣貼個圖再一步一步斷點思維更加清晰)

貼一下我斷點對遞歸的分析,希望對你有一定的幫助~

二叉樹遍歷的遞歸實現思路自然、簡單,易於理解,但執行效率較低。為了提高程序的執行效率,可以顯式的設置棧,寫出相應的非遞歸遍歷算法。非遞歸的遍歷算法可以根據遞歸算法的執行過程寫出。至於代碼可以嘗試去寫一寫,這也是一種提升!具體的非遞歸算法主要流程圖貼在下面了:

二叉樹遍歷算法分析:

二叉樹遍歷算法中的基本操作是訪問根結點,不論按哪種次序遍歷,都要訪問所有的結點,對含n個結點的二叉樹,其時間複雜度均為O(n)。所需輔助空間為遍歷過程中所需的棧空間,最多等於二叉樹的深度k乘以每個結點所需空間數,最壞情況下樹的深度為結點的個數n,因此,其空間複雜度也為O(n)。

5.二叉樹中節點的查找與刪除

剛才講到二叉樹的三種金典遍歷放法,那麼節點的查找同樣是可以效仿的,分別叫做前序查找、中序查找以及後序查找,下面代碼只以前序查找為例,三者查找方法思路類似~

至於刪除節點,有三種情況:

1、如果刪除的是根節點,那麼二叉樹就完全被刪了
2、如果刪除的是雙親節點,那麼該雙親節點以及他下面的所有子節點所構成的子樹將被刪除
3、如果刪除的是恭弘=叶 恭弘子節點,那麼就直接刪除該恭弘=叶 恭弘子節點

那麼,我把完整的三個類給貼出來(包含創建、遍歷、查找、刪除)

依舊是Node節點類

package demo5;
/*
 * 節(結)點類 
 */
public class Node {
    //節點的權
    int value;
    //左兒子
    Node leftNode;
    //右兒子
    Node rightNode;
    //構造函數,初始化的時候就給二叉樹賦上權值
    public Node(int value) {
        this.value=value;
    }
    
    //設置左兒子
    public void setLeftNode(Node leftNode) {
        this.leftNode = leftNode;
    }
    //設置右兒子
    public void setRightNode(Node rightNode) {
        this.rightNode = rightNode;
    }
    
    //前序遍歷
    public void frontShow() {
        //先遍歷當前節點的內容
        System.out.println(value);
        //左節點
        if(leftNode!=null) {
            leftNode.frontShow();
        }
        //右節點
        if(rightNode!=null) {
            rightNode.frontShow();
        }
    }

    //中序遍歷
    public void midShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.midShow();
        }
        //當前節點
        System.out.println(value);
        //右子節點
        if(rightNode!=null) {
            rightNode.midShow();
        }
    }

    //後序遍歷
    public void afterShow() {
        //左子節點
        if(leftNode!=null) {
            leftNode.afterShow();
        }
        //右子節點
        if(rightNode!=null) {
            rightNode.afterShow();
        }
        //當前節點
        System.out.println(value);
    }

    //前序查找
    public Node frontSearch(int i) {
        Node target=null;
        //對比當前節點的值
        if(this.value==i) {
            return this;
        //當前節點的值不是要查找的節點
        }else {
            //查找左兒子
            if(leftNode!=null) {
                //有可能可以查到,也可以查不到,查不到的話,target還是一個null
                target = leftNode.frontSearch(i);
            }
            //如果不為空,說明在左兒子中已經找到
            if(target!=null) {
                return target;
            }
            //查找右兒子
            if(rightNode!=null) {
                target=rightNode.frontSearch(i);
            }
        }
        return target;
    }
    
    //刪除一個子樹
    public void delete(int i) {
        Node parent = this;
        //判斷左兒子
        if(parent.leftNode!=null&&parent.leftNode.value==i) {
            parent.leftNode=null;
            return;
        }
        //判斷右兒子
        if(parent.rightNode!=null&&parent.rightNode.value==i) {
            parent.rightNode=null;
            return;
        }
        
        //遞歸檢查並刪除左兒子
        parent=leftNode;
        if(parent!=null) {
            parent.delete(i);
        }
        
        //遞歸檢查並刪除右兒子
        parent=rightNode;
        if(parent!=null) {
            parent.delete(i);
        }
    }

}

依舊是BinaryTree 二叉樹類

package demo5;
/*
 * 二叉樹Class
 */
public class BinaryTree {
    //根節點root
    Node root;
    
    //設置根節點
    public void setRoot(Node root) {
        this.root = root;
    }
    
    //獲取根節點
    public Node getRoot() {
        return root;
    }

    public void frontShow() {
        if(root!=null) {
            //調用節點類Node中的前序遍歷frontShow()方法
            root.frontShow();
        }
    }

    public void midShow() {
        if(root!=null) {
            //調用節點類Node中的中序遍歷midShow()方法
            root.midShow();
        }
    }

    public void afterShow() {
        if(root!=null) {
            //調用節點類Node中的後序遍歷afterShow()方法
            root.afterShow();
        }
    }
    //查找節點i
    public Node frontSearch(int i) {
        return root.frontSearch(i);
    }
    //刪除節點i
    public void delete(int i) {
        if(root.value==i) {
            root=null;
        }else {
            root.delete(i);
        }
    }
    
}

依舊是TestBinaryTree測試類

package demo5;

public class TestBinaryTree {

    public static void main(String[] args) {
        //創建一顆樹
        BinaryTree binTree = new BinaryTree();
        //創建一個根節點
        Node root = new Node(1);
        //把根節點賦給樹
        binTree.setRoot(root);
        //創建一個左節點
        Node rootL = new Node(2);
        //把新創建的節點設置為根節點的子節點
        root.setLeftNode(rootL);
        //創建一個右節點
        Node rootR = new Node(3);
        //把新創建的節點設置為根節點的子節點
        root.setRightNode(rootR);
        //為第二層的左節點創建兩個子節點
        rootL.setLeftNode(new Node(4));
        rootL.setRightNode(new Node(5));
        //為第二層的右節點創建兩個子節點
        rootR.setLeftNode(new Node(6));
        rootR.setRightNode(new Node(7));
        //前序遍歷樹
        binTree.frontShow();
        System.out.println("===============");
        //中序遍歷
        binTree.midShow();
        System.out.println("===============");
        //後序遍歷
        binTree.afterShow();
        System.out.println("===============");
        //前序查找
        Node result = binTree.frontSearch(5);
        System.out.println(result);
        
        System.out.println("===============");
        //刪除一個子樹
        binTree.delete(4);
        binTree.frontShow();
        
    }

}

到這裏,總結一下,我們學了一種非線性表數據結構,樹。關於樹,有幾個比較常用的概念你需要掌握,那就是:根節點、恭弘=叶 恭弘子節點、父節點、子節點、兄弟節點,還有節點的高度、深度、層數,以及樹的高度等。我們平時最常用的樹就是二叉樹。二叉樹的每個節點最多有兩個子節點,分別是左子節點和右子節點。二叉樹中,有兩種比較特殊的樹,分別是滿二叉樹和完全二叉樹。滿二叉樹又是完全二叉樹的一種特殊情況。二叉樹既可以用鏈式存儲,也可以用數組順序存儲。數組順序存儲的方式比較適合完全二叉樹,其他類型的二叉樹用數組存儲會比較浪費存儲空間。除此之外,二叉樹里非常重要的操作就是前、中、後序遍歷操作,遍歷的時間複雜度是O(n),你需要理解並能用遞歸代碼來實現。

如果本文章對你有幫助,哪怕是一點點,請點個讚唄,謝謝~

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

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

[UWP]通過自定義XamlCompositionBrushBase實現圖片平鋪

1. 什麼是XamlCompositionBrushBase

我早就想試試自定義XamlCompositionBrushBase,但一直沒機會。上一篇文章介紹到,原理很簡單,但每次都要寫這些代碼很繁瑣,正好就用這個作為例子試試XamlCompositionBrushBase。

CompositionBrush靈活多變,它的基本用法如下:

  1. 通過Compositor創建CompositionBrush;
  2. 配置CompositionBrush;
  3. 創建SpriteVisual並將它的Brush設置為CompositionBrush;
  4. 使用 將SpriteVisual設置到某個UIElement的可視化層里。

這些步驟很繁瑣,而且不能用在XAML中。XamlCompositionBrushBase提供了將CompositionBrush用在XAML中一個橋樑,他繼承自Brush類,可以直接像普通的XAML 畫筆(如SolidColorBrush)那樣直接用在XAML中。

如上圖所示,中已經提了很不少XamlCompositionBrushBase的實現,它們的使用方式已經有很多文章介紹,這裏不一一列舉。

2. 自定義XamlCompositionBrushBase

這篇文章將介紹一個自定義的畫筆:TiledImageBrush,它的主要目標是實現ImageBrush沒有的圖片平鋪功能,並且它可以在XAML中使用,使用方式如下:

<Rectangle IsHitTestVisible="False">
    <Rectangle.Fill>
        <controls:TiledImageBrush Source="ms-appx:///Assets/flutter.png"/>
    </Rectangle.Fill>
</Rectangle>

順便複習下普通的ImageBrush的用法:

<Rectangle >
    <Rectangle.Fill>
        <ImageBrush ImageSource="ms-appx:///Assets/flutter.png"/>
    </Rectangle.Fill>
</Rectangle>

看起來TiledImageBrush的用法是不是和ImageBrush很像?接下來講解TiledImageBrush的實現步驟。TiledImageBrush繼承自XamlCompositionBrushBase,而實現XamlCompositionBrushBase的一般步驟如下:

protected override void OnConnected()
{
    // Delay creating composition resources until they're required.
    if (CompositionBrush == null)
    {
         CompositionBrush = CreateCompositionBrush();//Create A CompositionBrush.
    }
}

protected override void OnDisconnected()
{
    // Dispose of composition resources when no longer in use.
    if (CompositionBrush != null)
    {
        CompositionBrush.Dispose();
        CompositionBrush = null;
    }
}

首先重寫,當畫筆在屏幕上首次用於繪製元素時會調用這個函數。在這個函數里創建CompositionBrush並賦值給。

然後重寫,它在畫筆不再用於繪製任何元素時被調用。在這個函數里盡可能地釋放各種資源,例如CompositionBrush。這兩步就是實現XamlCompositionBrushBase的基本步驟。

創建CompositionBrush有很多種玩法,我之前寫過兩篇文章分別介紹 及 。這裏使用這篇文章里介紹到的代碼,首先使用LoadedImageSurface.StartLoadFromUri創建CompositionSurfaceBrush,然後加入到BorderEffect里實現圖片平鋪,然後把產生的CompositionEffectBrush賦值給XamlCompositionBrushBase.CompositionBrush

TiledImageBrush中添加了Source屬性用於設置圖片Uri(實際上是個ImageSource類型),模仿ImageBrush,這裏的Source也是一個ImageSource類型的屬性,雖然實際上使用的是它的UriSource。詳細代碼如下:

public ImageSource Source
{
    get => (ImageSource)GetValue(SourceProperty);
    set => SetValue(SourceProperty, value);
}

private void UpdateSurface()
{
    if (Source != null && _surfaceBrush != null)
    {
        var uri = (Source as BitmapImage)?.UriSource ?? new Uri("ms-appx:///");
        _surface = LoadedImageSurface.StartLoadFromUri(uri);
        _surfaceBrush.Surface = _surface;
    }
}

OnConnected的詳細代碼如下:

protected override void OnConnected()
{
    base.OnConnected();

    if (CompositionBrush == null)
    {
        _surfaceBrush = Compositor.CreateSurfaceBrush();
        _surfaceBrush.Stretch = CompositionStretch.None;

        UpdateSurface();

        _borderEffect = new BorderEffect()
        {
            Source = new CompositionEffectSourceParameter("source"),
            ExtendX = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap,
            ExtendY = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap
        };

        _borderEffectFactory = Compositor.CreateEffectFactory(_borderEffect);
        _borderEffectBrush = _borderEffectFactory.CreateBrush();
        _borderEffectBrush.SetSourceParameter("source", _surfaceBrush);
        CompositionBrush = _borderEffectBrush;
    }
}

這樣一個基本的XamlCompositionBrush就完成了,完整的代碼可以在這裏查看:

3. 參考

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

Vue躬行記(9)——Vuex

  Vuex是一個專為Vue.js設計的狀態管理庫,適用於多組件共享狀態的場景。Vuex能集中式的存儲和維護所有組件的狀態,並提供相關規則保證狀態的獨立性、正確性和可預測性,這不僅讓調試變得可追蹤,還讓代碼變得更結構化且易維護。本文所使用的Vuex,其版本是3.1.1。

一、基本用法

  首先需要引入Vue和Vuex兩個庫,如果像下面這樣在Vue之後引入Vuex,那麼Vuex會自動調用Vue.use()方法註冊其自身;但如果以模塊的方式引用,那麼就得顯式地調用Vue.use()。注意,因為Vuex依賴Promise,所以對於那些不支持Promise的瀏覽器,要使用Vuex的話,得引入相關的polyfill庫,例如es6-promise。

<script src="js/vue.js"></script>
<script src="js/vuex.js"></script>

  然後創建Vuex應用的核心:Store(倉庫)。它是一個容器,保存着大量的響應式狀態(State),並且這些狀態不能直接修改,需要顯式地將修改請求提交到Mutation(變更)中才能實現更新,因為這樣便於追蹤每個狀態的變化。在下面的示例中,初始化了一個digit狀態,並在mutations選項中添加了兩個可將其修改的方法。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    add: state => state.digit++,
    minus: state => state.digit--
  }
});

  接着創建根實例,並將store實例注入,從而讓整個應用都能讀寫其中的狀態,在組件中可通過$store屬性訪問到它,如下所示,以計算屬性的方式讀取digit狀態,並通過調用commit()方法來修改該狀態。

var vm = new Vue({
  el: "#container",
  store: store,
  computed: {
    digit() {
      return this.$store.state.digit;
    }
  },
  methods: {
    add() {
      this.$store.commit("add");
    },
    minus() {
      this.$store.commit("minus");
    }
  }
});

  最後將根實例中的方法分別註冊到兩個按鈕的點擊事件中,如下所示,每當點擊這兩個按鈕時,狀態就會更新,並在頁面中显示。

<div id="container">
  <p>{{digit}}</p>
  <button @click="add">增加</button>
  <button @click="minus">減少</button>
</div>

二、主要組成

  Vuex的主要組成除了上一節提到的Store、State和Mutation之外,還包括Getter和Action,本節會對其中的四個做重點講解,它們之間的關係如圖2所示。

圖2  四者的關係

1)State

  State是一個可存儲狀態的對象,在應用的任何位置都能被訪問到,並且作為單一數據源(Single Source Of Truth)而存在。

  當組件需要讀取大量狀態時,一個個的聲明成計算屬性會顯得過於繁瑣而冗餘,於是Vuex提供了一個名為mapState()的輔助函數,用來將狀態自動映射成計算屬性,它的參數既可以是數組,也可以是對象。

  當計算屬性的名稱與狀態名稱相同,並且不需要做額外處理時,可將名稱組成一個字符串數組傳遞給mapState()函數,在組件中可按原名調用,如下所示。

var vm = new Vue({
  computed: Vuex.mapState([ "digit" ])
});

  當計算屬性的名稱與狀態名稱不同,或者計算屬性讀取的是需要處理的狀態時,可將一個對象傳遞給mapState()函數,其鍵就是計算屬性的名稱,而其值既可以是函數,也可以是字符串,如下代碼所示。如果是函數,那麼它的第一個參數是state,即狀態對象;如果是字符串,那麼就是從state中指定一個狀態作為計算屬性。

var vm = new Vue({
  computed: Vuex.mapState({
    digit: state => state.digit,
    alias: "digit"        //相當於state => state.digit
  })
});

  因為mapState()函數返回的是一個對象,所以當組件內已經包含計算屬性時,可以對其應用擴展運算符(…)來進行合併,如下所示,這是一種極為簡潔的寫法。

var vm = new Vue({
  computed: {
    name() {},
    ...Vuex.mapState([ "digit" ])
  }
});

2)Getter

  Getter是從State中派生出的狀態,當多個組件要對同一個狀態進行相同的處理時,就需要將狀態轉移到Getter中,以免產生重複的冗餘代碼。

  Getter相當於Store的計算屬性,它能接收兩個參數,第一個是state對象,第二個是可選的getters對象,該參數能讓不同的Getter之間相互訪問。Getter的返回值會被緩存,並且只有當依賴值發生變化時才會被重新計算。不過當返回值是函數時,其結果就不會被緩存,如下所示,其中caculate返回的是個数字,而sum返回的是個函數。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  getters: {
    caculate: state => {
      return state.digit + 2;
    },
    sum: state => right => {
      return state.digit + right;
    }
  }
});

  在組件內可通過this.$store.getters訪問到Getter中的數據,如下所示,讀取了上一個示例中的兩個Getter。

var vm = new Vue({
  methods: {
    add() {
      this.$store.getters.caculate;
      this.$store.getters.sum(1);
    }
  }
});

  Getter也有一個輔助函數,用來將Getter自動映射為組件的計算屬性,名字叫mapGetters(),其參數也是數組或對象。但與之前的mapState()不同,當參數是對象時,其值不能是函數,只能是字符串,如下所示,為了對比兩種寫法,聲明了兩個computed選項。

var vm = new Vue({
  computed: Vuex.mapGetters([ "caculate" ]),
  computed: Vuex.mapGetters({
    alias: "caculate"
  })
});

3)Mutation

  更改狀態的唯一途徑是提交Mutation,Vuex中的Mutation類似於事件,也包含一個類型和回調函數,在函數體中可進行狀態更改的邏輯,並且它能接收兩個參數,第一個是state對象,第二個是可選的附加數據,叫載荷(Payload)。下面這個Mutation的類型是“interval”,接收了兩個參數。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    interval: (state, payload) => state.digit += payload.number
  }
});

  在組件中不能直接調用Mutation的回調函數,得通過this.$store.commit()方法觸發更新,如下所示,採用了兩種提交方式,第一種是傳遞type參數,第二種是傳遞包含type屬性的對象。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.commit("interval", { number: 2 });             //第一種
      this.$store.commit({ type: "interval", number: 2 });       //第二種
    }
  }
});

  當多人協作時,Mutation的類型適合寫成常量,這樣更容易維護,也能減少衝突。

const INTERVAL = "interval";

  Mutation有一個名為mapMutations()的輔助函數,其寫法和mapState()相同,它能將Mutation自動映射為組件的方法,如下所示。

var vm = new Vue({
  methods: Vuex.mapMutations(["interval"])
  //相當於
  methods: {
    interval(payload) {
      this.$store.commit(INTERVAL, payload);
    }
  }
});

  注意,為了能追蹤狀態的變更,Mutation只支持同步的更新,如果要異步,那麼得使用Action。

4)Action

  Action類似於Mutation,但不同的是它可以包含異步操作,並且只能用來通知Mutation,不會直接更新狀態。Action的回調函數能接收兩個參數,第一個是與Store實例具有相同屬性和方法的context對象(注意,不是Store實例本身),第二個是可選的附加數據,如下所示,調用commit()方法提交了一個Mutation。

const store = new Vuex.Store({
  actions: {
    interval(context, payload) {
      context.commit("interval", payload);
    }
  }
});

  在組件中能通過this.$store.dispatch()方法分發Action,如下所示,與commit()方法一樣,它也有兩種提交方式。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.dispatch("interval", { number: 2 });            //第一種
      this.$store.dispatch({type: "interval", number: 2});        //第二種
    }
  }
});

  注意,由於dispatch()方法返回的是一個Promise對象,因此它能以一種更優雅的方式來處理異步操作,如下所示。

var vm = new Vue({
  methods: {
    interval() {
      this.$store.dispatch("interval", { number: 2 }).then(() => {
        console.log("success");
      });
    }
  }
});

  Action有一個名為mapActions()的輔助函數,其寫法和mapState()相同,它能將Action自動映射為組件的方法,如下所示。

var vm = new Vue({
  methods: Vuex.mapActions([ "interval" ])
});

三、模塊

  當應用越來越大時,為了避免Store變得過於臃腫,有必要將其拆分到一個個的模塊(Module)中。每個模塊就是一個對象,包含屬於自己的State、Getter、Mutation和Action,甚至還能嵌套其它模塊。

1)局部狀態

  對於模塊內部的Getter和Mutation,它們接收的第一個參數是模塊的局部狀態,而Getter的第三個參數rootState和Action的context.rootState屬性可訪問根節點狀態(即全局狀態),如下所示。

const moduleA = {
  state: { digit: 0 },
  mutations: {
    add: state => state.digit++
  },
  getters: {
    caculate: (state, getter, rootState) => {
      return state.digit + 2;
    }
  },
  actions: {
    interval(context, payload) {
      context.commit("add", payload);
    }
  }
};

2)命名空間

  默認情況下,只有在訪問State時需要帶命名空間,而Getter、Mutation和Action的調用方式不變。將之前的moduleA模塊註冊到Store實例中,如下所示,modules選項的值是一個子模塊對象,其鍵是模塊名稱。

const store = new Vuex.Store({
  modules: {
    a: moduleA
  }
});

  如果要訪問模塊中的digit狀態,那麼可以像下面這樣寫。

store.state.a.digit;

  當模塊的namespaced屬性為true時,它的Getter、Mutation和Action也會帶命名空間,在使用時,需要添加命名空間前綴,如下代碼所示,此舉大大提升了模塊的封裝性和復用性。

const moduleA = {
  namespaced: true
};
var vm = new Vue({
  el: "#container",
  store: store,
  methods: {
    add() {
      this.$store.commit("a/add");
    },
    caculate() {
      this.$store.getters["a/caculate"];
    }
  }
});

  如果要在帶命名空間的模塊中提交全局的Mutation或分發全局的Action,那麼只要將{root: true}作為第三個參數傳給commit()或dispatch()就可實現,如下所示。

var vm = new Vue({
  methods: {
    add() {
      this.$store.dispatch("add", null, { root: true });
      this.$store.commit("add", null, { root: true });
    }
  }
});

  如果要在帶命名空間的模塊中註冊全局的Action,那麼需要將其修改成對象的形式,然後添加root屬性並設為true,再將Action原先的定義轉移到handler()函數中,如下所示。

const moduleA = {
  actions: {
    interval: {
      root: true,
      handler(context, payload) {}
    }
  }
};

3)輔助函數

  當使用mapState()、mapGetters()、mapMutations()和mapActions()四個輔助函數對帶命名空間的模塊做映射時,需要顯式的包含命名空間,如下所示。

var vm = new Vue({
  computed: Vuex.mapState({
    digit: state => state.a.digit
  }),
  methods: Vuex.mapMutations({
    add: "a/add"
  })
});

  這四個輔助函數的第一個參數都是可選的,用於綁定命名空間,可簡化映射過程,如下所示。

var vm = new Vue({
  computed: Vuex.mapState("a", {
    digit: state => state.digit
  }),
  methods: Vuex.mapMutations("a", {
    add: "add"
  })
});

  Vuex還提供了另一個輔助函數createNamespacedHelpers(),可創建綁定命名空間的輔助函數,如下所示。

const { mapState, mapMutations } = Vuex.createNamespacedHelpers("a");

四、動態註冊

  在創建Store實例后,可通過registerModule()方法動態註冊模塊,如下代碼所示,調用了兩次registerModule()方法,第一次註冊了模塊“a”,第二次註冊了嵌套模塊“a/b”。

const store = new Vuex.Store();
store.registerModule("a", moduleA);    
store.registerModule(["a", "b"], moduleAB);

  通過store.state.a和store.state.a.b可訪問模塊的局部狀態。如果要卸載動態註冊的模塊,那麼可以通過unregisterModule()方法實現。

  registerModule()方法的第三個參數是可選的配置對象,當preserveState屬性的值為true時(如下所示),在註冊模塊時會忽略模塊中的狀態,即無法在store中讀取模塊中的狀態。

store.registerModule("a", moduleA, { preserveState: true });

五、表單處理

  表單默認能直接修改組件的狀態,但是在Vuex中,狀態只能由Mutation觸發更新。為了能更好的追蹤狀態的變化,也為了能更符合Vuex的思維,需要讓表單控件與狀態綁定在一起,並通過input或change事件監聽狀態更新的行為,如下所示。

<div id="container">
  <input :value="digit" @input="add" />
</div>

  然後在Store實例中初始化digit狀態,並添加更新狀態的Mutation,如下所示。

const store = new Vuex.Store({
  state: {
    digit: 0
  },
  mutations: {
    add: (state, value) => {
      state.digit = value;
    }
  }
});

  最後在創建根實例時,將digit狀態映射成它的計算屬性,在事件處理程序add()中調用commit()方法,並將控件的值作為第二個參數傳入,如下所示。

var vm = new Vue({
  el: "#container",
  store: store,
  computed: Vuex.mapState(["digit"]),
  methods: {
    add(e) {
      this.$store.commit("add", e.target.value);
    }
  }
});

  還有一個方法也能實現相同的功能,那就是在控件上使用v-model指令,但需要與帶setter的計算屬性配合,如下所示(只給出了關鍵部分的代碼)。

<div id="container">
  <input v-model="digit" />
</div>
<script>
  var vm = new Vue({
    computed: {
      digit: {
        get() {
          return this.$store.state.digit;
        },
        set(value) {
          this.$store.commit("add", value);
        }
      }
    }
  });
</script>

 

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

印尼省長要整治世界最髒河流 目標可飲用

摘錄自2018年9月20日中央社雅加達報導

西大魯河(Citarum River)全長300公里,是西爪哇省最長河流,是逾3500萬民眾生活用水來源,也是印尼重要的灌溉河流。美國布萊克史密斯研究所(Blacksmith Institute)研究顯示,每天排入西大魯河的化學廢料多達280公噸,有毒化學物質已達驚人程度,河水的鉛含量比美國的安全飲用水標準超出至少1000倍,因此被稱為「全世界最髒的河流」。

西爪哇省新任省長利德宛(Ridwan Kamil)向印尼國家通訊社安塔拉(Antara)表示,他有多管齊下的計畫,可以實現總統佐科威日前訂下的目標,讓西大魯河的水質在2025年前達到飲用標準。

整體計畫先期將先由印尼國營的軍武公司賓達(PT Pindad)設計的疏浚機開路,清理河中數量龐大的垃圾。中程計畫則將引進較佳的系統,用來遏阻和處理大批垃圾被傾倒至西大魯河的問題。

利德宛指出,長期而言,西爪哇省政府希望把周遭工業區及工廠遷移至其他地方。同時也要關注位於雅加達近郊勿加西(Bekasi)的瑪琅河(Malang River)等水路。

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

要吸管請「開金口」 加州餐廳明年起不主動提供

摘錄自2018年9月21日蘋果日報美國加州報導

美聯社報導,加州州長布朗(Jerry Brown)簽署通過法案,明年起禁止州內餐廳主動提供顧客塑膠吸管服務,首開全美先例。餐廳業者若不配合將先予以警告,兩次警告後開罰,每年最高300美元(約9,200台幣),由州內衛生人員負責不定時執行稽查。不過,此法僅適用於正式、有服務生點餐的餐廳,速食業者不再此限。儘管不是直接禁止,部分共和黨人仍批評這項新法「管太多」。

民主黨的布朗將環保議題列為優先施政考量。他指,每年大量塑膠垃圾流入海中,殺死鯨魚和魚類,污染最終進到民眾的食物和供水:「我們對一次性便利用具的迷戀,將招致災難性的後果。所有形式的塑膠製品,塑膠吸管、寶特瓶、包裝、塑膠袋等,都在使地球窒息。」

同天,布朗簽署通過另項法案,規定販售兒童餐的各級餐廳,都必須將菜單飲料預設為牛奶和開水。兩項法案皆從明年1月1日開始生效。

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!

020.掌握Pod-Pod基礎使用

一 Pod定義詳解

1.1 完整Pod定義文件

  1 apiVersion: v1			#必選,版本號,例如v1,版本號必須可以用 kubectl api-versions 查詢到
  2 kind: Pod				#必選,Pod
  3 metadata:				#必選,元數據
  4   name: string			#必選,Pod名稱,需符合RFC 1035規範
  5   namespace: string			#必選,Pod所屬的命名空間,默認為"default"
  6   labels:				#自定義標籤
  7     - name: string			#自定義標籤名字
  8   annotations:			#自定義註釋列表
  9     - name: string
 10 spec:				#必選,Pod中容器的詳細定義
 11   containers:			#必選,Pod中容器列表
 12   - name: string			#必選,容器名稱,需符合RFC 1035規範
 13     image: string			#必選,容器的鏡像名稱
 14     imagePullPolicy: [ Always|Never|IfNotPresent ]	#獲取鏡像的策略,Alawys表示每次都嘗試下載鏡像,IfnotPresent表示優先使用本地鏡像,否則下載鏡像,Nerver表示僅使用本地鏡像
 15     command: [string]		#容器的啟動命令列表,如不指定,使用打包時使用的啟動命令
 16     args: [string]			#容器的啟動命令參數列表
 17     workingDir: string		#容器的工作目錄
 18     volumeMounts:			#掛載到容器內部的存儲卷配置
 19     - name: string			#引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名
 20       mountPath: string		#存儲卷在容器內mount的絕對路徑,應少於512字符
 21       readOnly: boolean		#是否為只讀模式,默認為讀寫模式
 22     ports:				#需要暴露的端口庫號列表
 23     - name: string			#端口的名稱
 24       containerPort: int		#容器需要監聽的端口號
 25       hostPort: int		        #容器所在主機需要監聽的端口號,默認與Container相同
 26       protocol: string		#端口協議,支持TCP和UDP,默認TCP
 27     env:				#容器運行前需設置的環境變量列表
 28     - name: string			#環境變量名稱
 29       value: string		        #環境變量的值
 30     resources:			#資源限制和請求的設置
 31       limits:			#資源限制的設置
 32         cpu: string		        #CPU的限制,單位為core數,將用於docker run --cpu-shares參數
 33         memory: string		#內存限制,單位可以為Mib/Gib,將用於docker run --memory參數
 34       requests:			#資源請求的設置
 35         cpu: string		        #CPU請求,容器啟動的初始可用數量
 36         memory: string		#內存請求,容器啟動的初始可用數量
 37     livenessProbe:			#對Pod內各容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器,檢查方法有exec、httpGet和tcpSocket,對一個容器只需設置其中一種方法即可
 38       exec:			        #對Pod容器內檢查方式設置為exec方式
 39         command: [string]		#exec方式需要制定的命令或腳本
 40       httpGet:			#對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、port
 41         path: string
 42         port: number
 43         host: string
 44         scheme: string
 45         HttpHeaders:
 46         - name: string
 47           value: string
 48       tcpSocket:			#對Pod內個容器健康檢查方式設置為tcpSocket方式
 49          port: number
 50        initialDelaySeconds: 0	#容器啟動完成后首次探測的時間,單位為秒
 51        timeoutSeconds: 0		#對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒
 52        periodSeconds: 0		#對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次
 53        successThreshold: 0
 54        failureThreshold: 0
 55        securityContext:
 56          privileged: false
 57     restartPolicy: [Always | Never | OnFailure]	#Pod的重啟策略,Always表示一旦不管以何種方式終止運行,kubelet都將重啟,OnFailure表示只有Pod以非0退出碼退出才重啟,Nerver表示不再重啟該Pod
 58     nodeSelector: obeject		#設置NodeSelector表示將該Pod調度到包含這個label的node上,以key:value的格式指定
 59     imagePullSecrets:		#Pull鏡像時使用的secret名稱,以key:secretkey格式指定
 60     - name: string
 61     hostNetwork: false		#是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡
 62     volumes:			#在該pod上定義共享存儲卷列表
 63     - name: string			#共享存儲卷名稱 (volumes類型有很多種)
 64       emptyDir: {}			#類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值
 65       hostPath: string		#類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄
 66         path: string		#Pod所在宿主機的目錄,將被用於同期中mount的目錄
 67       secret:			#類型為secret的存儲卷,掛載集群與定義的secre對象到容器內部
 68         scretname: string
 69         items:
 70         - key: string
 71           path: string
 72       configMap:			#類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部
 73         name: string
 74         items:
 75         - key: string
 76           path: string

二 Pod的基本用法

2.1 創建Pod


Pod可以由1個或多個容器組合而成,通常對於緊耦合的兩個應用,應該組合成一個整體對外提供服務,則應該將這兩個打包為一個pod。

屬於一個Pod的多個容器應用之間相互訪問只需要通過localhost即可通信,這一組容器被綁定在一個環境中。

  1 [root@k8smaster01 study]# vi frontend-localredis-pod.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: redis-php
  6   label:
  7     name: redis-php
  8 spec:
  9   containers:
 10   - name: frontend
 11     image: kubeguide/guestbook-php-frontend:localredis
 12     ports:
 13     - containersPort: 80
 14   - name: redis-php
 15     image: kubeguide/redis-master
 16     ports:
 17     - containersPort: 6379
 18 
 19 [root@k8smaster01 study]# kubectl create -f frontend-localredis-pod.yaml
 20 


2.2 查看Pod

  1 [root@k8smaster01 study]# kubectl get pods	                #READY為2/2,表示此Pod中運行了yaml定義的兩個容器
  2 NAME        READY   STATUS    RESTARTS   AGE
  3 redis-php   2/2     Running   0          7m45s
  4 [root@k8smaster01 study]# kubectl describe pod redis-php	#查看詳細信息
  5 


三 靜態Pod

3.1 靜態Pod概述


靜態pod是由kubelet進行管理的僅存在於特定Node的Pod上,他們不能通過API Server進行管理,無法與ReplicationController、Deployment或者DaemonSet進行關聯,並且kubelet無法對他們進行健康檢查。靜態Pod總是由kubelet進行創建,並且總是在kubelet所在的Node上運行。

創建靜態Pod有兩種方式:配置文件或者HTTP方式。

3.2 配置文件方式創建

  1 [root@k8snode01 ~]# mkdir -p /etc/kubelet.d
  2 [root@k8snode01 ~]# vi /etc/kubelet.d/static-web.yaml
  3 apiVersion: v1
  4 kind: Pod
  5 metadata:
  6   name: static-web
  7   label:
  8     name: static-web
  9 spec:
 10   containers:
 11   - name: static-web
 12     image: nginx
 13     ports:
 14     - name: web
 15       containersPort: 80
 16 
 17 [root@k8snode01 ~]# vi /etc/systemd/system/kubelet.service
 18 ……
 19   --config=/etc/kubelet.d/ \·				#加入此參數
 20 ……
 21 [root@k8snode01 ~]# systemctl daemon-reload
 22 [root@k8snode01 ~]# systemctl restart kubelet.service	#重啟kubelet
 23 [root@k8snode01 ~]# docker ps				#查看創建的pod



提示:由於靜態pod不能通過API Server進行管理,因此在Master節點執行刪除操作後會變為Pending狀態,且無法刪除。刪除該pod只能在其運行的node上,將定義POD的yaml刪除。

3.3 HTTP方式


通過設置kubelet的啟動參數–mainfest-url,會定期從該URL下載Pod的定義文件,並以.yaml或.json文件的格式進行解析,從而創建Pod。

四 Pod容器共享Volume

4.1 共享Volume


在同一個Pod中的多個容器能夠共享Pod級別的存儲就Volume。Volume可以被定義為各種類型,多個容器各自進行掛載操作,將一個Volume掛載為容器內部需要的目錄。


示例1:

Pod級別設置Volume “app-logs”,同時Pod包含兩個容器,Tomcat向該Volume寫日誌,busybox讀取日誌文件。

  1 [root@k8smaster01 study]# vi pod-volume-applogs.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: volume-pod
  6 spec:
  7   containers:
  8   - name: tomcat
  9     image: tomcat
 10     ports:
 11     - containerPort: 8080
 12     volumeMounts:
 13     - name: app-logs
 14       mountPath: /usr/local/tomcat/logs
 15   - name: logreader
 16     image: busybox
 17     command: ["sh","-c","tail -f /logs/catalina*.log"]
 18     volumeMounts:
 19     - name: app-logs
 20       mountPath: /logs
 21   volumes:
 22   - name: app-logs
 23     emptyDir: {}

解釋:

Volume名:app-logs;

emptyDir:為Pod分配到Node的時候創建。無需指定宿主機的目錄文件,為Kubernetes自動分配的目錄。

  1 [root@k8smaster01 study]# kubectl create -f pod-volume-applogs.yaml	#創建
  2 [root@k8smaster01 study]# kubectl get pods				#查看
  3 [root@k8smaster01 study]# kubectl logs volume-pod -c busybox	#讀取log




  1 [root@k8smaster01 study]# kubectl exec -it volume-pod -c tomcat -- ls /usr/local/tomcat/logs
  2 catalina.2019-06-29.log      localhost_access_log.2019-06-29.txt
  3 host-manager.2019-06-29.log  manager.2019-06-29.log
  4 localhost.2019-06-29.log
  5 [root@k8smaster01 study]# kubectl exec -it volume-pod -c tomcat -- tail /usr/local/tomcat/logs/catalina.2019-06-29.log



提示:通過tomcat容器可查看日誌,對比busybox通過共享Volume查看的日誌是否一致。

五 Pod配置管理

5.1 Pod配置概述


應用部署的一個最佳實踐是將應用所需的配置信息與程序進行分離,使程序更加靈活。將相應的應用打包為鏡像,可以通過環境變量或者外掛volume的方式在創建容器的時候進行配置注入,從而實現更好的復用。

Kubernetes提供一種統一的應用配置管理方案:ConfigMap。

5.2 ConfigMap概述


ConfigMap供容器使用的主要場景:

  • 生成容器內部的環境變量;
  • 設置容器的啟動命令的參數(需設置為環境變量);
  • 以volume的形式掛載為容器內部的文件或者目錄。


ConfigMap以一個或多個key:value的形式定義。value可以是string也可以是一個文件內容,可以通過yaml配置文件或者通過kubectl create configmap 的方式創建configMap。

5.3 創建ConfigMap資源對象——yaml方式

  1 [root@k8smaster01 study]# vi cm-appvars.yaml
  2 apiVersion: v1
  3 kind: ConfigMap
  4 metadata:
  5   name: cm-appvars
  6 data:
  7   apploglevel: info
  8   appdatadir: /var/data
  9 
 10 [root@k8smaster01 study]# kubectl create -f cm-appvars.yaml
 11 configmap/cm-appvars created
 12 [root@k8smaster01 study]# kubectl get configmaps
 13 NAME         DATA   AGE
 14 cm-appvars   2      8s
 15 [root@k8smaster01 study]# kubectl describe configmaps cm-appvars



  1 [root@k8smaster01 study]# kubectl get configmaps cm-appvars -o yaml


5.4 創建ConfigMap資源對象——命令行方式


語法1

  1 # kubectl create configmap NAME --from-file=[key=]source --from-file=[key=]source



解釋:通過–from-file參數從文件中創建,可以指定key名稱,也可以制定多個key。

語法2

  1 # kubectl create configmap NAME --from-file=config-files-dir



解釋:通過–from-file參數從目錄中創建,該目錄下的每個配置文件名都被設置為key,文件的內容被設置為value。

語法3

  1 # kubectl create configmap NAME --from-literal=key1=value1 --from-literal=key2=value2



解釋:通過–from-literal參數從文本中創建,直接將指定的key#=value#創建為ConfigMap的內容。

5.5 Pod使用ConfigMap


容器應用使用ConfigMap有兩種方式:

  • 通過環境變量獲取ConfigMap中的內容;
  • 通過Volume掛載的方式將ConfigMap中的內容掛載為容器內容的文件或目錄。

  1 [root@k8smaster01 study]# vi cm-test-pod.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: cm-test-pod
  6 spec:
  7   containers:
  8   - name: cm-test
  9     image: busybox
 10     command: ["/bin/sh","-c","env|grep APP"]	#容器里執行查看環境變量的命令
 11     env:
 12     - name: APPLOGLEVEL				#定義容器環境變量名稱
 13       valueFrom:
 14         configMapKeyRef:			#環境變量的值來自ConfigMap
 15           name: cm-appvars			#指定來自cm-appvars的ConfigMap
 16           key: apploglevel			#key為apploglevel
 17     - name: APPDATADIR
 18       valueFrom:
 19         configMapKeyRef:
 20           name: cm-appvars
 21           key: appdatadir
 22 
 23 [root@k8smaster01 study]# kubectl create -f cm-test-pod.yaml
 24 [root@k8smaster01 study]# kubectl get pods
 25 NAME          READY   STATUS      RESTARTS   AGE
 26 cm-test-pod   0/1     Completed   2          24s



【掛載形式-待補充】

5.6 ConfigMap限制


  • Configmap必須在pod創建之間創建;
  • ConfigMap受到namespace的限制,只有同一個命名空間下才能引用;
  • ConfigMap暫時無法配置配額;
  • 靜態的pod無法使用ConfigMap;
  • 在使用volumeMount掛載的時候,configMap基於items創建的文件會整體的將掛載數據卷的容器的目錄下的文件全部覆蓋。

六 Pod獲取自身信息

6.1 Downward API


pod擁有唯一的名字、IP地址,並且處於某個Namespace中。pod的容器內獲取pod的信息科通過Downward API實現。具體有以下兩種方式:

  • 環境變量:用於單個變量,可以將pod信息和container信息注入容器內部;
  • volume掛載:將數組類信息生成為文件,掛載至容器內部。


舉例1:通過Downward API將Pod的IP、名稱和所在的Namespace注入容器的環境變量。

  1 [root@k8smaster01 study]# vi dapi-test-pod.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: dapi-test-pod
  6 spec:
  7   containers:
  8     - name: test-container
  9       image: busybox
 10       command: [ "/bin/sh", "-c", "env" ]
 11       env:
 12         - name: MY_POD_NAME
 13           valueFrom:
 14             fieldRef:
 15               fieldPath: metadata.name
 16         - name: MY_POD_NAMESPACE
 17           valueFrom:
 18             fieldRef:
 19               fieldPath: metadata.namespace
 20         - name: MY_POD_IP
 21           valueFrom:
 22             fieldRef:
 23               fieldPath: status.podIP
 24   restartPolicy: Never



提示:Downward API提供如下變量:

metadata.name:Pod的名稱,當Pod通過RC生成時,其名稱是RC隨機產生的唯一名稱;

status.podIP:Pod的IP地址,POd的IP屬於狀態數據,而非元數據;

metadata.namespace:Pod所在的namespace。

  1 [root@k8smaster01 study]# kubectl create -f dapi-test-pod.yaml
  2 [root@k8smaster01 study]# kubectl logs dapi-test-pod | grep MY_POD
  3 MY_POD_NAMESPACE=default
  4 MY_POD_IP=172.30.240.4
  5 MY_POD_NAME=dapi-test-pod
  6 



舉例2:通過Downward API將Container的自願請求和限制信息注入容器的環境變量。

  1 [root@k8smaster01 study]# vi dapi-test-pod-container-vars.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: dapi-test-pod-container-vars
  6 spec:
  7   containers:
  8     - name: test-container
  9       image: busybox
 10       imagePullPolicy: Never
 11       command: [ "/bin/sh", "-c" ]
 12       args:
 13       - while true; do
 14           echo -en '\n';
 15           printenv MY_CPU_REQUEST MY_CPU_LIMIT;
 16           printenv MY_MEM_REQUEST MY_MEM_LIMIT;
 17           sleep 3600;
 18         done;
 19       resources:
 20         requests:
 21           memory: "32Mi"
 22           cpu: "125m"
 23         limits:
 24           memory: "64Mi"
 25           cpu: "250m"
 26       env:
 27         - name: MY_CPU_REQUEST
 28           valueFrom:
 29             resourceFieldRef:
 30               containerName: test-container
 31               resource: requests.cpu
 32         - name: MY_CPU_LIMIT
 33           valueFrom:
 34             resourceFieldRef:
 35               containerName: test-container
 36               resource: limits.cpu
 37         - name: MY_MEM_REQUEST
 38           valueFrom:
 39             resourceFieldRef:
 40               containerName: test-container
 41               resource: requests.memory
 42         - name: MY_MEM_LIMIT
 43           valueFrom:
 44             resourceFieldRef:
 45               containerName: test-container
 46               resource: limits.memory
 47   restartPolicy: Never



提示:Downward API提供如下變量:

requests.cpu:容器的CPU請求值;

limits.cpu:容器的CPU限制值;

requests.memory:容器的內存請求值;

limits.memory:容器的內存限制值。

  1 [root@k8smaster01 study]# kubectl create -f dapi-test-pod-container-vars.yaml
  2 [root@k8smaster01 study]# kubectl logs dapi-test-pod-container-vars
  3 1
  4 1
  5 33554432
  6 67108864



舉例3:通過Downward API將Pod的Label、Annotation列表通過Volume掛載為容器內的一個文件。

  1 [root@k8smaster01 study]# vi dapi-test-pod-volume.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: dapi-test-pod-volume
  6   labels:
  7     zone: us-est-coast
  8     cluster: test-cluster1
  9     rack: rack-22
 10   annotations:
 11     build: two
 12     builder: john-doe
 13 spec:
 14   containers:
 15     - name: test-container
 16       image: busybox
 17       imagePullPolicy: Never
 18       command: [ "/bin/sh", "-c" ]
 19       args:
 20       - while true; do
 21           if [[ -e /etc/labels ]]; then
 22             echo -en '\n\n'; cat /etc/labels; fi;
 23           if [[ -e /etc/annotations ]]; then
 24             echo -en '\n\n'; cat /etc/annotations; fi;
 25           sleep 3600;
 26         done;
 27       volumeMounts:
 28         - name: podinfo
 29           mountPath: /etc
 30           readOnly: false
 31   volumes:
 32     - name: podinfo
 33       downwardAPI:
 34         items:
 35           - path: "labels"
 36             fieldRef:
 37               fieldPath: metadata.labels
 38           - path: "annotations"
 39             fieldRef:
 40               fieldPath: metadata.annotations



注意:Volume中的ddownwardAPI的items語法,將會以path的名稱生成文件。如上所示,會在容器內生產/etc/labels和/etc/annotations兩個文件,分別包含metadata.labels和metadata.annotations的全部Label。

  1 [root@k8smaster01 study]# kubectl create -f dapi-test-pod-volume.yaml
  2 [root@k8smaster01 study]# kubectl logs dapi-test-pod-volume
  3 



提示:DownwardAPI意義:

在某些集群中,集群中的每個節點需要將自身的標識(ID)及進程綁定的IP地址等信息事先寫入配置文件中,進程啟動時讀取此類信息,然後發布到某個類似註冊服務中心。此時可通過DowanwardAPI,將一個預啟動腳本或Init Container,通過環境變量或文件方式獲取Pod自身的信息,然後寫入主程序配置文件中,最後啟動主程序。本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

吸髒空氣容易失智 空污物濃度高 50歲以上患病風險增四成

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

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

統達能源宣布進軍中國大陸電動車市場

台灣統振集團旗下統達能源宣布,將聯合易能電網科技以及中國機車公司奔馬實業,共同進軍中國大陸電動車市場,以電動機車用鋰電池交換系統搶攻中國商機,最快將在2016年下半年於廣東、福建地區成立營運示範據點。

統達能源過去著重歐洲電動自行車店員市場開發,在歐洲已有10%市占率,本次為首度跨入電動機車電源的供應鏈。總經理楊模樺表示,合作夥伴易能電網科技整合了桶達的動力電池系統與台灣IBM的技術,打造「MIT(台灣製造)」的智能化電池交換服務系統,並已在台灣搭配電動機車實際上路。

奔馬實業去年在中國大陸共生產了20萬輛機車,帶來10億元人民幣的營收。本次與統達能源的合作,聚焦在電動機車與電池交換站營運事業。奔馬實業董事長湛玉娟認為,車店分離電池月租致的商業模式能透過電池交換服務系統的管理與遠端監控來提高電池之可靠性、安全性、壽命,降低營運商維護成本,並與物聯網概念結合。她相信,合作夥伴易能的電池交換系統將有效改善中國大陸電動機車的能源供給問題,也能改善電池環保管理問題。

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

【其他文章推薦】

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

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

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

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

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

※試算大陸海運運費!

Model 3 接單爆量部分是膨風?Musk:重複訂單將取消

特斯拉(Tesla Motors)平價電動車「Model 3」3 月 31 日亮相一週就狂接了 32.5 萬筆訂單,執行長 Elon Musk 之後更於 4 月 22 日指稱 Model 3 已接獲近 40 萬筆訂單。   不過,許多人對此抱持懷疑態度,認為或許有顧客重複下單導致數據「膨風」,分析師 Anton Wahlman 上週更在 Seeking Alpha 網站發文宣稱,雖然特斯拉限制每人只能預購兩台 Model 3,但他個人卻已成功下了 20 筆訂單。   Yahoo! Finance 的新聞記者隨後也宣稱,只要以不同表格遞交多筆訂單,就能成功突破每人限購 2 台的限制。   Wahlman 宣稱,這意味著特斯拉接獲的 40 萬筆訂單有一部份也許來自投機客,Model 3 龐大的需求可能只是膨風。   對此,Musk 決定重新檢視顧客的下單狀況,並把多餘的訂單全部取消。MarketWatch 2 日報導,Musk 4 月 29 日透過 Twitter 指出,重複的訂單將被刪除,每位客戶都只限購 2 台,而所有訂單中僅有 5% 預購了 2 台 Model 3,因此不太可能有投機客。   儘管如此,投機客瘋狂預購 Model 3,仍代表這款車種深獲顧客期待,畢竟 Model 3 在還未亮相前,搶著掏錢預訂 Model 3 的粉絲們就已在實體門市外排起超長人龍,這樣的熱絡景象真的是前所未見。   現在特斯拉遇到的問題,應該不是需求疲軟、而是訂單太多。有分析師認為,特斯拉當前產能不足以應付爆量需求,未來可能得籌錢擴產、甚至打造第二座組裝廠。   CNBC、Forbes 4 月中報導,巴克萊分析師 Brian Johnson 表示,他估計特斯拉需要另行籌資 30 億美元,才能滿足如雪片飛來的訂單。他說,特斯拉訂單暴增,或許會以需要打造更多汽車和電池工廠為由,尋求額外資金。   從特斯拉生產情況看來,確實有擴產需要。今年第一季,特斯拉交車數量僅有 14,820 輛,低於預期的 16,000 輛。今年全年特斯拉預計交車 80,000~90,000 輛。這讓外界憂心忡忡,擔心該公司無法從小眾車廠,蛻變為大型公司。特斯拉的加州組裝工廠,以往為通用汽車(GM)和豐田(Toyota)的合資企業,年度產能達 50 萬輛,意味特斯拉仍有大幅增產空間,能加裝設備,生產 Model 3。    (本文內容由授權使用;首圖來源: CC BY 2.0)

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

【其他文章推薦】

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

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

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

台灣寄大陸海運貨物規則及重量限制?

大陸寄台灣海運費用試算一覽表

台中搬家,彰化搬家,南投搬家前需注意的眉眉角角,別等搬了再說!