Redis系列(五):數據結構List雙向鏈表源碼解析和API實現

1.介紹

Redis中List是通過ListNode構造的雙向鏈表。

特點:

1.雙端:獲取某個結點的前驅和後繼結點都是O(1)

2.無環:表頭的prev指針和表尾的next指針都指向NULL,對鏈表的訪問都是以NULL為終點

3.帶表頭指針和表尾指針:獲取表頭和表尾的複雜度都是O(1)

4.帶鏈表長度計數器:len屬性記錄,獲取鏈表長度O(1)

5.多態:鏈表結點使用void*指針來保存結點的值,並且可以通過鏈表結構的三個函數為結點值設置類型特定函數,所以鏈表可以保存各種不同類型的值

雙向鏈表詳解:https://www.cnblogs.com/vic-tory/p/13140779.html

中文網:http://redis.cn/commands.html#list

2.源碼解析

// listNode 雙端鏈表節點
typedef struct listNode {

    // 前置節點
    struct listNode *prev;

    // 後置節點
    struct listNode *next;

    // 節點的值
    void *value;

} listNode;

 

// list 雙端鏈表
typedef struct list { // 在c語言中,用結構體的方式來模擬對象是一種常見的手法

    // 表頭節點
    listNode *head;

    // 表尾節點
    listNode *tail;

    // 節點值複製函數
    void *(*dup)(void *ptr);

    // 節點值釋放函數
    void(*free)(void *ptr);

    // 節點值對比函數
    int(*match)(void *ptr, void *key);

    // 鏈表所包含的節點數量
    unsigned long len;

} list;

 

/* 作為宏實現的函數 */
//獲取長度
#define listLength(l) ((l)->len)
//獲取頭節點
#define listFirst(l) ((l)->head)
//獲取尾結點
#define listLast(l) ((l)->tail)
//獲取前一個結點
#define listPrevNode(n) ((n)->prev)
//獲取后一個結點
#define listNextNode(n) ((n)->next)
//獲取結點的值 是一個void類型指針
#define listNodeValue(n) ((n)->value)

/* 下面3個函數主要用來設置list結構中3個函數指針,參數m為method的意思 */
#define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m))

/* 下面3個函數主要用來獲取list結構的單個函數指針 */
#define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)

3.API實現

listCreate函數:創建一個不包含任何結點的新鏈表

/*
 * listCreate 創建一個新的鏈表
 *
 * 創建成功返回鏈表,失敗返回 NULL 。
 *
 * T = O(1)
 */
list *listCreate(void)
{
    struct list *list;

    // 分配內存
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;//內存分配失敗則返回NULL

    // 初始化屬性
    list->head = list->tail = NULL;//空鏈表
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;

    return list;
}

listAddNodeHead函數:將一個包含給定值的新結點添加到給定鏈表的表頭

/*
 * listAddNodeHead 將一個包含有給定值指針 value 的新節點添加到鏈表的表頭
 *
 * 如果為新節點分配內存出錯,那麼不執行任何動作,僅返回 NULL
 *
 * 如果執行成功,返回傳入的鏈表指針
 *
 * T = O(1)
 */
list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    // 為節點分配內存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指針
    node->value = value;

    // 添加節點到空鏈表
    if (list->len == 0) {
        list->head = list->tail = node;
        //該結點的前驅和後繼都為NULL
        node->prev = node->next = NULL;
    }
    else { // 添加節點到非空鏈表
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }

    // 更新鏈表節點數
    list->len++;

    return list;
}

listAddNodeTail函數:將一個包含給定值的新結點插入到給定鏈表的表尾

/*
 * listAddNodeTail 將一個包含有給定值指針 value 的新節點添加到鏈表的表尾
 *
 * 如果為新節點分配內存出錯,那麼不執行任何動作,僅返回 NULL
 *
 * 如果執行成功,返回傳入的鏈表指針
 *
 * T = O(1)
 */
list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    // 為新節點分配內存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指針
    node->value = value;

    // 目標鏈表為空
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    }//目標鏈非空
    else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }

    // 更新鏈表節點數
    list->len++;

    return list;
}

listInsertNode函數:將一個給定值的新結點插入到給定結點之前或者之後

/*
 * listInsertNode 創建一個包含值 value 的新節點,並將它插入到 old_node 的之前或之後
 *
 * 如果 after 為 0 ,將新節點插入到 old_node 之前。
 * 如果 after 為 1 ,將新節點插入到 old_node 之後。
 *
 * T = O(1)
 */
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;

    // 創建新節點
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值
    node->value = value;

    // 將新節點添加到給定節點之後
    if (after) {
        node->prev = old_node;
        node->next = old_node->next;
        // 給定節點是原表尾節點
        if (list->tail == old_node) {
            list->tail = node;
        }
    }
    // 將新節點添加到給定節點之前
    else {
        node->next = old_node;
        node->prev = old_node->prev;
        // 給定節點是原表頭節點
        if (list->head == old_node) {
            list->head = node;
        }
    }

    // 更新新節點的前置指針
    if (node->prev != NULL) {
        node->prev->next = node;
    }
    // 更新新節點的後置指針
    if (node->next != NULL) {
        node->next->prev = node;
    }

    // 更新鏈表節點數
    list->len++;

    return list;
}

listDelNode函數:從指定的list中刪除給定的結點

/*
 * listDelNode 從鏈表 list 中刪除給定節點 node
 *
 * 對節點私有值(private value of the node)的釋放工作由調用者進行。該函數一定會成功.
 *
 * T = O(1)
 */
void listDelNode(list *list, listNode *node)
{
    // 調整前置節點的指針
    if (node->prev)
        node->prev->next = node->next;
    else
        list->head = node->next;

    // 調整後置節點的指針
    if (node->next)
        node->next->prev = node->prev;
    else
        list->tail = node->prev;

    // 釋放值
    if (list->free) list->free(node->value);

    // 釋放節點
    zfree(node);

    // 鏈表數減一
    list->len--;
}

listRelease函數:釋放給定鏈表以及鏈表中所有結點

 

/*
 * listRelease 釋放整個鏈表,以及鏈表中所有節點, 這個函數不可能會失敗.
 *
 * T = O(N)
 */
void listRelease(list *list)
{
    unsigned long len;
    listNode *current, *next;

    // 指向頭指針
    current = list->head;
    // 遍歷整個鏈表
    len = list->len;
    while (len--) {
        next = current->next;

        // 如果有設置值釋放函數,那麼調用它
        if (list->free) list->free(current->value);

        // 釋放節點結構
        zfree(current);

        current = next;
    }

    // 釋放鏈表結構
    zfree(list);
}

 

該函數不僅釋放了表結點的內存還釋放了表結構的內存

 listGetIterator函數:為給定鏈表創建一個迭代器

在講這個函數之前,我們應該先看看鏈表迭代器的結構:

// listIter 雙端鏈表迭代器
typedef struct listIter {

    // 當前迭代到的節點
    listNode *next;

    // 迭代的方向
    int direction;

} listIter;

迭起器只有兩個重要的屬性:當前迭代到的結點,迭代的方向

下面再看看鏈表的迭代器創建函數

/*
 * listGetIterator 為給定鏈表創建一個迭代器,
 * 之後每次對這個迭代器調用 listNext 都返回被迭代到的鏈表節點,調用該函數不會失敗
 *
 * direction 參數決定了迭代器的迭代方向:
 *  AL_START_HEAD :從表頭向表尾迭代
 *  AL_START_TAIL :從表尾想表頭迭代
 *
 * T = O(1)
 */
listIter *listGetIterator(list *list, int direction)
{
    // 為迭代器分配內存
    listIter *iter;
    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;

    // 根據迭代方向,設置迭代器的起始節點
    if (direction == AL_START_HEAD)
        iter->next = list->head;
    else
        iter->next = list->tail;

    // 記錄迭代方向
    iter->direction = direction;

    return iter;
}

listReleaseIterator函數:釋放指定的迭代器

/*
 * listReleaseIterator 釋放迭代器
 *
 * T = O(1)
 */
void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

listRewind函數和listRewindTail函數:迭代器重新指向表頭或者表尾的函數

 

/*
 * 將迭代器的方向設置為 AL_START_HEAD,
 * 並將迭代指針重新指向表頭節點。
 *
 * T = O(1)
 */
void listRewind(list *list, listIter *li) {
    li->next = list->head;
    li->direction = AL_START_HEAD;
}

/*
 * 將迭代器的方向設置為 AL_START_TAIL,
 * 並將迭代指針重新指向表尾節點。
 *
 * T = O(1)
 */
