月份: 2020 年 8 月

  • 設計模式系列之一:簡單工廠模式

    設計模式系列之一:簡單工廠模式

    1.1 定義

     簡單工廠模式屬於創建型模式,又稱為靜態工廠方法模式,在簡單工廠模式中,可以根據參數的不同,來返回不同類的實例,簡單工廠模式專門定義一個類來負責創建子類的實例,被創建的類通常有一個共同的父類

    1.2 簡單工廠模式結構圖(簡版)

                                 

     Factory:工廠類,簡單工廠模式的核心,它負責實現創建所有實例的內部邏輯。工廠類的創建產品類的方法可以被外界直接調用,創建所需的產品對象

     IProduct:抽象產品類,簡單工廠模式所創建的所有對象的父類,它負責描述所有實例所共有的公共接口

     Product:具體產品類,是簡單工廠模式的目標類

    1.3 簡單工廠的實現一

     假設有一個電腦的代工生產商,它目前已經可以代工生產聯想電腦了,隨着業務的拓展,這個代工生產商還要生產惠普和mac電腦;

     這樣我們就需要用一個單獨的類來專門生產電腦,這就用到了簡單工廠模式,同時用到了繼承、封裝、多態等面向對象編程的思想,下面我們來實現簡單工廠模式

    1.3.1 產品抽象類

    public abstract class Computer {
        /**
         * 產品的抽象方法,由具體的產品類去實現
         */
        public abstract void start();
    
    }

     

    1.3.2  具體實現類

    public class LenovoComputer extends Computer {
    
        @Override
        public void start() {
            System.out.println("lenovo computer run");
        }
    }
    public class MacComputer extends Computer {
        @Override
        public void start() {
            System.out.println("Mac computer run");
        }
    }
    public class HpComputer extends Computer {
        @Override
        public void start() {
            System.out.println("hp computer run");
        }
    }

    1.3.3  工廠類

    public class ComputerFactory {
        public static Computer createComputer(String type) {
            Computer computer = null;
            switch (type) {
                case "lenovo":
                    computer = new LenovoComputer();
                    break;
                case "hp":
                    computer = new HpComputer();
                    break;
                case "Mac":
                    computer = new MacComputer();
                    break;
                default:
                    break;
            }
            return computer;
        }
    }

    1.3.4  UML類圖

                              

    1.4 優缺點

     優點:

     1. 工廠類含有必要的判斷邏輯,可以決定在什麼時候創建哪一個產品類的實例,客戶端可以免除直接創建產品對象的責任,而僅僅“消費”產品;實現了對責任的分割,它提供了專門的工廠類用於創建對象。

     2. 客戶端無須知道所創建的具體產品類的類名,只需要知道具體產品類所對應的參數即可,對於一些複雜的類名,通過簡單工廠模式可以減少使用者的記憶量。

     3. 通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。

    缺點:

     1. 由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。

     2. 使用簡單工廠模式將會增加系統中類的個數,在一定程序上增加了系統的複雜度和理解難度。

     3. 系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,同樣破壞了“開閉原則”;在產品類型較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴展和維護。

     4. 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構

    1.5 適用場景

     1. 工廠類負責創建的對象比較少:由於創建的對象較少,不會造成工廠方法中的業務邏輯太過複雜。

     2. 客戶端只知道傳入工廠類的參數,對於如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數。

    1.6 模式應用

     1. JDK類庫中廣泛使用了簡單工廠模式,如工具類java.text.DateFormat,它用於格式化一個本地日期或者時間

    public final static DateFormat getDateInstance(); 
    public final static DateFormat getDateInstance(int style); 
    public final static DateFormat getDateInstance(int style,Locale locale);

     2. 獲取不同加密算法的密鑰生成器

    KeyGenerator keyGen=KeyGenerator.getInstance("DESede");

    1.7 開閉原則

     對於上面兩種簡單工廠模式的實現方法,如果我們要添加新的 parser,那勢必要改動到 RuleConfigParserFactory 的代碼,那這是不是違反開閉原則呢?

     實際上,如果不是需要頻繁地添加新的 parser,只是偶爾修改一下 RuleConfigParserFactory 代碼,稍微不符合開閉原則,也是完全可以接受的。

     儘管簡單工廠模式的代碼實現中,有多處 if 分支判斷邏輯,違背開閉原則,但權衡擴展性和可讀性,這樣的代碼實現在大多數情況下(比如,不需要頻繁地添加 parser,也沒有太多的 parser)是沒有問題的

     

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

    【其他文章推薦】

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

    ※新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

  • 03 . 二進制部署kubernetes1.18.4

    03 . 二進制部署kubernetes1.18.4

    簡介

    目前生產部署kubernetes集群主要兩種方式

    kubeadm

    Kubeadm是一個K8s部署工具,提供kubeadm init和kubeadm join,用於快速部署Kubernetes集群。

    二進制包

    從github下載發行版的二進制包,手動部署每個組件,組成Kubernetes集群。

    Kubeadm降低部署門檻,但屏蔽了很多細節,遇到問題很難排查。如果想更容易可控,推薦使用二進制包部署Kubernetes集群,雖然手動部署麻煩點,期間可以學習很多工作原理,也利於後期維護。

    二進制部署K8s

    List
    CentOS7.3
    cni-plugins-linux-amd64-v0.8.6.tgz
    etcd-v3.4.9-linux-amd64.tar.gz
    kube-flannel.yml
    kubernetes-server-linux-amd64.tar.gz
    
    角色 IP 組件
    master 192.168.31.71 kube-apiserver,kube-controller-manager,kube-scheduler,etcd
    Node1 192.168.31.74 kube-apiserver,kube-controller-manager,kube-scheduler
    Node2 192.168.31.72 kubelet,kube-proxy,docker etcd

    初始化環境

    # 初始化
    init_security() {
    systemctl stop firewalld
    systemctl disable firewalld &>/dev/null
    setenforce 0
    sed -i '/^SELINUX=/ s/enforcing/disabled/'  /etc/selinux/config
    sed -i '/^GSSAPIAu/ s/yes/no/' /etc/ssh/sshd_config
    sed -i '/^#UseDNS/ {s/^#//;s/yes/no/}' /etc/ssh/sshd_config
    systemctl enable sshd crond &> /dev/null
    rpm -e postfix --nodeps
    echo -e "\033[32m [安全配置] ==> OK \033[0m"
    }
    init_security
    
    init_yumsource() {
    if [ ! -d /etc/yum.repos.d/backup ];then
        mkdir /etc/yum.repos.d/backup
    fi
    mv /etc/yum.repos.d/* /etc/yum.repos.d/backup 2>/dev/null
    if ! ping -c2 www.baidu.com &>/dev/null    
    then
        echo "您無法上外網,不能配置yum源"
        exit    
    fi
        curl -o /etc/yum.repos.d/163.repo http://mirrors.163.com/.help/CentOS7-Base-163.repo &>/dev/null
        curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo &>/dev/null
        yum clean all
        timedatectl set-timezone Asia/Shanghai
        echo "nameserver 114.114.114.114" > /etc/resolv.conf
        echo "nameserver 8.8.8.8" >> /etc/resolv.conf
        chattr +i /etc/resolv.conf
        yum -y install ntpdate
        ntpdate -b  ntp1.aliyun.com        # 對時很重要
        echo -e "\033[32m [YUM Source] ==> OK \033[0m"
    }
    init_yumsource
    
    # 關掉swap分區
    swapoff -a
    # 如果想永久關掉swap分區,打開如下文件註釋掉swap哪一行即可.
    sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab #永久
    
    # 配置主機名解析
    tail -3 /etc/hosts
    192.168.0.121 master
    192.168.0.123 node1
    192.168.0.124 node2
    
    # 將橋接的IPv4流量傳遞到iptables的鏈
    cat > /etc/sysctl.d/k8s.conf << EOF
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    EOF
    sysctl --system  # 生效
    
    
    # 升級內核(非必須,只是性能更好)
    wget https://cbs.centos.org/kojifiles/packages/kernel/4.9.220/37.el7/x86_64/kernel-4.9.220-37.el7.x86_64.rpm
      
    rpm -ivh kernel-4.9.220-37.el7.x86_64.rpm
    reboot
    
    

    部署etcd集群

    Etcd 是一個分佈式鍵值存儲系統,Kubernetes使用Etcd進行數據存儲,所以先準備一個Etcd數據庫,為解決Etcd單點故障,應採用集群方式部署,這裏使用3台組建集群,可容忍1台機器故障,當然,你也可以使用5台組建集群,可容忍2台機器故障。

    節點名稱 IP
    etcd-1 192.168.31.71
    etcd-2 192.168.31.72
    etcd-3 192.168.31.73

    注:為了節省機器,這裏與K8s節點機器復用。也可以獨立於k8s集群之外部署,只要apiserver能連接到就行。

    準備cfssl證書生成工具

    cfssl是一個開源的證書管理工具,使用json文件生成證書,相比openssl更方便使用。

    找任意一台服務器操作,這裏用Master節點。

    wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
    wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
    wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
    chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64
    mv cfssl_linux-amd64 /usr/local/bin/cfssl
    mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
    mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
    
    生成etcd證書
    創建工作目錄
    mkdir -p ~/TLS/{etcd,k8s}
    
    cd TLS/etcd
    
    自簽CA
    cat > ca-config.json << EOF
    {
      "signing": {
        "default": {
          "expiry": "87600h"
        },
        "profiles": {
          "www": {
             "expiry": "87600h",
             "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ]
          }
        }
      }
    }
    EOF
    
    cat > ca-csr.json << EOF
    {
        "CN": "etcd CA",
        "key": {
            "algo": "rsa",
            "size": 2048
        },
        "names": [
            {
                "C": "CN",
                "L": "Beijing",
                "ST": "Beijing"
            }
        ]
    }
    EOF
    
    生成證書
    cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
    
    ls *pem
    ca-key.pem  ca.pem
    
    使用自簽CA簽發etcd https證書

    創建證書申請文件

    cat > server-csr.json << EOF
    {
        "CN": "etcd",
        "hosts": [
        "192.168.0.121",
        "192.168.0.123",
        "192.168.0.124"
        ],
        "key": {
            "algo": "rsa",
            "size": 2048
        },
        "names": [
            {
                "C": "CN",
                "L": "BeiJing",
                "ST": "BeiJing"
            }
        ]
    }
    EOF
    

    注:上述文件hosts字段中IP為所有etcd節點的集群內部通信IP,一個都不能少!為了方便後期擴容可以多寫幾個預留的IP。

    生成證書
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server
    
    ls server*pem
    server-key.pem  server.pem
    
    下載etcd二進制文件
    wget https://github.com/etcd-io/etcd/releases/download/v3.4.9/etcd-v3.4.9-linux-amd64.tar.gz
    
    創建工作目錄並解壓二進制包
    mkdir /opt/etcd/{bin,cfg,ssl} -p
    tar zxvf etcd-v3.4.9-linux-amd64.tar.gz
    mv etcd-v3.4.9-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
    
    # 配置etcd
    cat /opt/etcd/cfg/etcd.conf 
    #[Member]
    ETCD_NAME="etcd-1"
    ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
    ETCD_LISTEN_PEER_URLS="https://192.168.0.121:2380"
    ETCD_LISTEN_CLIENT_URLS="https://192.168.0.121:2379"
    #[Clustering]
    ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.121:2380"
    ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.121:2379"
    ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
    ETCD_INITIAL_CLUSTER_STATE="new"
    
    # ETCD_NAME:節點名稱,集群中唯一
    # ETCD_DATA_DIR:數據目錄
    # ETCD_LISTEN_PEER_URLS:集群通信監聽地址
    # ETCD_LISTEN_CLIENT_URLS:客戶端訪問監聽地址
    # ETCD_INITIAL_ADVERTISE_PEER_URLS:集群通告地址
    # ETCD_ADVERTISE_CLIENT_URLS:客戶端通告地址
    # ETCD_INITIAL_CLUSTER:集群節點地址
    # ETCD_INITIAL_CLUSTER_TOKEN:集群Token
    # ETCD_INITIAL_CLUSTER_STATE:加入集群的當前狀態,new是新集群,existing表示加入已有集群
    
    systemd管理etcd
    cat > /usr/lib/systemd/system/etcd.service << EOF
    [Unit]
    Description=Etcd Server
    After=network.target
    After=network-online.target
    Wants=network-online.target
    [Service]
    Type=notify
    EnvironmentFile=/opt/etcd/cfg/etcd.conf
    ExecStart=/opt/etcd/bin/etcd \
    --cert-file=/opt/etcd/ssl/server.pem \
    --key-file=/opt/etcd/ssl/server-key.pem \
    --peer-cert-file=/opt/etcd/ssl/server.pem \
    --peer-key-file=/opt/etcd/ssl/server-key.pem \
    --trusted-ca-file=/opt/etcd/ssl/ca.pem \
    --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem \
    --logger=zap
    Restart=on-failure
    LimitNOFILE=65536
    [Install]
    WantedBy=multi-user.target
    EOF
    
    拷貝剛生成證書及生成的文件拷貝到節點2,節點3
    cp ~/TLS/etcd/ca*pem ~/TLS/etcd/server*pem /opt/etcd/ssl/
    
    scp -r /opt/etcd/ node1:/opt/
    scp -r /opt/etcd/ node2:/opt/
    scp /usr/lib/systemd/system/etcd.service node1:/usr/lib/systemd/system/
    scp /usr/lib/systemd/system/etcd.service node2:/usr/lib/systemd/system/
    
    修改節點2和節點3etcd.conf配置文件
    node-1
    cat /opt/etcd/cfg/etcd.conf 
    #[Member]
    ETCD_NAME="etcd-2"
    ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
    ETCD_LISTEN_PEER_URLS="https://192.168.0.123:2380"
    ETCD_LISTEN_CLIENT_URLS="https://192.168.0.123:2379"
    #[Clustering]
    ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.123:2380"
    ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.123:2379"
    ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
    ETCD_INITIAL_CLUSTER_STATE="new"
    
    
    # node-2
    #[Member]
    ETCD_NAME="etcd-3"
    ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
    ETCD_LISTEN_PEER_URLS="https://192.168.0.124:2380"
    ETCD_LISTEN_CLIENT_URLS="https://192.168.0.124:2379"
    #[Clustering]
    ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.0.124:2380"
    ETCD_ADVERTISE_CLIENT_URLS="https://192.168.0.124:2379"
    ETCD_INITIAL_CLUSTER="etcd-1=https://192.168.0.121:2380,etcd-2=https://192.168.0.123:2380,etcd-3=https://192.168.0.124:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
    ETCD_INITIAL_CLUSTER_STATE="new"
    
    # 啟動服務並設置開機自啟
    systemctl daemon-reload
    systemctl start etcd
    systemctl enable etcd
    
    驗證etcd集群狀態
     /opt/etcd/bin/etcdctl --cacert=/opt/etcd/ssl/ca.pem --cert=/opt/etcd/ssl/server.pem --key=/opt/etcd/ssl/server-key.pem --endpoints="https://192.168.0.121:2379,https://192.168.0.123:2379,https://192.168.0.124:2379" endpoint healthhttps://192.168.0.124:2379 is healthy: successfully committed proposal: took = 13.213712ms
    https://192.168.0.121:2379 is healthy: successfully committed proposal: took = 12.907787ms
    https://192.168.0.123:2379 is healthy: successfully committed proposal: took = 12.168703ms
            
    # 如果輸出上面信息,就說明集群部署成功。如果有問題第一步先看日誌:/var/log/message 或 journalctl -u etcd
    

    安裝docker

    下載安裝docker
    sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    yum -y install docker-ce-19.03.9-3.el7
    
    配置docker鏡像源
    mkdir /etc/docker
    cat > /etc/docker/daemon.json << EOF
    {
      "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]
    }
    EOF
    
    啟動並設置開機自啟
    systemctl daemon-reload
    systemctl start docker
    systemctl enable docker
    

    部署Master Node

    生成kube-apiserver證書

    1. 自簽證書頒發機構(CA)

    cat > ca-config.json << EOF
    {
      "signing": {
        "default": {
          "expiry": "87600h"
        },
        "profiles": {
          "kubernetes": {
             "expiry": "87600h",
             "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ]
          }
        }
      }
    }
    EOF
    cat > ca-csr.json << EOF
    {
        "CN": "kubernetes",
        "key": {
            "algo": "rsa",
            "size": 2048
        },
        "names": [
            {
                "C": "CN",
                "L": "Beijing",
                "ST": "Beijing",
                "O": "k8s",
                "OU": "System"
            }
        ]
    }
    EOF
    
    # 生成證書
    cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
    
    ls *pem
    ca-key.pem  ca.pem
    
    使用自簽CA簽發kube-apiserver https 證書
    cat /root/TLS/k8s/server-csr.json 
    {
        "CN": "kubernetes",
        "hosts": [
          "10.0.0.1",
          "127.0.0.1",
          "192.168.0.121",
          "192.168.0.123",
          "192.168.0.124",
          "192.168.0.125",
          "192.168.0.100",
          "kubernetes",
          "kubernetes.default",
          "kubernetes.default.svc",
          "kubernetes.default.svc.cluster",
          "kubernetes.default.svc.cluster.local"
        ],
        "key": {
            "algo": "rsa",
            "size": 2048
        },
        "names": [
            {
                "C": "CN",
                "L": "BeiJing",
                "ST": "BeiJing",
                "O": "k8s",
                "OU": "System"
            }
        ]
    }
    
    # 上述文件hosts字段中IP為所有Master/LB/VIP IP,一個都不能少!為了方便後期擴容可以多寫幾個預留的IP。
    
    # 生成證書
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare server
    
    ls server*pem
    server-key.pem  server.pem
    
    下載解壓二進制包
    wget https://dl.k8s.io/v1.18.4/kubernetes-server-linux-amd64.tar.gz
    mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs} 
    tar zxvf kubernetes-server-linux-amd64.tar.gz
    cd kubernetes/server/bin
    cp kube-apiserver kube-scheduler kube-controller-manager /opt/kubernetes/bin
    cp kubectl /usr/bin/
    
    部署kube-apiserver
    cat /opt/kubernetes/cfg/kube-apiserver.conf 
    KUBE_APISERVER_OPTS="--logtostderr=false \
    --v=2 \
    --log-dir=/opt/kubernetes/logs \
    --etcd-servers=https://192.168.0.121:2379,https://192.168.0.123:2379,https://192.168.0.124:2379 \
    --bind-address=192.168.0.121 \
    --secure-port=6443 \
    --advertise-address=192.168.0.121 \
    --allow-privileged=true \
    --service-cluster-ip-range=10.0.0.0/24 \
    --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
    --authorization-mode=RBAC,Node \
    --enable-bootstrap-token-auth=true \
    --token-auth-file=/opt/kubernetes/cfg/token.csv \
    --service-node-port-range=30000-32767 \
    --kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \
    --kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \
    --tls-cert-file=/opt/kubernetes/ssl/server.pem  \
    --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \
    --client-ca-file=/opt/kubernetes/ssl/ca.pem \
    --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \
    --etcd-cafile=/opt/etcd/ssl/ca.pem \
    --etcd-certfile=/opt/etcd/ssl/server.pem \
    --etcd-keyfile=/opt/etcd/ssl/server-key.pem \
    --audit-log-maxage=30 \
    --audit-log-maxbackup=3 \
    --audit-log-maxsize=100 \
    --audit-log-path=/opt/kubernetes/logs/k8s-audit.log"
    
    
    # –logtostderr:啟用日誌
    # —v:日誌等級
    # –log-dir:日誌目錄
    # –etcd-servers:etcd集群地址
    # –bind-address:監聽地址
    # –secure-port:https安全端口
    # –advertise-address:集群通告地址
    # –allow-privileged:啟用授權
    # –service-cluster-ip-range:Service虛擬IP地址段
    # –enable-admission-plugins:准入控制模塊
    # –authorization-mode:認證授權,啟用RBAC授權和節點自管理
    # –enable-bootstrap-token-auth:啟用TLS bootstrap機制
    # –token-auth-file:bootstrap token文件
    # –service-node-port-range:Service nodeport類型默認分配端口範圍
    # –kubelet-client-xxx:apiserver訪問kubelet客戶端證書
    # –tls-xxx-file:apiserver https證書
    # –etcd-xxxfile:連接Etcd集群證書
    # –audit-log-xxx:審計日誌
    
    拷貝剛生成證書
    cp ~/TLS/k8s/ca*pem ~/TLS/k8s/server*pem /opt/kubernetes/ssl/
    
    啟用TLS Bootstrapping機制

    TLS Bootstraping:Master apiserver啟用TLS認證后,Node節點kubelet和kube-proxy要與kube-apiserver進行通信,必須使用CA簽發的有效證書才可以,當Node節點很多時,這種客戶端證書頒發需要大量工作,同樣也會增加集群擴展複雜度。為了簡化流程,Kubernetes引入了TLS bootstraping機制來自動頒發客戶端證書,kubelet會以一個低權限用戶自動向apiserver申請證書,kubelet的證書由apiserver動態簽署。所以強烈建議在Node上使用這種方式,目前主要用於kubelet,kube-proxy還是由我們統一頒發一個證書

    TLS bootstraping 工作流程

    創建上述配置文件中token文件

    cat > /opt/kubernetes/cfg/token.csv << EOF
    c47ffb939f5ca36231d9e3121a252940,kubelet-bootstrap,10001,"system:node-bootstrapper"
    EOF
    
    # 格式:token,用戶名,UID,用戶組
    

    token也可自行生成替換

    head -c 16 /dev/urandom | od -An -t x | tr -d ' '
    
    systemd管理apiserver
    cat > /usr/lib/systemd/system/kube-apiserver.service << EOF
    [Unit]
    Description=Kubernetes API Server
    Documentation=https://github.com/kubernetes/kubernetes
    [Service]
    EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf
    ExecStart=/opt/kubernetes/bin/kube-apiserver \$KUBE_APISERVER_OPTS
    Restart=on-failure
    [Install]
    WantedBy=multi-user.target
    EOF
    
    啟動設置開機啟動
    systemctl daemon-reload
    systemctl start kube-apiserver
    systemctl enable kube-apiserver
    
    授權kubelet-bootstrap用戶允許請求證書
    kubectl create clusterrolebinding kubelet-bootstrap \
    --clusterrole=system:node-bootstrapper \
    --user=kubelet-bootstrap
    

    部署kube-controller-manager

    創建配置文件
    cat /opt/kubernetes/cfg/kube-controller-manager.conf 
    KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \
    --v=2 \
    --log-dir=/opt/kubernetes/logs \
    --leader-elect=true \
    --master=127.0.0.1:8080 \
    --bind-address=127.0.0.1 \
    --allocate-node-cidrs=true \
    --cluster-cidr=10.244.0.0/16 \
    --service-cluster-ip-range=10.0.0.0/24 \
    --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \
    --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \
    --root-ca-file=/opt/kubernetes/ssl/ca.pem \
    --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \
    --experimental-cluster-signing-duration=87600h0m0s"
    
    # –master:通過本地非安全本地端口8080連接apiserver。
    # –leader-elect:當該組件啟動多個時,自動選舉(HA)
    # –cluster-signing-cert-file/–cluster-signing-key-file:自動為kubelet頒發證書的CA,與apiserver保持一致
    
    systemd管理controller-manager
    cat > /usr/lib/systemd/system/kube-controller-manager.service << EOF
    [Unit]
    Description=Kubernetes Controller Manager
    Documentation=https://github.com/kubernetes/kubernetes
    [Service]
    EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf
    ExecStart=/opt/kubernetes/bin/kube-controller-manager \$KUBE_CONTROLLER_MANAGER_OPTS
    Restart=on-failure
    [Install]
    WantedBy=multi-user.target
    EOF
    
    啟動設置開機啟動
    systemctl daemon-reload
    systemctl start kube-controller-manager
    systemctl enable kube-controller-manager
    

    部署kube-scheduler

    創建配置文件
    cat /opt/kubernetes/cfg/kube-scheduler.conf 
    KUBE_SCHEDULER_OPTS="--logtostderr=false --v=2 --log-dir=/opt/kubernetes/logs --leader-elect --master=127.0.0.1:8080 --bind-address=127.0.0.1"
    
    # –master:通過本地非安全本地端口8080連接apiserver。
    # –leader-elect:當該組件啟動多個時,自動選舉(HA)
    
    systemd管理scheduler
    cat > /usr/lib/systemd/system/kube-scheduler.service << EOF
    [Unit]
    Description=Kubernetes Scheduler
    Documentation=https://github.com/kubernetes/kubernetes
    [Service]
    EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf
    ExecStart=/opt/kubernetes/bin/kube-scheduler \$KUBE_SCHEDULER_OPTS
    Restart=on-failure
    [Install]
    WantedBy=multi-user.target
    EOF
    
    啟動並設置開機自啟動
    systemctl daemon-reload
    systemctl start kube-scheduler
    systemctl enable kube-scheduler
    
    查看集群狀態
    # 所有組件都已經啟動成功,通過kubectl工具查看當前集群組件狀態:
    kubectl get cs
    NAME                 STATUS    MESSAGE             ERROR
    scheduler            Healthy   ok                  
    controller-manager   Healthy   ok                  
    etcd-2               Healthy   {"health":"true"}   
    etcd-1               Healthy   {"health":"true"}   
    etcd-0               Healthy   {"health":"true"} 
    

    部署worker node

    下面還是在Master Node上操作,即同時作為Worker Node

    創建工作目錄並拷貝二進制文件

    在所有worker node創建工作目錄

    mkdir -p /opt/kubernetes/{bin,cfg,ssl,logs} 
    

    從master節點拷貝

    cd kubernetes/server/bin
    cp kubelet kube-proxy /opt/kubernetes/bin   # 本地拷貝,註釋這裏操作還是master節點,
    

    部署kubelet

    創建配置文件
    cat /opt/kubernetes/cfg/kubelet.conf 
    KUBELET_OPTS="--logtostderr=false \
    --v=2 \
    --log-dir=/opt/kubernetes/logs \
    --hostname-override=master \
    --network-plugin=cni \
    --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
    --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
    --config=/opt/kubernetes/cfg/kubelet-config.yml \
    --cert-dir=/opt/kubernetes/ssl \
    --pod-infra-container-image=lizhenliang/pause-amd64:3.0"
    
    
    # –hostname-override:显示名稱,集群中唯一
    # –network-plugin:啟用CNI
    # –kubeconfig:空路徑,會自動生成,後面用於連接apiserver
    # –bootstrap-kubeconfig:首次啟動向apiserver申請證書
    # –config:配置參數文件
    # –cert-dir:kubelet證書生成目錄
    # –pod-infra-container-image:管理Pod網絡容器的鏡像
    
    配置參數文件
    cat > /opt/kubernetes/cfg/kubelet-config.yml << EOF
    kind: KubeletConfiguration
    apiVersion: kubelet.config.k8s.io/v1beta1
    address: 0.0.0.0
    port: 10250
    readOnlyPort: 10255
    cgroupDriver: cgroupfs
    clusterDNS:
    - 10.0.0.2
    clusterDomain: cluster.local 
    failSwapOn: false
    authentication:
      anonymous:
        enabled: false
      webhook:
        cacheTTL: 2m0s
        enabled: true
      x509:
        clientCAFile: /opt/kubernetes/ssl/ca.pem 
    authorization:
      mode: Webhook
      webhook:
        cacheAuthorizedTTL: 5m0s
        cacheUnauthorizedTTL: 30s
    evictionHard:
      imagefs.available: 15%
      memory.available: 100Mi
      nodefs.available: 10%
      nodefs.inodesFree: 5%
    maxOpenFiles: 1000000
    maxPods: 110
    EOF
    
    生成bootstrap.kubeconfig文件
    KUBE_APISERVER="https://192.168.0.121:6443" # apiserver IP:PORT
    
    TOKEN="c47ffb939f5ca36231d9e3121a252940" # 與token.csv里保持一致
    
    # 生成 kubelet bootstrap kubeconfig 配置文件
    kubectl config set-cluster kubernetes \
      --certificate-authority=/opt/kubernetes/ssl/ca.pem \
      --embed-certs=true \
      --server=${KUBE_APISERVER} \
      --kubeconfig=bootstrap.kubeconfig
      
      
    kubectl config set-credentials "kubelet-bootstrap" \
      --token=${TOKEN} \
      --kubeconfig=bootstrap.kubeconfig
      
      
    kubectl config set-context default \
      --cluster=kubernetes \
      --user="kubelet-bootstrap" \
      --kubeconfig=bootstrap.kubeconfig
      
      
    kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
    
    # 拷貝到配置文件路徑
    cp bootstrap.kubeconfig /opt/kubernetes/cfg
    
    systemd管理kubelet
    cat > /usr/lib/systemd/system/kubelet.service << EOF
    [Unit]
    Description=Kubernetes Kubelet
    After=docker.service
    [Service]
    EnvironmentFile=/opt/kubernetes/cfg/kubelet.conf
    ExecStart=/opt/kubernetes/bin/kubelet \$KUBELET_OPTS
    Restart=on-failure
    LimitNOFILE=65536
    [Install]
    WantedBy=multi-user.target
    EOF
    
    啟動並設置開機自啟
    systemctl daemon-reload
    systemctl start kubelet
    systemctl enable kubelet
    
    批准kubelet證書並加入集群
    # 查看kubelet證書請求
    kubectl get csr
    NAME                                                   AGE    SIGNERNAME                                    REQUESTOR           CONDITION
    node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A   6m3s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending
    
    # 批准申請
    kubectl certificate approve node-csr-uCEGPOIiDdlLODKts8J658HrFq9CZ--K6M4G7bjhk8A
    
    # 查看節點
    kubectl get node
    NAME     STATUS   ROLES    AGE    VERSION
    master   Ready    <none>   123m   v1.18.4
    
    # 由於網絡插件還沒有部署,節點會沒有準備就緒 NotReady
    

    部署kube-proxy

    創建配置文件
    cat > /opt/kubernetes/cfg/kube-proxy.conf << EOF
    KUBE_PROXY_OPTS="--logtostderr=false \\
    --v=2 \\
    --log-dir=/opt/kubernetes/logs \\
    --config=/opt/kubernetes/cfg/kube-proxy-config.yml"
    EOF
    
    創建參數文件
    cat > /opt/kubernetes/cfg/kube-proxy-config.yml << EOF
    kind: KubeProxyConfiguration
    apiVersion: kubeproxy.config.k8s.io/v1alpha1
    bindAddress: 0.0.0.0
    metricsBindAddress: 0.0.0.0:10249
    clientConnection:
      kubeconfig: /opt/kubernetes/cfg/kube-proxy.kubeconfig
    hostnameOverride: k8s-master
    clusterCIDR: 10.0.0.0/24
    EOF
    
    生成kube-proxy.kubeconfig文件

    生成kube-proxy證書

    # 切換工作目錄
    cd TLS/k8s
    
    # 創建證書請求文件
    cat > kube-proxy-csr.json << EOF
    {
      "CN": "system:kube-proxy",
      "hosts": [],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [
        {
          "C": "CN",
          "L": "BeiJing",
          "ST": "BeiJing",
          "O": "k8s",
          "OU": "System"
        }
      ]
    }
    EOF
    
    # 生成證書
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
    
    ls kube-proxy*pem
    kube-proxy-key.pem  kube-proxy.pem
    
    生成kubeconfig文件
    KUBE_APISERVER="https://192.168.0.121:6443"
    
    kubectl config set-cluster kubernetes \
      --certificate-authority=/opt/kubernetes/ssl/ca.pem \
      --embed-certs=true \
      --server=${KUBE_APISERVER} \
      --kubeconfig=kube-proxy.kubeconfig
      
    kubectl config set-credentials kube-proxy \
      --client-certificate=./kube-proxy.pem \
      --client-key=./kube-proxy-key.pem \
      --embed-certs=true \
      --kubeconfig=kube-proxy.kubeconfig
      
    kubectl config set-context default \
      --cluster=kubernetes \
      --user=kube-proxy \
      --kubeconfig=kube-proxy.kubeconfig
      
    kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
    
    # 拷貝配置文件到指定路徑
    cp kube-proxy.kubeconfig /opt/kubernetes/cfg/
    
    systemd管理kube-proxy
    cat > /usr/lib/systemd/system/kube-proxy.service << EOF
    [Unit]
    Description=Kubernetes Proxy
    After=network.target
    [Service]
    EnvironmentFile=/opt/kubernetes/cfg/kube-proxy.conf
    ExecStart=/opt/kubernetes/bin/kube-proxy \$KUBE_PROXY_OPTS
    Restart=on-failure
    LimitNOFILE=65536
    [Install]
    WantedBy=multi-user.target
    EOF
    
    啟動並設置開機啟動
    systemctl daemon-reload
    systemctl start kube-proxy
    systemctl enable kube-proxy
    

    部署CNI網絡

    準備二進制文件
    wget https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz
      
    # 解壓二進制文件並移動到默認工作目錄
    mkdir /opt/cni/bin
    tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz -C /opt/cni/bin
    
    # 部署cni網絡
    wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    sed -i -r "s#quay.io/coreos/flannel:.*-amd64#lizhenliang/flannel:v0.12.0-amd64#g" kube-flannel.yml
    
    # 默認鏡像地址無法訪問,修改為docker hub鏡像倉庫。
    kubectl apply -f kube-flannel.yml
    
    kubectl get pods -n kube-system
    NAME                          READY   STATUS    RESTARTS   AGE
    kube-flannel-ds-amd64-2pc95   1/1     Running   0          72s
    
    kubectl get node
    NAME         STATUS   ROLES    AGE   VERSION
    master   Ready    <none>   41m   v1.18.4
    # 部署好網絡插件,Node準備就緒
    
    授權apiserver訪問kubelet
    cat > apiserver-to-kubelet-rbac.yaml << EOF
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      annotations:
        rbac.authorization.kubernetes.io/autoupdate: "true"
      labels:
        kubernetes.io/bootstrapping: rbac-defaults
      name: system:kube-apiserver-to-kubelet
    rules:
      - apiGroups:
          - ""
        resources:
          - nodes/proxy
          - nodes/stats
          - nodes/log
          - nodes/spec
          - nodes/metrics
          - pods/log
        verbs:
          - "*"
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: system:kube-apiserver
      namespace: ""
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: system:kube-apiserver-to-kubelet
    subjects:
      - apiGroup: rbac.authorization.k8s.io
        kind: User
        name: kubernetes
    EOF
    
    kubectl apply -f apiserver-to-kubelet-rbac.yaml
    
    新增加Worker Node

    拷貝已部署好的Node相關文件到新節點

    scp -r /opt/kubernetes/ node1:/opt/
    scp -r /usr/lib/systemd/system/{kubelet,kube-proxy}.service node1:/usr/lib/systemd/system
    
    scp -r /opt/cni/ node1:/opt/
    scp /opt/kubernetes/ssl/ca.pem node1:/opt/kubernetes/ssl
    
    刪除kubelet證書和kubeconfig文件
    rm -f /opt/kubernetes/cfg/kubelet.kubeconfig 
    rm -f /opt/kubernetes/ssl/kubelet*
    
    # 這幾個文件是證書申請審批后自動生成的,每個Node不同,必須刪除重新生成。
    
    修改主機名並設置開機自啟動
    vi /opt/kubernetes/cfg/kubelet.conf
    --hostname-override=node1
    
    vi /opt/kubernetes/cfg/kube-proxy-config.yml
    hostnameOverride: node1
    
    # 啟動並設置開機啟動
    systemctl daemon-reload
    systemctl start kubelet
    systemctl enable kubelet
    systemctl start kube-proxy
    systemctl enable kube-proxy
    
    再master上批准Node kubelet證書申請
    kubectl get csr
    NAME                                                   AGE   SIGNERNAME                                    REQUESTOR           CONDITION
    node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro   89s   kubernetes.io/kube-apiserver-client-kubelet   kubelet-bootstrap   Pending
    
    kubectl certificate approve node-csr-4zTjsaVSrhuyhIGqsefxzVoZDCNKei-aE2jyTP81Uro
    
    kubectl get node
    NAME     STATUS   ROLES    AGE    VERSION
    master   Ready    <none>   138m   v1.18.4
    node1    Ready    <none>   120m   v1.18.4
    node2    Ready    <none>   112m   v1.18.4
    

    部署Dashboard

    下載dashboard.yaml文件
    wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
    
    修改yaml配置文件使其端口暴露外部訪問
    wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
      
    # 默認Dashboard只能集群內部訪問,修改Service為NodePort類型,暴露到外部:
    
    vi recommended.yaml
    kind: Service
    apiVersion: v1
    metadata:
      labels:
        k8s-app: kubernetes-dashboard
      name: kubernetes-dashboard
      namespace: kubernetes-dashboard
    spec:
      ports:
        - port: 443
          targetPort: 8443
          nodePort: 30001  # 修改這裏
      type: NodePort   # 修改這裏
      selector:
        k8s-app: kubernetes-dashboard
    
    kubectl apply -f recommended.yaml
    
    kubectl get pods,svc -n kubernetes-dashboard
    NAME                                             READY   STATUS    RESTARTS   AGE
    pod/dashboard-metrics-scraper-694557449d-69x7g   1/1     Running   0          111m
    pod/kubernetes-dashboard-9774cc786-kwgkt         1/1     Running   0          111m
    
    NAME                                TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
    service/dashboard-metrics-scraper   ClusterIP   10.0.0.3     <none>        8000/TCP        111m
    service/kubernetes-dashboard        NodePort    10.0.0.122   <none>        443:30001/TCP   111m
    
    創建service account並綁定默認cluster-admin管理員集群角色
    kubectl create serviceaccount dashboard-admin -n kube-system
    kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
    kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
    
    # 接下來訪問https://node ip:30001
    # 然後將上面過濾出來的token複製上面即可訪問dashboard
    
    # 我們可以部署個Nginx測試下集群可用性
    kubectl run --generator=run-pod/v1 nginx-test2 --image=daocloud.io/library/nginx --port=80 --replicas=1
    
    kubectl get pods -o wide
    kubectl get pods -o wide
    NAME          READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
    nginx-test2   1/1     Running   0          89m   10.244.2.2   node2   <none>           <none>
    
    # 我們去相應的節點訪問指定IP即可訪問
    [root@node1 ~]# curl -I -s  10.244.2.2 |grep 200
    HTTP/1.1 200 OK
    

    部署CoreDNS

    CoreDNS用於集群內部Service名稱解析

    kubectl apply -f coredns.yaml 
    serviceaccount/coredns created
    clusterrole.rbac.authorization.k8s.io/system:coredns created
    clusterrolebinding.rbac.authorization.k8s.io/system:coredns 
    configmap/coredns created
    deployment.apps/coredns created
    
    DNS解析測試
    kubectl run -it --rm dns-test --image=busybox:1.28.4 sh
    If you don't see a command prompt, try pressing enter.
    / # nslookup kubernetes
    Server:    10.0.0.2
    Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
    
    Name:      kubernetes
    Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
    

    此篇文章借鑒於公眾號DevOps技術棧 ,作者阿良

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

    【其他文章推薦】

    ※新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

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

  • jvm入門及理解(六)——垃圾回收與算法

    jvm入門及理解(六)——垃圾回收與算法

    一、jvm垃圾回收要做的事情

    • 哪些內存需要回收
    • 什麼時候回收
    • 怎麼回收

    二、如何判斷對象已經死亡,或者說確定為垃圾

    1. 引用計數法:

      給對象中添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。這也就是需要回收的對象,簡單地說,即一個對象如果沒有任何與之關聯的引用,即他們的引用計數都為 0,則說明對象不太可能再被用到,那麼這個對象就是可回收的對象。

      引用計數法是對象記錄自己被多少程序引用,引用計數為零的對象將被清除。

      計數器表示的是有多少程序引用了這個對象(被引用數)。計數器是無符號的整數。

    2. 根搜索算法:

        通過一系列成為GC roots的點作為起點,向下搜索,當一個對象到任何GC Roots時沒有引用鏈相連,則說明對象已經死亡。

        如果在GC roots和一個對象之間沒有可達路徑(引用鏈),則稱該對象是不可達的。要注意的是,不可達對象不等價於可回收對象,不可達對象變為可回收對象至少要經過兩次標記 過程。兩次標記后仍然是可回收對象,則將面臨回收。

        jvm會將以下的對象定義為GC Roots:

     

    • Java虛擬機棧中引用的對象:比如方法裏面定義這種局部變量 User user= new User();
    • 方法區中的靜態屬性引用的對象:比如 private static User user = new User();
    • 常量引用的對象:比如 private static final  User user = new User();
    • 本地方法棧(JNI)中引用的對象

    三、垃圾回收算法

    1. 標記清除算法(Mark-Sweep)

        是最基礎的垃圾回收算法,分為兩個階段,標註和清除。標記階段標記出所有需要回收的對象,清除階段回收被標記的對象所佔用的空間。首先從根開始將可能被引用的對象用遞歸的方式進行標記,然後將沒有標記到的對象作為垃圾進行回收。

     

     

    從圖中我們就可以發現,該算法最大的問題是內存碎片化嚴重,後續可能發生大對象不能找到可利用空間的問題。

     

     

      2. 複製算法(copying)

       為了解決 Mark-Sweep 算法內存碎片化的缺陷而被提出的算法。按內存容量將內存劃分為等大小 的兩塊。每次只使用其中一塊,當這一塊內存滿后將尚存活的對象複製到另一塊上去,把已使用 的內存清掉,如圖:

     

     

     

     

     

     這種算法雖然實現簡單,內存效率高,不易產生碎片,但是最大的問題是可用內存被壓縮到了原 本的一半。且存活對象增多的話,Copying 算法的效率會大大降低。

      

      3.標記整理算法(Mark-Compact)

       結合了以上兩個算法,為了避免缺陷而提出。標記階段和 Mark-Sweep 算法相同,標記后不是清 理對象,而是將存活對象移向內存的一端。然後清除端邊界外的對象。如圖:

     

     

     

    四、分代收集算法

      介紹:分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不同生命周期將內存 劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代(Young Generation)。老生代的特點是每次垃圾回收時只有少量對象需要被回收,新生代的特點是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的算法。

    1. 新生代與複製算法

        目前大部分 JVM 的 GC 對於新生代都採取 Copying 算法,因為新生代中每次垃圾回收都要 回收大部分對象,即要複製的操作比較少,但通常並不是按照 1:1 來劃分新生代。一般將新生代 劃分為一塊較大的 Eden 空間和兩個較小的 Surviror 空間(From Space, To Space),每次使用 Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另 一塊 Survivor 空間中。

      

     

     

     假設from space為s0,to space 為s1

    算法過程:

    1. Eden+S0可分配新生對象;
    2. 對Eden+S0進行垃圾收集,存活對象複製到S1。清理Eden+S0。一次新生代GC結束。
    3. Eden+S1可分配新生對象;
    4. 對Eden+S1進行垃圾收集,存活對象複製到S0。清理Eden+S1。二次新生代GC結束。
    5. 循環1。

      2.老年代與標記整理算法

    老年代因為每次只回收少量對象,因而採用 Mark-Compact 算法。

    1. JAVA 虛擬機提到過的處於方法區的永生代(Permanet Generation),它用來存儲 class 類, 常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。

    2. 對象的內存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目 前存放對象的那一塊),少數情況會直接分配到老生代。

    3. 當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 后,Eden Space 和 From Space 區的存活對象會被挪到 To Space,然後將 Eden Space 和 From Space 進行清理。

    4. 如果 To Space 無法足夠存儲某個對象,則將這個對象存儲到老生代。

    5. 在進行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反覆循環。

    6. 當對象在 Survivor 區躲過一次 GC 后,其年齡就會+1。默認情況下年齡到達 15 的對象會被 移到老生代中。

     

    五、分區收集算法

    分區算法則將整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的 好處是可以控制一次回收多少個小區間 , 根據目標停頓時間, 每次合理地回收若干個小區間(而不是 整個堆), 從而減少一次 GC 所產生的停頓。

     

    六、java中的四種引用

    • 強引用

    在 Java 中最常見的就是強引用,把一個對象賦給一個引用變量,這個引用變量就是一個強引 用。當一個對象被強引用變量引用時,它處於可達狀態,它是不可能被垃圾回收機制回收的,即 使該對象以後永遠都不會被用到 JVM 也不會回收。因此強引用是造成 Java 內存泄漏的主要原因之一,直接new的對象就是強引用的。

    • 軟引用

    軟引用需要用 SoftReference 類來實現,對於只有軟引用的對象來說,當系統內存足夠時它 不會被回收,當系統內存空間不足時它會被回收。軟引用通常用在對內存敏感的程序中。

    • 弱引用

    弱引用需要用 WeakReference 類來實現,它比軟引用的生存期更短,對於只有弱引用的對象 來說,只要垃圾回收機制一運行,不管 JVM 的內存空間是否足夠,總會回收該對象佔用的內存。

    • 虛引用

    虛引用需要 PhantomReference 類來實現,它不能單獨使用,必須和引用隊列聯合使用。虛 引用的主要作用是跟蹤對象被垃圾回收的狀態。

     

    七、GC 垃圾收集器

    Java 堆內存被劃分為新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收算法; 年老代主要使用標記-整理垃圾回收算法,因此 java 虛擬中針對新生代和年老代分別提供了多種不 同的垃圾收集器,JDK1.6 中 Sun HotSpot 虛擬機的垃圾收集器如下:

     

    新生代:

    1.  Serial 垃圾收集器(單線程、複製算法)

    Serial(英文連續)是最基本垃圾收集器,使用複製算法,曾經是JDK1.3.1 之前新生代唯一的垃圾 收集器。Serial 是一個單線程的收集器,它不但只會使用一個 CPU 或一條線程去完成垃圾收集工 作,並且在進行垃圾收集的同時,必須暫停其他所有的工作線程,直到垃圾收集結束。 Serial 垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對於限 定單個 CPU 環境來說,沒有線程交互的開銷,可以獲得最高的單線程垃圾收集效率,因此 Serial 垃圾收集器依然是 java 虛擬機運行在 Client 模式下默認的新生代垃圾收集器。

       2.ParNew 垃圾收集器(Serial+多線程)

    ParNew 垃圾收集器其實是 Serial 收集器的多線程版本,也使用複製算法,除了使用多線程進行垃 圾收集之外,其餘的行為和 Serial 收集器完全一樣,ParNew 垃圾收集器在垃圾收集過程中同樣也 要暫停所有其他的工作線程。 ParNew 收集器默認開啟和 CPU 數目相同的線程數,可以通過-XX:ParallelGCThreads 參數來限 制垃圾收集器的線程數。ParNew雖然是除了多線程外和Serial 收集器幾乎完全一樣,但是ParNew垃圾收集器是很多 java 虛擬機運行在 Server 模式下新生代的默認垃圾收集器。

      3.Parallel Scavenge 收集器(多線程複製算法、高效)

    Parallel Scavenge 收集器也是一個新生代垃圾收集器,同樣使用複製算法,也是一個多線程的垃 圾收集器,它重點關注的是程序達到一個可控制的吞吐量(Thoughput,CPU 用於運行用戶代碼 的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)), 高吞吐量可以最高效率地利用 CPU 時間,儘快地完成程序的運算任務,主要適用於在後台運算而 不需要太多交互的任務。自適應調節策略也是 ParallelScavenge 收集器與 ParNew 收集器的一個 重要區別。

     

    老年代:

      4.Serial Old 收集器(單線程標記整理算法 )

    Serial Old 是 Serial 垃圾收集器年老代版本,它同樣是個單線程的收集器,使用標記-整理算法, 這個收集器也主要是運行在 Client 默認的 java 虛擬機默認的年老代垃圾收集器。 在 Server 模式下,主要有兩個用途:

        1. 在 JDK1.5 之前版本中與新生代的 Parallel Scavenge 收集器搭配使用。

        2. 作為年老代中使用 CMS 收集器的後備垃圾收集方案。

    新生代 Parallel Scavenge 收集器與 ParNew 收集器工作原理類似,都是多線程的收集器,都使 用的是複製算法,在垃圾收集過程中都需要暫停所有的工作線程。

      

      5.Parallel Old 收集器(多線程標記整理算法)

    Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多線程的標記-整理算法,在 JDK1.6 才開始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old 正是為了在年老代同樣提供吞 吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。

     

      6.Parallel Old 收集器(多線程標記整理算法)

    Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多線程的標記-整理算法,在 JDK1.6 才開始提供。 在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old 正是為了在年老代同樣提供吞 吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。

      

      7.CMS 收集器(多線程標記清除算法)

    Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾 回收停頓時間,和其他年老代使用標記-整理算法不同,它使用多線程的標記-清除算法。 最短的垃圾收集停頓時間可以為交互比較高的程序提高用戶體驗。

     

     

      8.G1 收集器

    Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器,G1 收 集器兩個最突出的改進是:

        1. 基於標記-整理算法,不產生內存碎片。

        2. 可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。 G1 收集器避免全區域垃圾收集,它把堆內存劃分為大小固定的幾個獨立區域,並且跟蹤這些區域 的垃圾收集進度,同時在後台維護一個優先級列表,每次根據所允許的收集時間,優先回收垃圾 最多的區域。區域劃分和優先級區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收 集效率。

     

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

  • 【Spring】@Transactional 閑聊

    菜瓜:上次的AOP理論知識看完收穫挺多的,雖然有一個自定義註解的demo,但還是覺得差點東西

    水稻:我也覺得沒有跟一遍源碼還是差點意思,這次結合@Transactional註解深入源碼看一下

    菜瓜:事務註解,這個平時用的挺多的

    水稻:是嗎?來看看你的基礎咋樣

    1. 要保證一個方法中多個數據庫操作的原子性,要共用一個數據庫連接,但是coding時我們不用显示傳遞連接對象,這是咋弄的?
    2. 如果一個方法裏面只有查詢操作,是否不用開啟事務?
    3. 如何解決非事務方法調用本地事務方法失效的?
    4. 註解常用的傳播屬性,你知道他們的區別嗎

    菜瓜:雖然沒看過源碼,我大膽猜測一下

    1. 隱式傳遞連接對象可以將其封裝到線程中,一般一次請求操作都是在一個線程中完成。使用ThreadLocal將連接和線程綁定
    2. 查詢操作也得看業務場景,如果多次查詢相同的數據要避免不可重複讀問題,可開啟只讀事務 (readOnly = true)
    3. 結合AOP的知識,這裏其實要解決調用事務方法的對象不是代理對象的問題。用代理對象調本地事務方法即可(注入自己)
      • /**
         * @author QuCheng on 2020/6/24.
         */
        @Service
        public class ItemServiceImpl implements ItemService {
        
            @Resource
            private IcbcItemMapper itemMapper;
        
            @Resource
            private ItemService itemService;
        
            @Override
            public void changeNameById(Long itemId) {
                // changeItemById(itemId);
                itemService.changeItemById(itemId);
            }
        
            @Transactional(rollbackFor = RuntimeException.class)
            @Override
            public void changeItemById(Long itemId) {
                itemMapper.updateNameById(itemId, "name4");
                int a = 10 / 0;
                itemMapper.updatePriceById(itemId, 100L);
            }
        }
    4. 傳播屬性這個沒了解過啊,數據庫事務裏面么得這個概念

    水稻:可以啊,平時的代碼沒白寫

    菜瓜:coding這種事情,easy啦!

    水稻:這就飄了?來看這個問題

    • 如果我想在A事務方法中調用B事務方法,B方法如果回滾了,不能影響A事務繼續執行,但是A事務如果執行出問題了,B也要回滾,怎麼弄?

    菜瓜:。。。這不就是大事務嵌套小事務嘛。。。我不會

    水稻:不扯了,來看源碼吧,這個問題等解釋了傳播屬性你就知道了

    • 上回我們說到,@Transactional是AOP的典型應用,bean被實例化之後要創建代理(參考自定義註解),就少不了切面類Advisor對象。那麼它是誰,它在哪,它在干什麼?
    • 回到夢開始的地方,事務功能開啟的註解@EnableTransactionManagement
      • 沒錯,它肯定會有一個Import註解引入TransactionManagementConfigurationSelector類,它又引入了切面類
      • public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
         
           @Override
           protected String[] selectImports(AdviceMode adviceMode) {
              switch (adviceMode) {
                 case PROXY:
                    return new String[] {AutoProxyRegistrar.class.getName(),
                          // 看這裏
                          ProxyTransactionManagementConfiguration.class.getName()};
                 case ASPECTJ:
                    return new String[] {determineTransactionAspectClass()};
                 default:
                    return null;
              }
           }
        。。。
        
        }
        
        
        @Configuration
        public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
        
           @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
           @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
           public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
              BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
              advisor.setTransactionAttributeSource(transactionAttributeSource());
              advisor.setAdvice(transactionInterceptor());
              if (this.enableTx != null) {
                 advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
              }
              return advisor;
           }
        
           @Bean
           @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
           public TransactionAttributeSource transactionAttributeSource() {
              return new AnnotationTransactionAttributeSource();
           }
        
           @Bean
           @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
           public TransactionInterceptor transactionInterceptor() {
        // 增強 TransactionInterceptor interceptor
        = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } } 
      • 切面類對象設置了事務的掃描器,也set了增強類TransactionInterceptor
      • public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
        。。。
        @Override
            @Nullable
            public Object invoke(MethodInvocation invocation) throws Throwable {
                // Work out the target class: may be {@code null}.
                // The TransactionAttributeSource should be passed the target class
                // as well as the method, which may be from an interface.
                Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        
                // Adapt to TransactionAspectSupport's invokeWithinTransaction...
                return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
            }
        }
        
        public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
          
        。。。  
        @Nullable
        protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
              final InvocationCallback invocation) throws Throwable {
           。。。
        // ①創建事務,數據庫連接處理也在這 TransactionInfo txInfo
        = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal = null; try {
        // 調用目標方法 retVal
        = invocation.proceedWithInvocation(); } catch (Throwable ex) {
        // 異常後事務處理 completeTransactionAfterThrowing(txInfo, ex);
        throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } 。。。 } 

    菜瓜:懂,接下來的代碼邏輯就是在增強類TransactionInterceptor的invoke方法里

    水稻:對

    • 先看數據庫連接的處理 – 驗證ThreadLocal
    • protected void doBegin(Object transaction, TransactionDefinition definition) {
         。。。
      // 如果連接是新的,就進行綁定
      if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(this.obtainDataSource(), txObject.getConnectionHolder()); } 。。。 } class TransactionSynchronizationManager public static void bindResource(Object key, Object value) throws IllegalStateException { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); Assert.notNull(value, "Value must not be null"); Map<Object, Object> map = resources.get(); // set ThreadLocal Map if none found if (map == null) { map = new HashMap<>(); resources.set(map); } Object oldValue = map.put(actualKey, value); // Transparently suppress a ResourceHolder that was marked as void... if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) { oldValue = null; } if (oldValue != null) { throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } if (logger.isTraceEnabled()) { logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]"); } }
    • 回過頭來看AB方法調用的回滾問題,直接給出答案(突然發現這個問題要講清楚篇幅會很大,就。。挺突然的。。挺突然der)
      • 在B方法上設置傳播屬性為NESTED即可,然後在A中catch住B的異常
      • 你肯定會問我不加NESTED去catch不行嗎?不行,非NESTED的方法拋出的異常是無法回滾的。
      • 不信你看
      • @Transactional(rollbackFor = RuntimeException.class)
            @Override
            public void changeNameById(Long itemId) {
                itemMapper.updateNameById(itemId, "A");
                try {
                    itemService.changeItemById(itemId);
        //            itemService.changeItemByIdNested(itemId);
                } catch (Exception e) {
                    System.out.println("我想繼續執行,不影響修改A");
                }
                itemMapper.updatePriceById(itemId, 1L);
            }
        
            @Transactional(rollbackFor = RuntimeException.class)
            @Override
            public void changeItemById(Long itemId) {
                itemMapper.updateNameById(itemId, "B+REQUIRED");
                itemMapper.updatePriceById(itemId, 10L);
                int a = 10 / 0;
            }
        
            @Transactional(rollbackFor = RuntimeException.class, propagation = Propagation.NESTED)
            @Override
            public void changeItemByIdNested(Long itemId) {
                itemMapper.updateNameById(itemId, "B+NESTED");
                itemMapper.updatePriceById(itemId, 100L);
                int a = 10 / 0;
            }
        
        
        -- 測試結果
        //①  itemService.changeItemById(itemId);  數據庫所有數據都不會改變
        我想繼續執行,不影響修改A
        org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
        
        // ② itemService.changeItemByIdNested(itemId); 第一個方法的修改會生效
        我想繼續執行,不影響修改A

    菜瓜:這就是傳播屬性NESTED?默認的是REQUIRED,還有一個常用的REQUIRES_NEW呢?

    水稻:搞清楚這個其實從數據庫連接入手其實就很清楚

    • REQUIRED修飾的方法和A使用同一個連接,A和B是掛一起的,誰回滾都會影響對方,且B方法的異常會被事務管理器標記為必須回滾
    • NESTED修飾的方法和A使用同一個連接,但是用到了數據庫的savePoint特性,它可以回滾到指定的點,如果是有回滾點的操作,拋出的異常可以被處理
    • REQUIRES_NEW修飾的方法和A使用的就不是一個連接了,回不回滾都不會影響對方,當然,要捕捉異常

    菜瓜:傳播屬性了解。回滾的問題還得再看看,篇幅很大是很複雜嗎?

    水稻:其實不複雜,就是要跟蹤源碼斷點調試。。。截圖搞來搞去,篇幅就很長,你自己去調的話其實很快

    菜瓜:那我下去康康

     

    總結

    • 這裏提到Transactional註解其實是為了鞏固AOP的,當然提到了一些注意點。譬如本地調用,譬如ThreadLocal的應用,還譬如傳播屬性
    • 傳播屬性其實用的少,但是聊起來就比較多了,可以聊事務的隔離級別,聊ACID的實現,聊MySQL的鎖

     

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

  • 三年增15萬個 北京2016年電動車搖號指標將占四成

    26日北京市人大常委會會議表示,北京將逐步提高北京市小客車搖號指標中電動車的數量,未來三年分別新增3萬、6萬、6萬輛。到2016年電動車指標將占所有搖號指標的40%。   2011年起,北京開始實施小客車總量調控政策,將每年新增指標控制在24萬輛。2014年,這一數位進一步壓縮到15萬輛。目前,北京市機動車保有量已經超過500萬輛。北京市提出,將在每年15萬輛小客車新增指標內逐步提高電動車的數量。

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

    【其他文章推薦】

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

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

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

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

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

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

  • 成大氫能電動機車 預估續航力可達 160 公里

    成大氫能電動機車 預估續航力可達 160 公里

      台灣機車密度是世界之最,造成嚴重的污染問題,電動、綠能機車的開發一直是學者致力的目標。國立成功大學近日整合關鍵技術,研發出以新型氫能燃料與鋰電池做為混合動力的新電動機車「飛馬一號」,首航測試即輕易完成 80 多公里路程,估計實際續航力可達 160 公里。   由成大航太工程學系賴維祥教授帶領的研究團隊,一年半前在經濟部能源局「能源學界科技專案」補助下,開始與銓寶工業、福桑聯合企業進行產學合作,共同研發代號「飛馬一號」氫能電動機車。   成大指出,「飛馬一號」是從銓寶公司提供的電動車改裝而來,除了原有的鋰電池外,研究團隊另外在車後加裝了 3,000 瓦級的燃料電池,以及 2 支容積達 6.8 公升、能儲存 300 大氣壓的儲氫瓶,原理大致與汽車大廠 TOYOTA 年底即將量產的氫氣電動車相同。   「飛馬一號」的動力系統是以新高壓儲存技術填充氫氣,可達傳統方式的 4 倍密度,利用氫能直接啟動燃料電池,再讓燃料電池把電傳給鋰電池,鋰電池的電力則驅動馬達運轉,此一模式可讓鋰電池在行進間同時充電,延長使用壽命。     (Source:)

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

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

    ※新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 步伐密集 易事特再建江蘇充電樁專案

    易事特今日發佈公告稱,與江蘇高淳經濟開發區開發總公司就新能源汽車充電樁設備研發製造、IDC整體機房研發製造、智慧微電網等項目投資達成一致意見並簽署協議書。易事特承諾新辦企業固定資產投資總額為6億元人民幣(約新臺幣30億),畝均稅收不低於20萬元(約新臺幣101萬),首期建築面積不低於30000平方米。高淳經濟開發區將供地約120畝,使用年限為50年。   易事特佈局充電樁可謂步伐密集。今年8月13日,易事特與馬鞍山經濟技術開發區管理委員會簽訂了《投資框架協議書》,擬在對方區域內投資建設資料中心、分散式發電設備與系統集成和新能源汽車充電站(樁)項目。

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

    ※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 中國政策放開電動車准入要求 小米或入局電動車

    中國政策放開電動車准入要求 小米或入局電動車

    11月26日,國家發改委網站掛出《新建純電動乘用車生企業投資項目和生產準入管理的暫行規定(徵求意見稿)》(以下簡稱《規定》),計劃為純電動車生準入放開口子。   《規定》要求的三條基礎能力中,最核心的是:(申報企業)有3年以上純電動乘用車的研發基礎,具有專業研發團隊和整車正向研發能力,掌握整車控制系統、動力電池系統、整車整合和整車輕量化方面的核心技術以及相應的試驗驗證能力,擁有純電動乘用車自主智慧財產權和已授權的相關發明專利。另外還要求新建企業只能生產純電動轎車和純電動其他乘用車(包括增程式電動乘用車),插電式混合動力不在準入範圍內。   面對新一批電動車開放名單,包括小米、樂視與萬向等企業集團都展現出高度興趣,爭取成為入圍名單。其中小米的動向最受矚目。小米創辦人之一黎萬強早在10月底就悄悄飛往矽谷展開暖身動作,小米董事長雷軍更在拜會特斯拉後就對電動車念念不忘。事實上,小米電動車的諜照早就在網路上曝光,只是細節外界所知不多。不過,這次開放的限制很嚴,小米能否如願以償擠進電動車窄門,備受關注。   (照片來源: )

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 電動巴士車廠華德今年拚損益兩平 年底前登錄興櫃

    電動巴士車廠華德動能於 11 月 3 日與群益金鼎證券簽訂股票上市櫃輔導契約,目標今年底前登錄興櫃,華德動能董事長蔡易忠表示,今年電動巴士預計出貨 53 輛 ,市占全台第一大,今年力拚損益兩平。   華德為台灣第二家電動巴士掛牌企業,同時也是車王電子轉投資公司,車王電持有比例為 14.9%,華德動能去年合併營收 5,782 萬元,虧損 6,000 萬元,每股虧損 4.73 元。今年上半年營收躍升至 2.28 億元,並順利轉盈,稅後純益 1,300 萬元,每股稅後純益 0.65 元。   蔡易忠表示,公司主要業務為電動巴士、電動商用貨車以及儲能系統三大主軸。華德電動巴士使用的電池為稀土鋰釔電池,不同於 F-立凱電為採用磷酸鐵鋰材料,蔡易忠表示,只要電池材料能讓電動巴士行駛安全、性能佳,都會持續找尋新的電池材料,不會局限在單一材料。   蔡易忠表示,華德積極布局開發海外市場,未來擬將底盤系統或關鍵動力組件出口至東南亞、中國、美國及歐洲地區,近期先出口至馬來西亞。目前公司也積極規劃建廠計畫,擬以租賃方式承租廠房,計劃由年產 80 輛擴大至 500 輛,目標 2015 年第 3 季加入營運。

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

    【其他文章推薦】

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

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

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

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

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

  • 晶華攜手 BMW 設電動車充電站 其他廠牌插頭符合也適用

    晶華攜手 BMW 設電動車充電站 其他廠牌插頭符合也適用

      台北晶華酒店與 BMW 總代理汎德公司首度合作,在酒店停車場設置「BMW i 公共充電站」,為全台第一個五星級飯店「BMW i 公共充電站」,也是 BMW 計畫在各通路設置「BMW i 公共充電站」的首站。   擁有 BMW i3 或 BMW i8 車款車主,不論是否到晶華用餐或是住宿均可免費充電,但要付停車費,第一個小時 300 元,第二個小時 200 元。此外,其他廠牌的電動車,只要插頭符合,同樣適用。晶華表示,晶華麗晶酒店集團旗下晶英酒店包括蘭城晶英及太魯閣晶英酒店正計畫裝設中。   BMW 總代理汎德表示,BMW i3 及 BMW i8 於今年第 2 季起全球開始量產上市,BMW i3 是純電動車,一台售價新台幣 239 萬元起,台灣目前已有 40 至 50 張訂單,BMW i8 是油電混合車,一台售價新台幣 989 萬元,目前約有 30 張訂單,已在陸續交車中。   汎德表示,電動車是未來趨勢,汎德已在全台 BMW 經銷商及服務廠設置充電站,目前已設 28 座,年底會再增 2 座,合計為 30 座,BMW 車主都提供免費充電服務。另與晶華酒店合作,於台北晶華設置 2 座,現已正式啟用。此外,汎德還會與裕隆、台達電洽談合作事宜。   (照片提供:台北晶華酒店)

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

    【其他文章推薦】

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

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

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

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

    ※新北清潔公司,居家、辦公、裝潢細清專業服務