void listRewindTail(list *list, listIter *li) {
    li->next = list->tail;
    li->direction = AL_START_TAIL;
}

listNext函數:返回當前迭代器指向的結點

 

/*
 * 返回迭代器當前所指向的節點。
 *
 * 刪除當前節點是允許的,但不能修改鏈表裡的其他節點。
 *
 * 函數要麼返回一個節點,要麼返回 NULL,常見的用法是:
 *
 * iter = listGetIterator(list,<direction>);
 * while ((node = listNext(iter)) != NULL) {
 *     doSomethingWith(listNodeValue(node));
 * }
 *
 * T = O(1)
 */
listNode *listNext(listIter *iter)
{
    listNode *current = iter->next;

    if (current != NULL) {
        // 根據方向選擇下一個節點
        if (iter->direction == AL_START_HEAD)
            // 保存下一個節點,防止當前節點被刪除而造成指針丟失
            iter->next = current->next;
        else
            // 保存下一個節點,防止當前節點被刪除而造成指針丟失
            iter->next = current->prev;
    }

    return current;
}

 

 

 

該函數保持了當前結點的下一個結點,避免了當前結點被刪除而迭代器無法繼續迭代的尷尬情況

 listDup函數:複製整個鏈表,返回副本

 

/*
 * 複製整個鏈表。
 *
 * 複製成功返回輸入鏈表的副本,
 * 如果因為內存不足而造成複製失敗,返回 NULL 。
 *
 * 如果鏈表有設置值複製函數 dup ,那麼對值的複製將使用複製函數進行,
 * 否則,新節點將和舊節點共享同一個指針。
 *
 * 無論複製是成功還是失敗,輸入節點都不會修改。
 *
 * T = O(N)
 */
list *listDup(list *orig)
{
    list *copy;//鏈表副本
    listIter *iter;//鏈表迭代器
    listNode *node;//鏈表結點

    // 創建新的空鏈表
    if ((copy = listCreate()) == NULL)
        return NULL;//創建空的鏈表失敗則返回NULL

    // 設置副本鏈表的節點值處理函數
    copy->dup = orig->dup;
    copy->free = orig->free;
    copy->match = orig->match;

    //獲取輸入鏈表的迭代器
    iter = listGetIterator(orig, AL_START_HEAD);
    
    //遍歷整個輸入鏈表進行複製
    while ((node = listNext(iter)) != NULL) {
        
        //副本結點值
        void *value;

        // 存在複製函數
        if (copy->dup) {
            
            //調用複製函數複製
            value = copy->dup(node->value);
         
            //複製結果為空,說明複製失敗
            if (value == NULL) {
                
                //複製失敗則釋放副本鏈表和迭代器,避免內存泄漏
                listRelease(copy);
                listReleaseIterator(iter);
            
                return NULL;
            }
        }
        //不存在複製函數 則直接指針指向
        else
            value = node->value;

        // 將節點添加到副本鏈表 
        if (listAddNodeTail(copy, value) == NULL) {
                
            //如果不能成功添加,則釋放副本鏈表和迭代器,避免內存泄漏
            listRelease(copy);
            listReleaseIterator(iter);
        
            return NULL;
        }
    }

    // 釋放迭代器
    listReleaseIterator(iter);

    // 返回副本
    return copy;
}

 

如果複製失敗則要注意釋放副本鏈表和迭代器,避免內存泄漏

 listSearchKey函數:查找list中值和key匹配的結點

 

/*
 * 查找鏈表 list 中值和 key 匹配的節點。
 *
 * 對比操作由鏈表的 match 函數負責進行,
 * 如果沒有設置 match 函數,
 * 那麼直接通過對比值的指針來決定是否匹配。
 *
 * 如果匹配成功,那麼第一個匹配的節點會被返回。
 * 如果沒有匹配任何節點,那麼返回 NULL 。
 *
 * T = O(N)
 */
listNode *listSearchKey(list *list, void *key)
{
    listIter *iter;//鏈表迭代器
    listNode *node;//鏈表結點

    //獲得鏈表迭代器
    iter = listGetIterator(list, AL_START_HEAD);

    //遍歷整個鏈表查詢
    while ((node = listNext(iter)) != NULL) {

        //存在比較函數
        if (list->match) {

            //利用比較函數進行比較
            if (list->match(node->value, key)) {

                //返回目標結點之前釋放迭代器空間,避免內存泄漏
                listReleaseIterator(iter);

                return node;
            }
        }
        //不存在比較函數
        else {
            //直接比較
            if (key == node->value) {

                //返回目標結點之前釋放迭代器空間,避免內存泄漏
                listReleaseIterator(iter);
                // 找到
                return node;
            }
        }
    }

    //返回目標結點之前釋放迭代器空間,避免內存泄漏
    listReleaseIterator(iter);

    // 未找到
    return NULL;
}

listIndex函數:返回鏈表在給定索引上的值

 

/*
 * 返回鏈表在給定索引上的值。
 *
 * 索引以 0 為起始,也可以是負數, -1 表示鏈表最後一個節點,諸如此類。
 *
 * 如果索引超出範圍(out of range),返回 NULL 。
 *
 * T = O(N)
 */
listNode *listIndex(list *list, long index) {
    
    listNode *n;//鏈表結點

    
    /* n不用設置成NULL的原因:
    如果索引超出範圍,
    那肯定是找到表頭或者表尾沒有找到,
    表頭的前驅和表尾的後繼都是NULL,
    所以這裏n不用設置為NULL,直接設置也可以*/
    
    // 如果索引為負數,從表尾開始查找
    if (index < 0) {
        
        //變成正數,方便索引
        index = (-index) - 1;
    
        //從尾部開始找
        n = list->tail;
        
        //尋找 因為從尾部開始找,所以是前驅
        while (index-- && n) n = n->prev;
        
    }
    
    // 如果索引為正數,從表頭開始查找
    else {
        
        //從頭部開始找
        n = list->head;
    
        //尋找 因為從頭部開始找,所以是後繼
        while (index-- && n) n = n->next;
    }

    return n;
}

listRotate函數:取出鏈表的表尾結點放到表頭,成為新的表頭結點

/*
 * 取出鏈表的表尾節點,並將它移動到表頭,成為新的表頭節點。
 *
 * T = O(1)
 */
void listRotate(list *list) {
    
    //表尾結點
    listNode *tail = list->tail;

    //如果鏈表中只有一個元素,那麼表頭就是表尾,可以直接返回
    if (listLength(list) <= 1) return;

    // 重新設置表尾節點
    list->tail = tail->prev;
    list->tail->next = NULL;

    // 插入到表頭
    list->head->prev = tail;
    tail->prev = NULL;
    tail->next = list->head;
    list->head = tail;
}

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

聚甘新

003.OpenShift網絡

一 OpenShift網絡實現

1.1 軟件定義網絡(SDN)


默認情況下,Docker網絡使用僅使用主機虛機網橋bridge,主機內的所有容器都連接至該網橋。連接到此橋的所有容器都可以彼此通信,但不能與不同主機上的容器通信。通常,這種通信使用端口映射來處理,其中容器端口綁定到主機上的端口,所有通信都通過物理主機上的端口路由。

當有大量主機和容器時,使用此模式,需要手動管理所有端口綁定非常不現實。

為了支持跨集群的容器之間的通信,OpenShift容器平台使用了軟件定義的網絡(SDN)方法。軟件定義的網絡是一種網絡模型,它通過幾個網絡層的抽象來管理網絡服務。SDN將處理流量的軟件(稱為控制平面)和路由流量的底層機制(稱為數據平面)解耦。SDN支持控制平面和數據平面之間的通信。

在OpenShift Container Platform 3.9中(之後簡稱OCP),管理員可以為pod網絡配置三個SDN插件:

  1. ovs-subnet:默認插件,子網提供了一個flat pod網絡,其中每個pod可以與其他pod和service通信。
  2. ovs-multitenant:該為pod和服務提供了額外的隔離層。當使用此插件時,每個project接收一個惟一的虛擬網絡ID (VNID),該ID標識來自屬於該project的pod的流量。通過使用VNID,來自不同project的pod不能與其他project的pod和service通信。
  3. ovs-networkpolicy:此插件允許管理員使用NetworkPolicy對象定義自己的隔離策略。


cluster network由OpenShift SDN建立和維護,它使用Open vSwitch創建overlay網絡,master節點不能通過集群網絡訪問容器,除非master同時也為node節點。

注意:VNID為0的project可以與所有其他pod通信,在OpenShift容器平台中,默認項目的VNID為0。

1.2 Kubernetes SDN Pod




在默認的OpenShift容器平台安裝中,每個pod都有一個惟一的IP地址。pod中的所有容器都對外表現在相同的主機上。給每個pod提供自己的IP地址意味着,在端口分配、網絡、DNS、負載平衡、應用程序配置和遷移方面,pod被視為物理主機或虛擬機的獨立節點(僅從網絡層面看待)。

Kubernetes提供了service的概念,在任何OpenShift應用程序中,service都是必不可少的資源。service充當一個或多個pod前的負載平衡器。該service提供一個固定的IP地址,並且允許與pod通信,而不必跟蹤單獨的pod IP地址。



大多數實際應用程序都不是作為單個pod運行的。它們需要水平伸縮,這樣應用程序就可以在許多pod上運行,以滿足不斷增長的用戶需求。在OpenShift集群中,pod不斷地在集群中的節點之間創建和銷毀。每次創建pod時,它們都會獲得一個不同的IP地址。一個service提供一個單獨的、惟一的IP地址供其他pod使用,而不依賴於pod運行的節點,因此一個pod不必一定需要發現另一個pod的IP地址。客戶端通過service的請求在不同pod之間實現負載均衡。

1.3 Kubernetes SDN Service


service背後運行的一組pod由OpenShift容器平台自動管理。每個service都被分配了一個唯一的IP地址供客戶端連接。這個IP地址也來自OpenShift SDN,它與pod的內部網絡不同,也只在集群中可見。每個與selector匹配的pod都作為endpoint添加到service資源中。當創建和銷毀pods時,service後面的endpoint將自動更新。

service yaml語法:

  1 - apiVersion: v1
  2   kind: Service			#聲明資源類型
  3   metadata:
  4     labels:
  5       app: hello-openshift
  6       name: hello-openshift	#服務的唯一名稱
  7   spec:
  8     ports:,
  9     - name: 8080-tcp
 10       port: 8080		#服務對外公開的端口客戶機連接到服務端口
 11       protocol: TCP
 12       targetPort: 8080		#targetPort屬性必須匹配pod容器定義中的containerPort,服務將數據包轉發到pod中定義的目標端口。
 13     selector:			#該服務使用selector屬性查找要轉發數據包的pod。目標pod的元數據中需要有匹配的標籤。如果服務發現多個具有匹配標籤的pod,它將在它們之間實現負載
 14       app: hello-openshift
 15       deploymentconfig: hello-openshift


1.4 service對外暴露


默認情況下,pod和service IP地址不能從OpenShift集群外部訪問。對於需要從OpenShift集群外部訪問服務的應用程序,可以通過以下三種方式。

HostPort/HostNetwork:在這種方法中,client可以通過主機上的網絡端口直接訪問集群中的應用程序pod。應用程序pod中的端口被綁定到運行該pod的主機上的端口。這種方法在集群中運行大量pod時,存在端口衝突的風險。

NodePort:這是一種較老的基於Kubernetes的方法,通過綁定到node主機上的可用端口,將service公開給外部客戶端,然後node主機代理到service IP地址的連接。使用oc edit svc命令編輯服務屬性,指定NodePort的類型,併為NodePort屬性提供端口值。OpenShift然後通過node主機的公共IP地址和nodePort中設置的端口值代理到服務的連接。這種方法支持非http通信。

OpenShift routes:OpenShift中的推薦方式。它使用唯一的URL公開服務。使用oc expose命令公開用於外部訪問的服務,或者從OpenShift web控制台公開服務。在這種方法中,目前只支持HTTP、HTTPS、TLS whit SNI和WebSockets。

附圖:显示了NodePort服務如何允許外部訪問Kubernetes服務。



service nodeport yaml語法:

  1 apiVersion: v1
  2 kind: Service
  3 metadata:
  4 ...
  5 spec:
  6   ports:
  7   - name: 3306-tcp
  8     port: 3306
  9     protocol: TCP
 10     targetPort: 3306	#pod目標端口,即需要和pod定義的端口匹配
 11     nodePort: 30306	#OpenShift集群中主機上的端口,暴露給外部客戶端
 12   selector:
 13     app: mysqldb
 14     deploymentconfig: mysqldb
 15     sessionAffinity: None
 16   type: NodePort	#服務的類型,如NodePort
 17 ...



OpenShift將服務綁定到服務定義的nodePort屬性中定義的值,併為集群中所有node(包括master)上的流量打開該端口。外部客戶端可以連接到node端口上的任何節點的公共IP地址來訪問服務。請求會在服務後面的各個pod之間實現輪詢的負載平衡。

OpenShift route主要限於HTTP和HTTPS流量,但是節點端口可以處理非HTTP流量,當設置好公開的端口后,客戶機可以使用TCP或UDP的協議連接到該端口。

提示:缺省情況下,NodePort屬性的端口號限制在30000-32767之間,可通過在OpenShift主配置文件中配置範圍。

node port在集群中的所有node上都是打開的,包括master節點。如果沒有提供node端口值,OpenShift將自動在配置範圍內分配一個隨機端口

1.5 pod訪問外部網絡


pod可以使用其主機的地址與外部網絡通信。只要主機能夠解析pod需要到達的服務器,pod就可以使用網絡地址轉換(network address translation, NAT)機制與目標服務器通信。

二 OpenShift SDN練習

2.1 前置準備


[student@workstation ~]$ lab install-prepare setup

[student@workstation ~]$ cd /home/student/do280-ansible

[student@workstation do280-ansible]$ ./install.sh

提示:以上準備為部署一個正確的OpenShift平台。

2.2 本練習準備


[student@workstation ~]$ lab openshift-network setup #準備本實驗環境

2.3 創建應用


[student@workstation ~]$ oc login -u developer -p redhat https://master.lab.example.com

[student@workstation ~]$ oc new-project network-test #創建project

[student@workstation ~]$ oc new-app –name=hello -i php:7.0 http://registry.lab.example.com/scaling

[student@workstation ~]$ oc get pods

NAME READY STATUS RESTARTS AGE

hello-1-build 1/1 Running 0 8s

2.4 擴展應用


[student@workstation ~]$ oc scale –replicas=2 dc hello

[student@workstation ~]$ oc get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE

hello-1-kszfh 1/1 Running 0 11m 10.128.0.21 node1.lab.example.com

hello-1-q7wk2 1/1 Running 0 11m 10.129.0.37 node2.lab.example.com

2.5 測試訪問


[student@workstation ~]$ curl http://10.128.0.21:8080

curl: (7) Failed connect to 10.128.0.21:8080; Network is unreachable

[root@node1 ~]# curl http://10.128.0.21:8080

  1 <html>
  2  <head>
  3   <title>PHP Test</title>
  4  </head>
  5  <body>
  6  <br/> Server IP: 10.128.0.21
  7  </body>
  8 </html>
  9 [root@node1 ~]# curl http://10.129.0.37:8080
 10 <html>
 11  <head>
 12   <title>PHP Test</title>
 13  </head>
 14  <body>
 15  <br/> Server IP: 10.129.0.37
 16  </body>
 17 </html>



提示:默認情況下,pod的ip屬於內部,集群內部節點可以使用pod ip訪問,集群外部(如workstation)無法訪問。

[student@workstation ~]$ oc get svc hello

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

hello ClusterIP 172.30.253.212 <none> 8080/TCP 14m

[student@workstation ~]$ curl http://172.30.253.212:8080

curl: (7) Failed connect to 172.30.253.212:8080; Network is unreachable

[root@node1 ~]# curl http://172.30.253.212:8080 #驗證負載均衡

  1 <html>
  2  <head>
  3   <title>PHP Test</title>
  4  </head>
  5  <body>
  6  <br/> Server IP: 10.128.0.21
  7  </body>
  8 </html>
  9 [root@node1 ~]# curl http://172.30.253.212:8080		#驗證負載均衡
 10 <html>
 11  <head>
 12   <title>PHP Test</title>
 13  </head>
 14  <body>
 15  <br/> Server IP: 10.129.0.37
 16  </body>
 17 </html>



提示:默認情況下,cluster的ip屬於內部,集群內部節點可以使用cluster ip訪問,集群外部(如workstation)無法訪問。

2.6 檢查服務


[student@workstation ~]$ oc describe svc hello

Name: hello

Namespace: network-test

Labels: app=hello

Annotations: openshift.io/generated-by=OpenShiftNewApp

Selector: app=hello,deploymentconfig=hello

Type: ClusterIP

IP: 172.30.253.212

Port: 8080-tcp 8080/TCP

TargetPort: 8080/TCP

Endpoints: 10.128.0.21:8080,10.129.0.37:8080

Session Affinity: None

Events: <none>

解釋:

endpoint:显示請求路由到的pod IP地址列表。當pod有更新后,endpoint將自動更新。

Selector:OpenShift使用為pods定義的選擇器和標籤來使用給定的集群IP,以便實現應用的負載均衡。如上所示為OpenShift將此服務的請求路由到所有標記為app=hello和deploymentconfig=hello的pod。

2.7 檢查pod


[student@workstation ~]$ oc describe pod hello-1-kszfh

2.8 設置外部訪問


使用NodePort方式設置外部訪問。

[student@workstation ~]$ oc edit svc hello

  1 apiVersion: v1
  2 kind: Service
  3 metadata:
  4   annotations:
  5     openshift.io/generated-by: OpenShiftNewApp
  6   creationTimestamp: 2019-07-19T15:50:09Z
  7   labels:
  8     app: hello
  9   name: hello
 10   namespace: network-test
 11   resourceVersion: "24496"
 12   selfLink: /api/v1/namespaces/network-test/services/hello
 13   uid: e348e2a3-aa3c-11e9-b230-52540000fa0a
 14 spec:
 15   clusterIP: 172.30.253.212
 16   ports:
 17   - name: 8080-tcp
 18     port: 8080
 19     protocol: TCP
 20     targetPort: 8080
 21     nodePort: 30800
 22   selector:
 23     app: hello
 24     deploymentconfig: hello
 25   sessionAffinity: None
 26   type: NodePort
 27 status:



[student@workstation ~]$ oc describe svc hello

Name: hello

Namespace: network-test

Labels: app=hello

Annotations: openshift.io/generated-by=OpenShiftNewApp

Selector: app=hello,deploymentconfig=hello

Type: NodePort #驗證是否為NodePort

IP: 172.30.253.212

Port: 8080-tcp 8080/TCP

TargetPort: 8080/TCP

NodePort: 8080-tcp 30800/TCP

Endpoints: 10.128.0.21:8080,10.129.0.37:8080

Session Affinity: None

External Traffic Policy: Cluster

Events: <none>

2.9 驗證外部訪問


[student@workstation ~]$ curl http://node1.lab.example.com:30800

  1 <html>
  2  <head>
  3   <title>PHP Test</title>
  4  </head>
  5  <body>
  6  <br/> Server IP: 10.128.0.21
  7  </body>
  8 </html>



[student@workstation ~]$ curl http://node2.lab.example.com:30800

  1 <html>
  2  <head>
  3   <title>PHP Test</title>
  4  </head>
  5  <body>
  6  <br/> Server IP: 10.129.0.37
  7  </body>
  8 </html>


2.10 使用pod shell


[student@workstation ~]$ oc rsh hello-1-kszfh #使用pod的shell

sh-4.2$ curl http://services.lab.example.com

三 OpenShift router

3.1 OpenShift route概述


OpenShift service允許在OpenShift中的pod之間進行網絡訪問;

OpenShift routes允許從OpenShift外部對pods進行網絡訪問。



路由概念上是通過連接公網IP和DNS主機名訪問內網service IP。在實踐中,為了提高性能和減少延遲,OpenShift route通過OpenShift創建的網絡直接連接到pod,使用該服務只查找endpoint,service只是協助查詢Pod地址。

OpenShift 路由功能由router service提供,該服務在OpenShift實例中作為一個pod運行,可以像任何其他常規pod一樣伸縮和複製。router service基於開源軟件HAProxy實現。

OpenShift route配置的公共DNS主機名需要指向運行router的節點的公共IP地址。route pod與常規應用程序pod不同,它綁定到節點的公共IP地址,而不是內部pod網絡。這通常使用DNS通配符配置。

  1 - apiVersion: v1
  2   kind: Route				#聲明為route類型
  3   metadata:
  4     creationTimestamp: null
  5     labels:
  6       app: quoteapp
  7     name: quoteapp				#路由器名字
  8   spec:
  9     host: quoteapp.apps.lab.example.com	#與route關聯的FQDN,必須預先配置,以解析到OpenShift route pod運行的節點的IP地址
 10     port:
 11       targetPort: 8080-tcp
 12   to:					#一個對象,該對象聲明此route指向的資源類型(在本例中是OpenShift service),以及該資源的名稱(quoteapp)
 13     kind: Service
 14     name: quoteapp



提示:不同資源類型可以使用相同的名稱,如一個名為quoteapp的route可以指向一個名為quoteapp的service。

service通過selector與pod的label進行匹配,router通過name與service的name匹配。

3.2 創建route


創建route最簡單和推薦的方法是使用oc expose命令,將service資源名稱作為輸入參數。–name選項可用於控制route資源的名稱,–hostname選項可用於為route提供自定義主機名。

示例:

[user@demo ~]$ oc expose service quote \

–name quote –hostname=quoteapp.apps.lab.example.com

從模板或不帶–hostname參數的oc expose命令創建的路由,命名方式為:

<route-name>-<project-name>.<default-domain>

解釋

route-name:route的名稱,或原始資源的名稱;

project-name:包含資源的項目的名稱;

default-domain:該值是在OpenShift master上配置的,它對應於作為安裝OpenShift先決條件列出的通配符DNS域。

例如,在OpenShift集群中名為test的project中創建一條名為quote的路由,其中子域為apps.example.com,則FQDN為quote-test.apps.example.com

注意:承載通配符域的DNS服務器不知道任何route的主機名,它只將任何名稱解析為已配置的ip。只有OpenShift route知道route主機名,將每個主機都當作HTTP虛擬主機。無效的通配符域主機名,即不與任何route對應的主機名,將被OpenShift路由器阻塞。

通過向oc create提供JSON或YAML資源定義文件,也可以像其他OpenShift資源一樣創建route資源。

oc new-app命令在從容器映像、Dockerfiles或應用程序源代碼構建pod時不創建route資源。

oc new-app命令不知道pod是否打算從OpenShift實例外部訪問。當oc new-app命令從模板創建一組pod時,沒有什麼可以阻止模板將路由資源包含到應用程序中。

3.3 查找默認subdomain


默認路由子域是在OpenShift配置文件master-config.yaml中的routingConfig字段中定義,使用關鍵字subdomain。

routingConfig:

subdomain: apps.example.com

默認情況下,OpenShift HAProxy route綁定到主機端口80 (HTTP)和443 (HTTPS)。route必須放置在這些端口不使用的節點上。或者,可以通過設置ROUTER_SERVICE_HTTP_PORT和ROUTER_SERVICE_HTTPS_PORT環境變量來配置路由器監聽其他端口.

路由器支持以下協議:

  • HTTP


  • HTTPS with SNI
  • WebSockets
  • TLS with SNI

3.4 routing類型和選項


路由可以是安全的,也可以是非安全的。安全route提供了使用幾種類型的TLS方式來向客戶端提供證書的能力。不安全路由是最容易配置的,因為它們不需要密鑰或證書,但是安全路由會加密進出pod的流量。

在創建安全路由之前,需要生成TLS證書。

示例:如下步驟創建名為test.example.com的路由創建一個簡單的自簽名證書。

  • 使用openssl命令創建私鑰:


[user@demo ~]$ openssl genrsa -out example.key 2048

  • 使用生成的私鑰創建證書籤名請求(CSR):


[user@demo ~]$ openssl req -new -key example.key -out example.csr -subj “/C=US/ST=CA/L=Los Angeles/O=Example/OU=IT/CN=test.example.com”

  • 使用密鑰和CSR生成證書


[user@demo ~]$ openssl x509 -req -days 366 -in example.csr -signkey example.key -out example.crt

  • 當證書準備好時,創建一個edge-terminated的路由


[user@demo ~]$ oc create route edge –service=test \

–hostname=test.example.com \

–key=example.key –cert=example.crt

3.5 通配符子域


wildcard policy允許用戶定義domain中所有主機的route。route可以使用wildcardPolicy字段將wildcard policy指定為其配置的一部分。OpenShift路由器支持通配符路由,通過設置路由器部署配置中的ROUTER_ALLOW_WILDCARD_ROUTES環境變量為true,從而可將wildcardPolicy屬性設置為子域的任何route都由路由器提供服務。路由器根據route的通配符策略暴露相關的service。

示例:如下下示例表示對於三個不同的路由,a.lab.example.com、b.lab.example.com和c.lab.example.com,它們應該路由到一個名為test的OpenShift服務,可以使用通配符策略配置路由。

  • 將路由器作為集群管理用戶處理通配符路由


[user@demo ~]$ oc scale dc/router –replicas=0

[user@demo ~]$ oc set env dc/router ROUTER_ALLOW_WILDCARD_ROUTES=true

[user@demo ~]$ oc scale dc/router –replicas=1

  • 使用通配符策略創建新路由


[user@demo ~]$ oc expose svc test –wildcard-policy=Subdomain \

–hostname=’www.lab.example.com’

3.6 管理route


在master節點上,在default中查找router

[root@master]# oc project default

[root@master]# oc get pods

在master節點上,檢查路由器環境變量,以找到運行在pod中的HAProxy進程的連接參數

[root@master]# oc env pod router-1-32toa –list | tail -n 6

提示:當創建路由器時,STATS_PASSWORD變量中的密碼是隨機生成的。STATS_USERNAME和STATS_PORT變量有固定的默認值,但是它們都可以在路由器創建時更改。

在router運行的節點上,配置firewall-cmd以打開STATS_PORT變量指定的端口。

[root@node ~]# firewall-cmd –permanent –zone=public –add-port=1936

[root@node ~]# firewall-cmd –reload

打開web瀏覽器並訪問HAProxy statistics URL 為 http://nodeIP:STATS_PORT/。

在User Name字段中輸入STATS_USERNAME的值,在Password字段中輸入STATS_PASSWORD的值,然後單擊OK。則會显示的HAProxy metrics頁面。

四 創建Route練習

4.1 前置準備


準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

4.2 本練習準備


[student@workstation ~]$ lab secure-route setup #準備本實驗環境

4.3 創建應用


[student@workstation ~]$ oc login -u developer -p redhat https://master.lab.example.com

[student@workstation ~]$ oc new-project secure-route #創建project

[student@workstation ~]$ oc new-app –docker-image=registry.lab.example.com/openshift/hello-openshift –name=hello

[student@workstation ~]$ oc get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE

hello-1-wwgkr 1/1 Running 0 20s 10.129.0.38 node2.lab.example.com

4.4 創建TLS證書


[student@workstation ~]$ cd /home/student/DO280/labs/secure-route/ #使用環境中的腳本快速創建TLS自簽名證書

[student@workstation secure-route]$ ./create-cert.sh

4.5 創建route


[student@workstation secure-route]$ ll

-rw-r–r–. 1 student student 550 Aug 7 2018 commands.txt

-rwxr-xr-x. 1 student student 506 Jul 19 2018 create-cert.sh

-rw-rw-r–. 1 student student 1224 Jul 20 10:43 hello.apps.lab.example.com.crt

-rw-rw-r–. 1 student student 1017 Jul 20 10:43 hello.apps.lab.example.com.csr

-rw-rw-r–. 1 student student 1679 Jul 20 10:43 hello.apps.lab.example.com.key

[student@workstation secure-route]$ oc create route edge \

> –service=hello –hostname=hello.apps.lab.example.com \

> –key=hello.apps.lab.example.com.key \

> –cert=hello.apps.lab.example.com.crt

4.6 確認驗證


[student@workstation secure-route]$ oc get route

NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD

hello hello.apps.lab.example.com hello 8080-tcp edge None

[student@workstation secure-route]$ oc get route hello -o yaml #以yaml格式查看route

4.7 測試訪問


[student@workstation ~]$ curl http://hello.apps.lab.example.com #以http形式訪問會無法轉發至後端任何pod

  1 ……
  2       <h1>Application is not available</h1>
  3       <p>The application is currently not serving requests at this endpoint. It may not have been started or is still starting.</p>
  4 ……



[student@workstation ~]$ curl -k -vvv https://hello.apps.lab.example.com #以https形式訪問

  1 ……
  2 Hello OpenShift!
  3 * Connection #0 to host hello.apps.lab.example.com left intact
  4 ……


4.8 非安全形式訪問


由於加密的通信在路由器上終止,並且請求使用不安全的HTTP轉發到pods,所以可以使用pod IP地址通過普通HTTP訪問應用程序。為此,請使用oc get pods -o命令中指定的IP地址。

[student@workstation secure-route]$ oc get pod -o wide

NAME READY STATUS RESTARTS AGE IP NODE

hello-1-wwgkr 1/1 Running 0 21m 10.129.0.38 node2.lab.example.com

[root@node1 ~]# curl -vvv http://10.129.0.38:8080


五 OpenShift網絡綜合實驗

5.1 前置準備


準備完整的OpenShift集群,參考《003.OpenShift網絡》2.1。

5.2 本練習準備


[student@workstation ~]$ lab network-review setup

5.3 驗證所需資源


[student@workstation ~]$ oc login -u developer -p redhat \

https://master.lab.example.com

[student@workstation ~]$ oc get pod -o wide

NAME READY STATUS RESTARTS AGE IP NODE

hello-openshift-1-6ls8z 1/1 Running 0 2m 10.128.0.23 node1.lab.example.com

[student@workstation ~]$ oc get svc

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE

hello-openshift ClusterIP 172.30.124.237 <none> 8080/TCP,8888/TCP 2m

[student@workstation ~]$ oc get route

NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD

hello-openshift hello.apps.lab.example.com hello-opensift 8080-tcp None

5.4 測試訪問


[student@workstation ~]$ curl http://hello.apps.lab.example.com #測試http訪問

  1 ……
  2       <h1>Application is not available</h1>
  3       <p>The application is currently not serving requests at this endpoint. It may not have been started or is still starting.</p>
  4 ……

[root@node1 ~]# curl http://10.128.0.23:8080 #測試使用pod ip訪問

Hello OpenShift!

[root@node1 ~]# curl http://172.30.124.237:8080 #測試使用cluster ip訪問

curl: (7) Failed connect to 172.30.124.237:8080; Connection refused

5.5 TS cluster故障


[student@workstation ~]$ oc describe svc hello-openshift -n network-review



提示:由上可知,沒有endpoint,endpoint是使用selector對pod的label進行匹配。

[student@workstation ~]$ oc describe pod hello-openshift-1-6ls8z #查看pod詳情



故障點:由上可知,Selector的label不一致,則沒有標記為hello_openshift的pod能進行匹配。

[student@workstation ~]$ oc edit svc hello-openshift

  1 ……
  2   selector:
  3     app: hello-openshift
  4     deploymentconfig: hello-openshift
  5   sessionAffinity: None
  6 ……


5.6 測試訪問


[root@node1 ~]# curl http://10.128.0.23:8080 #測試使用pod ip訪問

Hello OpenShift!

[root@node1 ~]# curl http://172.30.124.237:8080 #再次測試

Hello OpenShift!

[student@workstation ~]$ curl http://hello.apps.lab.example.com #測試http訪問

  1 ……
  2       <h1>Application is not available</h1>
  3       <p>The application is currently not serving requests at this endpoint. It may not have been started or is still starting.</p>
  4 ……


5.7 TS route故障


[student@workstation ~]$ oc describe route hello-openshift



故障點:由上可知,此路由沒有endpoint。即對route的URL請求沒有後端endpoint進行響應。路由器查詢service的endpoint,並註冊有效的endpoint來實現負載平衡。同時發現service名稱中有一個拼寫錯誤,它應該是hello-openshift。

[student@workstation ~]$ oc edit route hello-openshift

  1 ……
  2 spec:
  3   host: hello.apps.lab.example.com
  4   port:
  5     targetPort: 8080-tcp
  6   to:
  7     kind: Service
  8     name: hello-openshift
  9     weight: 100
 10   wildcardPolicy: None
 11 ……



[root@node1 ~]# curl http://hello.apps.lab.example.com #再次測試

Hello OpenShift!

5.8 確認驗證


[student@workstation ~]$ lab network-review grade #使用腳本判斷 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

聚甘新

年僅 28 歲就宣布從字節跳動退休?

這两天,互聯網熱議最大的一個話題除了阿里 P8 程序員找生活助理的事,另外一個就是 28 歲的郭宇宣布從字節跳動退休,稱選擇經營溫泉旅行,選擇成為一名職業作家。

我看到這個話題的時候,情不自禁地“嘖嘖”了兩聲,真特么酸了,28 歲就退休了,我已經 31 歲了,還在辛苦打拚的路上,除了要忙工作,還要高產似母豬地更文,然而,即便我這麼努力,還是沒能成為一名“職業作家”,退休更是遙遙無期。

郭大佬非常牛逼的一點在於,高考之後就開始敲代碼,上了大學之後依然敲代碼,大三就在支付寶干過,然後創業的一家公司被字節跳動收購,再然後嘛,就財務自由退休了——28 歲,重新定義了退休的年紀。

字節跳動這家公司發展的真的是巨快,明星產品今日頭條和抖音,真的是國民級應用。反正我父母都是這兩款產品的忠實用戶,我妹妹雖然不玩今日頭條,但抖音玩得那叫一個熱火朝天。

我自己是不玩抖音也不看今日頭條的,因為覺得這種短視頻,或者說亂七八糟的新聞熱點有點浪費生命的感覺,所以一直很抵觸。

當然了,我如果說我一次也沒玩過,有點聖人的感覺,我做不到。但每次無聊到刷上倆小時的抖音,我就會噁心到把這款軟件卸載掉。尤其是聽到那些無厘頭的狂笑,我感覺到娛樂在致死。

這不是抖音的問題,是我的問題,是人性的問題,抖音就抓住了人性的弱點,讓你沉浸其中,忘乎所以。

抨擊歸抨擊,但我不能忽視的事實是,字節跳動是真的牛逼,郭大佬是真的有錢了。

每個人都有自己人生,郭大佬有實力又有運氣,他過的是一種極致的人生。

我是 2014 年回的洛陽,一回來就跟着一個老闆做創業項目,依稀還記得當初他給我許下的承諾:三年後讓你在洛陽買房買車,五年後帶你走上人生巔峰。

2015 年,我買了房,靠的是我和老婆辛苦攢下的一些積蓄,還有父母義無反顧的支持。老闆也借給了我兩三萬,一年後我就還他了,所以在買房這個承諾上,他有幫助,但遠非承諾中的那樣。

2016 年,我買了車,分期付款的那種,和老闆沒有一點關係。

至於五年後走上人生巔峰,更是瞎扯淡。我現在還是一名普通的程序員,生活的幸福指數也完全靠的是自己的付出。

這些年裡,老闆無數次胯下海口,聽得我耳朵都膩了。至於我為什麼還沒有離職,並不是我沉浸在溫柔故鄉,而是洛陽的軟件環境整體就這麼個樣,去哪都是打工,還不如自己踏踏實實做一些事情,比如說寫作。

對比我倆,就會發現一些很有意思的點,我來給同學們剖析一下。

1)學歷重不重要

很重要,郭大佬讀過深圳高級中學,深圳最好的高中之一,大學是暨南大學,211。

我呢,高中雖然是保送的,但那時候的學校已經走了下坡路,很動蕩,師資和校領導換了好幾波;然後我上的是一所大專。

所以我大學那會很自卑,即便專業是計算機網絡,也沒多少心思學習。而郭大佬就完全不一樣了,沉下一門心思學編程,為此還掛科了好幾門。因為他是非科班出身,專業是政治與行政管理。

假如,請允許我假如一下,給低學歷的同學們一點點信心。

假如我上大學那會一門心思撲倒編程上,大三也不至於出去參加培訓,真的,大把大把的時光我都浪費了。除了談戀愛是正事,我就只會打遊戲了。

假如拿現在的心態去過大學兩年的時光,我堅信,我一定能進阿里,因為拼過和沒拼過的人生差別巨大。

我就認識這樣一個初中小妹妹,平常老喊我二叔,搞得我都逆來順受了。她的成績非常優異,全年級第二名,為什麼不是第一名,因為語文成績拖了後腿。這不是關鍵,關鍵是小妹妹現在就開始學編程了,還去給初一的同學授過課。

後生可畏。

所以,我的結論就是,能通過學習改變命運,就下勁學,錯過這個年紀就真的沒機會了。如果真的上了大專,上了一般的本科,也不是沒有機會,別整天喊自己迷茫,誰的青春沒有迷茫過,關鍵是要發掘自己的興趣點,如果要從事程序員這個行業,就好好學編程。

2)要不要創業

十個創業九個坑,我只能這麼說,能進大廠進大廠,進不去進中廠,進不去中廠進小廠。如果非要創業,也得你自身實力夠硬,假如創業失敗,你還有出路,或者實在是沒有其他更好的選擇了,再選擇創業。

職場新人最好不要被忽悠去創業,太慘了。

你看,人家郭大佬在支付寶鍍了一層金,然後所在公司稀里糊塗被字節跳動收購了,這是運氣,沒得說。

大部分人的命運可能像我一樣,在日企待過三年半,有了一些資歷,然後作為技術大拿參与到創業公司,一開始老闆牛逼吹上天,最後,啥也沒撈着。

青春荒廢了,人際關係荒廢了,程序員的黃金年齡段也荒廢了。

3)要不要提前退休

有不少同學問過我這樣的話題,“二哥,我馬上到了結婚的年紀,雖然在一線城市掙得還可以,但遠沒到能買得起房子的水準,可能這輩子都不可能了,我想現在回二線城市或者三線城市,你看可行嗎?”

這種想法,其實就和郭大佬退休的想法是一致的,只不過郭財務自由了。

對於普通人來說,我的建議是這樣的,請認認真真做好筆記。

第一,不要盲目回二三線。

拿洛陽來說吧,一般程序員的極限工資就是一萬塊,撐死的那種。五險一金,包括獎金,能沒有公司就考慮沒有。

捫心自問一下,自己能否承受得起這份清心寡慾。另外,二三線城市也是會加班的,關鍵是不一定有加班工資。

第二,搞一份副業吧,同學們。

在一線城市,你可能沒有精力和時間搞副業,另外,主業的成長潛力並不比副業差,搞的意義不是特別大。但如果要回二三線,副業必須得搞,哪怕掙個零花錢,心裏不慌。

幸福指數高不高,離不開錢那,雖然很俗。粗茶淡飯沒問題,二三線城市的生活成本相對較低是真的,但你的掙錢能力也得匹配上吧,匹配不上的話,活得就會很累的。

我羡慕郭大佬,有些同學可能羡慕我,覺得我的幸福指數也很高。那我要告訴你的真相就是,我既要忙工作,還要讀書寫作,也是很拼的。

人生就是這樣,為別人的成功送上祝福的同時,不要忘記腳踏實地地活着。

如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。

本文已收錄 GitHub,傳送門~ ,裏面更有大廠面試完整考點,歡迎 Star。

我是沉默王二,一枚有顏值卻靠才華苟且的程序員。關注即可提升學習效率,別忘了三連啊,點贊、收藏、留言,我不挑,嘻嘻

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

【其他文章推薦】

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

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

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

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

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

聚甘新

Jmeter系列(21)- 詳解 HTTP Request

如果你想從頭學習Jmeter,可以看看這個系列的文章哦

https://www.cnblogs.com/poloyy/category/1746599.html

 

HTTP Request 介紹

用來發送 HTTP、HTTPS 協議請求

 

HTTP Request 界面

字段名 作用
名稱 不多介紹啦,建議自定義一個識別度高的名稱
註釋 對於測試沒有任何影響,僅記錄作用
協議

http或https,大小寫不敏感

默認:http

服務器名稱或IP
  • 服務器 host 或者 ip,不包括協議
  • 比如:www.baidu.com、192.168.196.128
端口號 目標服務器的端口號,默認:80
方法 發送 http 請求的方法
路徑
  • 目標請求的 URL 路徑
  • 不包括協議、host、ip、端口
內容編碼 請求的編碼方式,默認:iso8859
自動重定向
  • 發出的請求的響應碼是3**,會自動跳轉到新目標頁面
  • 只記錄最終頁面的返回結果
跟隨重定向
  • 和自動重定向唯一不同的是:
  • 會記錄重定向過程中的的所有請求的響應結果
使用 KeepAlive
  • jmeter 和目標服務器之間使用 Keep-Alive 方式進行 HTTP 通信
  • 真正做性能測試強烈建議不勾選
對POST使用multipart/form-data post 請求需要上傳文件時勾選
與瀏覽器兼容的頭
  • 當勾選 multipart/form-data 時,勾選此項
  • http請求頭中的 Content-Type 和Content-Transfer-Encoding 被忽略
  • 而只發送 Content-Disposition 部分

 

Parameters 講解

字段 描述
Name 參數名
Value 參數值
URL Encode?
  • 是否要 URL 編碼?
  • 重點:如果參數值包含了中文、特殊字符(非数字字母以外),最好勾上,當然全都勾上最穩妥
Content-Type
  • 參數值的資源類型
  • 默認:text/plain
Include Equals?
  • 當你的參數值為空的時候,可以選擇不包含=,默認勾選
  • 如果參數值不為空,則不可以取消勾選

 

什麼是 URL 編碼

  • URL 編碼解碼又叫百分號編碼,是統一資源定位(URL)的編碼方式
  • URL 地址(常說網址)規定了数字,字母可以直接使用,另外一批作為特殊用戶字符也可以直接用( / , : @  等),剩下的其它所有字符必須通過 %xx 編碼處理
  • 編碼方法很簡單,在該字符ascii碼的的16進制字符前面加%,如空格字符,ascii碼是32,對應16進制是20,那麼 urlencode 編碼結果是 %20 

 

URL 編碼的栗子

直接在網上搜在線 URL 編解碼

 

include equals 的栗子

參數值為空,且勾選 Include equals

 

參數值為空,但不勾選  Include equals

 

其實說的就是等於號而已,一般也不會傳空值,即使傳了也會帶上=

 

Body Data 講解

  • 沒啥好說的,傳 json 字符串就行了,注意下格式,後面再講具體栗子
  • 不過倒有個重點:如果 Parameters 有參數列表的話,是無法切換到 Body Data 的哦

 

Files Upload 講解

字段 描述
File Path 文件的本地路徑
Parameter Name 參數名
MIME Type 資源媒體類型

 

常見資源媒體類型

類型 文件後綴 格式
超文本標記語言文本 .html text/html
普通文本 .txt text/plain
XML 文件 .xml text/xml
PNG 圖片 .png image/png
GIF .gif image/gif
JPEG 圖片 .jpeg、jpg  image/jpeg

 

類型 文件後綴 格式
表單中進行文件上傳   multipart/form-data
表單默認提交數據的格式   application/x-www-form-urlencoded
XML 數據格式   application/xml
JSON 數據格式   application/json
PDF 文件 .pdf application/pdf
RTF 文本 .rtf application/rtf
GZIP 文件  .gz application/x-gzip
TAR 文件 .tar application/x-tar
AVI 文件 .avi  video/x-msvideo
MPEG 文件 .mpg、.mpeg video/mpeg

 

不同的content-type在jmeter中如何輸入參數

前提

因為是需要真實接口進行測試的,這裏提供兩種方案

  • 自己用 Flask 框架開發了本地的接口進行測試, 如果有需要的同學進群領取哦:870155189
  • 或者進入 http://open.yesapi.cn/?r=user/registration&from=wx_837493986,直接註冊個賬號,弄個免費會員,有在線免費的接口提供測試哦

 

application/x-www-form-urlencoded 的栗子

備註:也是表單提交最常見的栗子

 

Parameters 方式傳參

 

總結

  • 最終表單的參數列表會拼接到 URL 中,所以如果包含了中文、特殊字符就要勾選編碼?
  • 這裏不可以通過 Body Data 傳遞參數哦,會無法識別到參數,已實踐過(即使加了 HTTP請求頭也不行),乖乖用 Parameters 的方式傳參

 

content-type:application/json 的栗子

Body Data 方式傳參

 

添加 HTTP請求頭

 

請求體

 

請求頭

 

結論

重點就是添加 HTTP請求頭,指明 Content-type 是 json 格式

 

content-type:multipart/form-data

重點:用於 post 請求,需要文件上傳的場景;記住不是 get 請求

 

請求參數列表

如果選了 get 方法的話,文件參數是不會生效哦

 

文件參數

 

請求體

 

重點

  • 如果添加了 HTTP請求頭,請務必不要添加 content-type : multipart/form-data 
  • 如果加了的話:那麼所有的請求參數都會被當成文件以二進制形式傳輸,我們 parameters 里的文本格式參數就不會被識別,接口會提示參數為空

 

HTTP Request Advance

說實話我還沒用過這部分的內容,不過還是得了解下每個配置項是什麼意思哦

 

Client implemention 和 Timeouts

字段 描述
implementation 發送http請求的方式,可選項為 java、HttpClient4(默認)
Connect 連接超時時間,單位毫秒
Respones 響應等待超時時間,單位毫秒

 

Embedded Resources from HTML Files

  • 從HTML文件獲取所有內含的資源
  • jmeter 在發出的  HTTP請求獲得響應的 HTML文件內容后,對 HTML進行解析並獲取HTML中包含的所有資源(圖片、flash等)
字段 描述
Retrieve All Embedded Resources 發送http請求的方式,可選項為 java、HttpClient4(默認)
Parallel downloadds. Number

是否使用自設資源處。啟用后可以設置資源池大小,默認為6

URLs must match URL 匹配過濾,填寫此項則只會下載與此內容項匹配的 url 的資源

 

Source address

只用於 HTTP協議且 implemention = HttpClient4 時

字段 描述
IP/Hostname IP /主機名以使用特定的IP地址或(本地)主機名
Device 選擇設備以選擇該接口的第一個可用地址,該設備可以是IPv4或IPv6
Device IPv4 選擇IPv4設備來選擇名稱設備的IPv4地址(如eth0, lo, em0)
Device IPv6 選擇IPv6設備來選擇名稱設備的IPv6地址(如eth0, lo, em0)

 

Proxy Server

代理服務器

字段 描述
Server Name or IP 代理服務器的名稱或者IP地址
Port Number 代理服務器的端口號
Username 代理服務器的用戶名
Password 代理服務器的密碼

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

聚甘新

汎德和台達電合作,為北市增添電動車充電站

電源管理及散熱解決方案廠商台達電10日宣布,BMW台灣總代理汎德選定台達的電動車充電解決方案,並捐贈給台北市政府,將在今年3月底前,為台北市20處公用停車場設置40台具有網路通訊功能、IP55防塵防水與時尚精巧的外型設計的台達智能電動車交流充電機,為越來越多的BMW i系列以及搭配J1772 Type1規格的電動車提供更便捷的充電服務,同時也強化了台達在台灣的充電站普及率。

台達副總裁暨電源系統事業群總經理鄭安強調,台達十分樂意與夥伴攜手實現智能綠生活,很榮幸可以滿足BMW台灣總代理汎德對充電站的嚴謹要求,包括其高度可靠度以及智能化。事實上,台達電動車充電解決方案已獲得許多國家的肯定,並且在歐洲、台灣以及大中華區累積不少成功案例。

台達電指出,台達智能電動車交流充電機擁有網路通訊功能、內建RFID讀卡機與7kW電源容量,能協助系統管理廠商為電動車車主提供更便利的服務。另外,這款通過台灣CNS標準認證的交流充電機具有IP55防塵防水與防破壞(IK8)機體設計,除了相當堅固外,時尚精巧的外型設計更能節省空間,而台達充電站管理系統(Site Management System, SMS)能進行遠端支援及能源管理的功能,提升充電基地營運效率。

台達電並表示,台達電動車充電解決方案逐步於全球協助夥伴建立住宅及商業應用的高效、快速與方便的電動車充電設備。例如,台達的120kW直流超快速電動車充電機於挪威及瑞典的高速公路能夠同時提供四台電動車快充的服務。台達其它直流與交流技術也於香港國際機場、上海的商業大樓與台灣的日月潭旅客租車服務支援電動交通。

(本文內容由授權使用。)

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

【其他文章推薦】

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

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

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

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

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

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

第七屆中國國際新能源汽車論壇2017之電池前沿技術與前景

毋庸置疑,電池是新能源汽車的核心部件。當前比較主流的電池包括超級電容器、金屬氫化物鎳電池、鋰離子電池、燃料電池。超級電容器的特點是可承受瞬間大電流充放電,但儲電量低,不能驅動車輛長時間的使用;金屬氫化物電池具備大電流充放電能力,安全性好,但是比容量低,體積較大;鋰離子電池的電壓在這幾類電池中最高,比容量高,但它的是安全性、低溫性能差;燃料電池從去年開始走進更多人的視線,能量儲備充足,可快速補充燃料,但成本高,瞬間輸出能力差,致命的缺陷是不能進行能量的回饋,導致驅動的車輛不能只用燃料電池實現刹車時能量的回收。

站在“風口”的新能源汽車正在經歷“冰火兩重天”:一面是整個行業不斷高速擴張銷量加速,但另一面,作為新能源汽車“心臟”的動力電池供應卻出現不足,影響新能源整車產量從量上看,今年新能源產銷帶來的動力電池需求依然將進一步放大。但在下游原材料價格暴漲以及上游整車廠價格打壓的雙重壓力下,什麼樣的動力電池企業能夠實現突圍?業內人士預計,在短暫的火爆期之後,成本和技術導向或使得今年動力電池行業將進入洗牌期。而在霧霾圍城、PM2.5屢屢爆表的現狀下,發展並普及新能源汽車具有重要的現實意義。而因新能源汽車井噴所帶來的大量退役動力電池,引發了行業的未雨綢繆。

新能源汽車對於電池的需求量仍處於巨大的增長空間,並且總體而言,動力電池的安全性與新能源汽車的使用要求還存在一定差距,中國國際新能源汽車論壇—致力於打造全球規模最大,最國際化的新能源汽車論壇的第七屆大會攜手上海市嘉定區人民政府將會專門設立動力電池板塊,進行為期2天的技術探討及分享,屆時國內外知名的整車商,動力電池知名供應商及零部件企業高層領導等將前來分享各自對於目前動力市場的發展前景看法及介紹最前沿的技術,同時,也會側重於目前存在的難點——梯次利用及回收和技術方面進行更深層次的交流。

本屆大會將於2017年5月17-19日在上海舉辦,大會涉及七個論壇、頒獎典禮、研討會及晚宴。屆時將會有全球範圍內的整車製造商、電網電力公司、電池廠商、零部件供應商、核心技術提供商和政府官員等600多位行業人士一起,對新能源汽車產業面臨的挑戰,機遇與對策各方面進行為期三天更深層次並具有建設和戰略性的探討,期待您的參與。

會議結構

若您對峰會有更多要求,請撥打021-6045 1760與我們聯繫,謝謝理解和支持!
我們期待與貴單位一起出席於2017年5月17-19日在上海舉辦的第七屆中國國際新能源汽車論壇2017,以利決策!
想瞭解詳細內容,請登陸官方網站:
連絡人:Summer(謝清)
電話:+86 21-6045 1766
傳真:+86 21-6047 5887
郵箱: market@ourpolaris.com

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

【其他文章推薦】

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

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

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

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

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

Nissan 無人駕駛電動車預計2020量產

日本汽車廠商Nissan 宣布,該公司歐洲的首次無人駕駛汽車測試將在倫敦進行,2017 年下半年有望在歐洲市場推出配置半自動駕駛的SUV,這一車款已經在日本市場上市。

Nissan 對於無人駕駛技術的研發非常重視,同時在應用方面表現得非常謹慎。此次在倫敦進行測試的無人駕駛車採用的技術短期內不會應用在量產車上,測試的將是一輛LEAF 電動車,配置了雷達、鐳射和鏡頭,預期在2020 年會應用到量產車上。Nissan 會邀請監管機構和政府工作人員來體驗無人駕駛車,測試的過程中車內會安排司機監控,以避免意外狀況的發生,測試不會向大眾開放。

Nissan 並不是第一家在英國進行無人駕駛公共道路測試的公司,Volvo 將在英國的公共道路上進行100 台無人駕駛汽車的測試。

汽車廠商認為無人駕駛技術能夠有效地提升乘車的安全性,減少車禍的死亡率,但這一技術的推廣仍面臨許多挑戰,公眾對於無人駕駛技術存在諸多質疑。

據倫敦經濟學院最新報告顯示,大約有55% 的英國司機表示在無人駕駛汽車中非常不自在,83% 的司機更擔心無人駕駛汽車發生故障,85% 的司機認為無人駕駛系統無法跟人互動。

(合作媒體:。圖片出處:)

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

Honda和GM發展氫燃料電池,最快2020生產

在鋰電池電動車發展趨穩定的現階段,也有另一派人支持發展氫動力燃料電動車。美國本田汽車(Honda)和通用汽車(GM)宣布將攜手發展氫動力燃料電池新系統,預計2020 年投產。

美國加州政策規定,自2018年起,各家汽車製造商要銷售一定比例之「零排放車」。看準美國對電動車需求將增加,兩公司於1月30日宣布,將共同出資成立公司,合作發展氫動力電池系統,廠址將設於通用美國密西根州的工廠內。而本田汽車目前於日本櫪木縣高根澤町的工廠,之後將逐步停產。

鋰電池電動車和氫燃料電動車各有其擁護者與利弊。雖然兩者都被列為「零汙染」汽車,但實際上鋰電池電動車環保與否還要追溯到發電的來源,而氫動力燃料電池則有甲烷和一氧化碳排放的疑慮。充電方面,電動車充電耗時;氫燃料補充速度和油槍一般,只是日後加氫站的設點還有待規劃,安全性的需求也更高。

本田和通用自2013年就已展開合作。本次協議各出資一半資金成立新公司,目標是降低氫燃料電池成本較高的難題,並在環保車競爭市場保有優勢。

(首圖來源:General Motors)

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

【其他文章推薦】

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

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

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

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

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

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

受樂視影響,法拉第縮小工廠規模

根據路透社的報導,中國影視廠商樂視網旗下子公司,豪華電動車新創公司法拉第未來(Faraday Future),已經決定縮減在美國興建車廠的規模。組裝廠規模將由原來佔地300 萬平方英尺,大幅縮減至65 萬平方英尺,生產車款數量亦會由7 款大減至2 款。

報導指出,法拉第曾於2015 年表示,總投資金額達到13 億美元,在內華達州北拉斯維加斯的組裝廠年產可高達15 萬輛,而且2017 年底投產。不過根據目前的狀況顯示,法拉第目前該工廠得預定產能每年將少於1 萬輛,而且還必須延後到2019 年才開始正式量產。

由於受到前一段時間母公司樂視網財務困境的影響,法拉第工廠一度傳出高層離職、欠供應商貨款、甚至是停工歇業的消息。雖然,在母公司獲得人民幣168 億元的資金援助後,工廠重新啟動。不過,可能也是因為這樣的原因,使得樂事網不得不宣布縮小其工廠的規模與未來的生產能量。

在2017 年的CES 展上,法拉第推出首輛量產車款FF91 之後,由於具備高度智慧連網功能,以及搭載時下最熱門的自動駕駛技術。並且全車配備3D 雷達、10 個高感度相機、13 個長距離和短距離雷達以及12 個超聲波感測器,可以說性能超越市面上包括賓利、法拉利和特斯拉的所有電動車。但即便如此,分析師仍認為,法拉第FF91 的推出依舊是遠水救不了近火。

有統計指出,未來幾年內,樂視網在法拉第汽車上的資金缺口約在人民幣460 億元以上。而根據樂視網在2017 年1 月13 日發布的公告表示,引入包括融創中國在內的多家戰略投資,投資金額為人民幣168 億元的資金,則將汽車業務排除在外。換言之,樂事網的汽車豪賭,未來還需要再另籌資金。所以,在FF91 售價高達人民幣200 萬元,再加上樂視網資金仍有隱憂的情況下,法拉第工廠能不能順利在2019 年生產、上路,恐怕還有變數。

(合作媒體:。圖片出處:Faraday Future)

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

【其他文章推薦】

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

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

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

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

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

智慧汽車前景看好,車用LED廠展開投資

近來,陸續有汽車與科技大廠於加州展開無人駕駛路上測試,智慧汽車儼然成為科技市場的新藍海。這股趨勢也吸引光學鏡頭和LED廠商展開投資。

看好無人駕駛技術發展潛力,除Tesla、Ford、GM與Goole等廠商展開道路測試外,也有鏡頭廠和LED廠開始投入。自駕車上路仰賴感測器進行路況偵測,因此感測器和車燈比以往更受重視,以滿足嚴格的安全性標準。

在車用LED市場方面,日本Panasonic(松下)有意收購歐洲車燈大廠ZKW,結合自家感測器專長,攻自駕車市場;而據《聯合財經網》報導,台廠群光以子公司群電買下中國車燈廠以進攻其供應鏈,且與LED車燈廠頻繁接觸,同樣有意搶食車用LED這塊大餅。

另外,照明大廠歐司朗(OSRAM)在2017年調降LED車用成本,為的是加快市場滲透率。其採用金屬導線架以取代原先LED陶瓷散熱基板,除成本降低之外,也利於設計;且其車尾燈、方向燈等產品均已改用EMC導線架。

《聯合財經網》指出,有供應商表示,除了LED大燈仍採用陶瓷基板外,其餘車體內外的晝型燈可能改用EMC導線架。隨車用LED應用變多、EMC導線架的使用增加,中國LED晝型的燈滲透率也逼近50%。

 (首圖來源: CC2.0)

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準