編程必備基礎之操作系統_網頁設計公司

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

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

操作系統概述

  操作系統是管理計算機硬件和軟件資源的計算機程序,管理配置內存、決定資源供需順序、控制輸入輸出設備等。操作系統提供讓用戶和系統交互的操作界面。操作系統的種類是多種多樣的,不局限於計算機,從手機到超級計算機,操作系統可簡單也可複雜,在不同的設備上,操作系統可向用戶呈現多種操作。因為我們不可能直接操作計算機硬件,而且設備種類繁多,需要一個統一的界面,因此有了操作系統,操作系統的簡易性使得更多人能使用計算機。常見的操作系統有:Windows、Linux、MacOS、Android等,總結一句話就是:操作系統是管理硬件、提供用戶交互的軟件系統。

  • 操作系統的基本功能
    • 操作系統統一管理着計算機資源。這些計算機資源包括處理器資源、存儲器資源、IO設備資源和文件資源等。
    • 操作系統實現了對計算機資源的抽象。即用戶無需面向硬件接口編程;IO設備管理軟件,提供獨寫接口;文件管理軟件,提供操作文件的接口。
    • 操作系統提供了用戶和計算機之間的接口。例如圖像窗口形式、命令行形式和系統調用形式等。
  • 操作系統的相關概念
    • 併發性:說到併發就不得不提一下并行性,并行性是指兩個或多個事件可以在同一時刻發生,而併發性是指兩個或多個事件可以在同一個時間間隔發生。
    • 共享性:多個程序可以同時使用主存資源,資源共享根據屬性分為互斥共享和同時訪問兩種形式
      • 互斥共享形式:當資源被程序A佔用時,其他想使用的話就只能等待,只有進程A使用完以後,其他進程才可以使用該資源。
      • 同時訪問形式:某種資源在一段時間內併發地被多個程序訪問,這種“同時”是宏觀的,從宏觀去看該資源可以被同時訪問
    • 虛擬性:虛擬性表現為把一個物理實體轉為若干個邏輯實體,物理實體是真實存在的,邏輯實體是虛擬的,虛擬的技術主要有時分復用技術和空分復用技術。
      • 時分復用技術:資源在時間上進行復用,不同程序進行併發使用,多道程序分時使用計算機的硬件資源,提高資源的利用率
        • 虛擬處理器技術:藉助多道程序設計技術,為每個程序建立進程,多個程序分時復用處理器
        • 虛擬設備技術:物理設備虛擬為多個邏輯設備,每個程序佔用一個邏輯設備,多個程序通過邏輯設備併發訪問
      • 空分復用技術:空分復用技術用來實現虛擬磁盤、虛擬內存等,提高資源利用率,提高編程效率
        • 虛擬磁盤技術:物理磁盤虛擬為邏輯磁盤,例如C、D、E等邏輯盤,使用起來更加安全方便
        • 虛擬內存技術:在邏輯上擴大程序的存儲容量,使用比實際內存更大的容量,大大提升編程效率
    • 異步性:在多道程序環境下,允許多個進程併發執行,進程在使用資源時可能需要等待和放棄,進程的執行並不是一氣呵成的,而是以走走停停的形式推進

進程管理

  為什麼需要進程呢?在沒有配置OS(操作系統)之前,資源屬於當前運行的程序,配置OS之後,引入多道程序設計的概念,可以合理的隔離資源、運行環境、提升資源利用率。進程是系統進行資源分配和調度的基本單位,進程作為程序獨立運行的的載體保障程序正常運行,進程的存在使得操作系統資源的利用率大幅提升。

進程管理之進程實體

主存中得進程形態

  • 標識符:標識符唯一標記一個進程,用戶區別其他進程,如進程id
  • 狀態:標記進程的進程狀態,如:運行態
  • 程序計數器:指向進程即將被執行的下一條指令的地址
  • 內存指針:程序代碼,進程數據相關指針
  • 上下文數據(重要):進程執行時處理存儲器的數據
  • IO狀態信息:被進程IO操作時所佔用的文件列表
  • 記賬信息:使用處理器時間、時鐘數總和等。

  由此可知,主存中的進程形態主要包括進程標識符,處理機狀態,進程調度信息,進程控制信息等。其中進程控制塊(PCB)是用於描述和控制進程運行的通用數據結構,記錄進程當前狀態和控制進程進行運行的全部信息,PCB使得進程成為能夠獨立運行的基本單位。PCB是操作系統進行調度經常會被讀取的信息,而且是常駐內存的,存放在系統專門開闢的PCB區域內。

進程與線程

  之前說過進程是操作系統進行資源分配和調度的基本單位,而線程是操作系統進行運行調度的最小單位,線程包含在進程之中,是進程中實際運行的工作單位,一個進程可以併發多個線程,每個線程執行不同任務。

   進程 線程
資源 資源分配的基本單位 不擁有資源
調度 獨立調度的基本單位 獨立調度最小單位
系統開銷 進程系統開銷大 線程系統開銷小
通信 進程IPC 讀寫同一進程數據通信

  一個進程可以有多個線程,一個進程中的線程共享資源,計算機對進程的調度,實際上是對進程中的線程進行調度

五狀態模型

  • 創建狀態:創建進程時擁有PCB但其它資源尚未就緒的狀態稱為創建狀態,操作系統提供fork函數接口創建進程。
  • 就緒狀態:當進程被分配到除CPU以外的所有必要資源后,只要再獲得CPU的使用權,就可以立即運行。其他資源都轉準備好、只差CPU資源的成為就緒狀態。
    • 在一個系統中處於就緒狀態的進程通常排成一個隊列,稱為就緒隊列。
  • 執行狀態:進程獲得CPU,其程序正在執行稱為執行狀態,再單處理機中,在某個時刻只能有一個進程是處於執行狀態。
  • 阻塞狀態:進程因某種原因如:其他設備未就緒而無法繼續執行,從而放棄CPU的狀態稱為阻塞狀態。
  • 終止狀態:程序執行完成。

進程同步

  為什麼需要進程間的同步呢?先讓我們來看一個經典的問題:生產者-消費者問題
生產者-消費者問題:有一群生產者進程在生產產品,並將這些產品提供給消費者進程進行消費,生產者進程和消費者進程可以併發執行,在兩者之間設置了一個具有n可緩衝區的緩衝池,生產者進程需要將所生產的產品放到一個緩衝區中,消費者進程可以從緩衝區取走產品消費

由上圖我們可以看出,單從生產者程序或消費者程序去看是沒問題的,但兩者併發執行時就可能會出現差錯。如下圖:

這裏的緩衝區就相當於臨界資源。
  再來看一個哲學家進餐問題:
有五個哲學家,他們的生活方式時是交替的進行思考和進餐,哲學家們共同使用一張圓桌子,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五支筷子。平時哲學家們只進行思考,飢餓時則試圖取靠近他們的左、右兩隻筷子,只有兩支筷子都被他拿到的時候才能進餐,進餐完畢后,放下左右筷子繼續思考。

  出現上圖中的問題是什麼呢?其根源問題是:彼此之間沒有相互通信,如果“生產者通知消費者我已經完成了一件生產”,“哲學家向旁邊哲學家說我要進餐了”,就不會出現上圖中的問題了,也就是需要進程間的同步。

  什麼是進程同步呢?當對競爭資源在多個進程間進行使用次序的協調,使得併發執行的多個進程之間可以有效使用資源和相互合作。這裏的競爭資源也就是上圖中的臨界資源,什麼是臨界資源?臨界資源指的是一些雖作為共享資源,卻又無法同時被多個線程共同訪問的共享資源。當有進程在使用臨界資源時,其他進程必須依據操作系統的同步機制,等待佔用進程釋放該共享資源,才可以重新競爭使用共享資源。
進程同步的原則:

  • 空閑讓進:資源五佔用,允許使用
  • 忙則等待:資源有佔用,請求進程等待
  • 有限等待:保證有限等待時間能夠使用資源
  • 讓權等待:等待時,進程需要讓出CPU

  進程間同步的常用方法:如消息隊列,共享存儲,信號量。當多個線程併發使用進程資源時,進程內的多線程也需要,因為進程中的資源時進程中線程的共享資源。線程同步的方法有:互斥量、讀寫鎖、自旋鎖、條件變量等,這些方法是如何保證線程同步的呢?

  • 互斥量:由於多個線程的指令交叉執行,而互斥量可以保證先後執行,即保證原子性。什麼是原子性呢?原子性是指一系列操作不可被中斷的特性, 這一系列操作要麼全部執行完成,要麼全部沒有執行,不存在部分執行部分未執行的情況
    • 互斥量是最簡單的線程同步方法
    • 互斥量(互斥鎖),處於兩態之一的變量:解鎖和加鎖
    • 兩個狀態可以保證資源的串行
  • 自旋鎖:自旋鎖也是一種多線程同步的變量,使用自旋鎖的線程會反覆檢查鎖變量是否可用,自旋鎖不會讓出CPU,是一種忙等待狀態,即死循環等待鎖被釋放。
    • 自旋鎖避免了進程或線程上下文切換的開銷
    • 操作系統內部很多地方都是使用的自旋鎖
    • 自旋鎖不適合在單核CPU中使用
  • 讀寫鎖:這種鎖適用於臨界資源多讀少寫,讀取的時候並不會改變臨界資源的值。
    • 讀寫鎖是一種特殊的自旋鎖
    • 允許多個讀者同時訪問資源以提高讀的性能
    • 對寫的操作則是互斥的
  • 條件變量
    • 條件變量是一種相對複雜的同步方法
    • 條件變量允許線程睡眠,直到滿足某種條件
    • 當滿足條件時,可以向該線程發送信號,通知喚醒
同步方法 描述
互斥鎖 最簡單的一種線程同步方法,會阻塞線程
自旋鎖 避免切換的一種線程同步方法,屬於“忙等待”
讀寫鎖 為“讀多寫少”的資源設計的線程同步方法,可以顯著提高性能
條件變量 相對複雜的一種線程同步方法,有更靈活的使用場景

進程同步之共享內存
  在某種程度上,多進程是是共同使用物理內存的,由於操作系統的進程管理,進程間的內存空間是獨立的,進程默認是不能訪問進程空間之外的內存空間的

共享內存就可以打破這個限制,因為有這個共享內存,不同進程就可以通過頁表映射到同一個共享內存去,這個共享內存既可以被進程1使用,也可以被進程2使用。

  共享存儲允許不相關的進程訪問同一片物理內存,共享內存是兩個進程之間共享和傳遞數據的最快方式,共享內存未提供同步機制,需要藉助其他機制訪問。通過共享內存同步的過程就是:申請共享內存->連接到進程空間->使用共享內存->脫離進程空間並且刪除。共享內存是高性能後台開發中最常用的同步方式。
進程同步之Unix域套接字
  域套接字是一種高級的進程間通信的方法,Unix域套接字可以用於同一台機器進程間通信。其運行過程是創建套接字->綁定(bind)套接字->監聽(listen)套接字->接收&處理信息。域套接字提供了簡單可靠的進程通信同步服務,只能在單機使用,不能跨機器使用。

Linux的進程管理

Linux進程的相關概念:

進程類型:

  1. 前台進程:前台進程就是具有終端,可以和用戶交互的進程
  2. 後台進程:
    • 與前台進程相對,沒有佔用終端的就是後台進程
    • 後台進程基本上b不和用戶交互,優先級比前台進程低
    • 將需要執行的命令以“&”符號結束
  3. 守護進程(daemon):特殊的後台進程
    • 很多守護進程在系統引導的時候啟動,一直運行到系統關閉
    • Linux系統有很多典型的守護進程。例如:crond,sshd,httpd,mysqld等,進程名字以“d”結尾的一般都是守護進程。
      進程標記:
  • 進程ID
    • 進程ID是進程的唯一標記,每個進程擁有不同的ID
    • 進程ID表現為一個非負整數,最大值由操作系統限定
    • 操作系統提供fork()函數接口創建進程。例如進程A調用fork接口創建了進程B,進程B調用fork接口創建了進程C,那此時進程A和進程B就存在父子進程關係,進程A是父進程,進程B是子進程。進程的父子關係可以通過pstree命令查看。

ID為0的進程是idle進程,是系統創建的第一個進程,ID為1的進程init進程,是0號進程的子進程,完成系統初始化,Init進程是所有用戶進程的祖先進程。

  • 進程的狀態標記

Linux中進程的狀態如下:

狀態符號 狀態說明
R (TASK_RUNNING),進程正處於運行狀態
S (TASK_INTERRUPTIBLE),進程正處於睡眠狀態
D (TASK_UNINTERRUPTIBLE),進程正處於IO等待的睡眠狀態
T (TASK_STOPPED),進程正處於暫停狀態
Z (TASK_DEAD or EXIT_ZOMBIE),進程正處於退出狀態,或殭屍進程

操作Linux進程的相關命令

  • ps命令:ps命令常用於显示當前進程的狀態,ps命令常配合aux參數或ef參數和grep命令檢索特定進程
  • top命令
  • kill命令:kill命令發送指定信號給進程,kill-l可以查看操作系統所支持的系統

作業管理

作業管理之進程調度

  進程調度是指計算機通過決策,決定哪個就緒進程可以獲得CPU使用權。通俗來說就是保留舊進程的運行信息,請出舊進程(收拾包袱),選擇新進程,準備運行環境並分配CPU(新駐進)。那麼是如何進行進程的調度的呢?

  • 就緒隊列的排隊機制:將就緒進程按照一定的方式排成隊列,以便調度程序可以最快找到就緒進程。
  • 選擇運行進程的委派機制:調度程序以一定的策略選擇就緒進程,將CPU資源分配給它
  • 新老進程的上下文切換機制:保存當前進程的上下文信息,裝入被委派執行進程的運行上下文

  進程的調度方式分為搶佔式調度和非搶佔式調度。非搶佔式調度是指處理器一旦分配給某個進程,就讓該進程一直使用下去,調度程序不以任何原因搶佔正在被使用的處理器,直到進程完成工作,或因為IO阻塞才會讓出處理器;搶佔式調度是指允許調度程序以一定的策略,暫停當前運行的進程,保存好進程的上下文信息,分配處理器給新進程。

   搶佔式調度 非搶佔式調度
系統開銷 頻繁切換,開銷大 切換次數少,開銷小
公平性 相對公平 不公平
應用 通用系統 專用系統

進程調度算法

  • 先來先服務調度算法
  • 短進程優先調度算法:調度程序優先選擇就緒隊列中估計運行時間最短的進程;短進程優先調度算法不利於長作業進程的運行
  • 高優先權優先調度算法:進程附帶優先權,調度程序優先選擇權最高的進程,高優先權優先調度算法使得 緊迫的任務可以處理
  • 時間片輪轉調度算法:按先來先服務的原則排列就緒進程,每次從隊列頭部取出待執行進程,分配一個時間片執行;是相對公平的調度算法,但不能保證及時響應用戶

作業管理之死鎖

  死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞現象,若無外力作用,他們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
死鎖的產生

  • 競爭資源:共享資源數量不滿足各個進程需求,各個進程 之間發生資源競爭導致死鎖,
  • 進程調度順序不當
    死鎖的四個必要條件:
  • 互斥條件:進程對資源的使用是排他性的使用,某資源只能由一個進程使用,其他進程需要使用只能等待
  • 請求保持條件:進程至少保持一個資源,又提出新的資源請求,新資源被佔用,請求被阻塞,被阻塞的進程不釋放自己保持的資源
  • 不可剝奪條件:進程獲得的資源在未完成使用前不能被剝奪,獲得的資源只能由進程自生釋放
  • 環路等待條件:發生死鎖時,必然存在進程-資源環形鏈
    死鎖的處理
  • 預防死鎖的方法
    • 摒棄請求保持條件:系統規定進程運行之前,一次性申請所有需要的資源,進程在運行期間不會提出資源的請求,從而摒棄請求保持條件
    • 摒棄步課剝奪條件:當進程請求一個新的資源得不到滿足時,必須釋放佔有的資源,當進程運行時佔有的資源可以被釋放,意味着可以被剝奪
    • 摒棄環路等待條件:可用資源線性排序,申請必須按照需要遞增申請,線性申請不在形成環路,從而摒棄了環路等待條件
  • 銀行家算法:銀行家算法是一個可操作得著名得避免死鎖得方法,以銀行借貸系統分配策略為基礎的算法。
    • 客戶申請的貸款是有限的,每次申請須聲明最大資金量
    • 銀行家在能夠滿足貸款時,都應該給用戶貸款
    • 客戶在使用貸款后,能夠及時歸還貸款。

根據還需要分配的資源表,對比可分配資源表,先給能夠滿足貸款的用戶,給用戶貸款,即圖中的P2,P2使用完資源后,需要及時歸還資源

存儲管理

  早期計算機編程並不需要過多的存儲管理,隨着計算機和程序越來越複雜,存儲管理成為必要。

  • 確保計算機有足夠的內存處理處理數據
  • 確保程序可以從可用內存中,獲取一部分內存使用
  • 確保程序可以歸還使用后的內存,已供其他程序使用

存儲管理之內存分配與回收

內存分配的過程

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

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

  • 單一連續分配:單一連續分配是最簡單的內存分配方式,只能在單一用戶、單進程的操作系統中使用
  • 固定分區分配:固定分區分配是支持多道程序的最簡單的存儲分配方式,內存空間被劃分成若干個固定大小的區域,每個分區只提供給一個用戶使用,互不干擾
  • 動態分區分配:根據進程實際需要,動態分配內存空間,相關數據結構、分配算法如下:
    • 動態分區空閑表數據結構:對空閑區進行標記,0表示空閑區,1表示已被使用
    • 動態分區空間鏈數據結構
    • 首次適應算法(FF算法):分配內存時從開始,順序查找適合內存區,若沒有合適的空閑區,則該次分配失敗;每次從頭部開始,使得頭部地址不斷被劃分
    • 最佳適應算法(BF算法):最佳適應算法要求空閑鏈表按照容量大小排序,遍歷空閑鏈表找到最佳合適的空閑區
    • 快速適應算法(QF算法):快速適應算法要求有多個空閑區鏈表,每個空閑區鏈表存儲一種容量的空閑區

內存回收的過程

  情況一:不需要新建空閑鏈表節點,只需要把空閑區1的容量增大為空閑區即可;情況二:將回收區與空閑區合併,新的空閑區使用回收區的地址;情況三:將空閑區1、空閑區2和回收區合併,新的空閑區使用空閑區1的地址;情況四:為回收區創建新的空閑節點,插入到相應的空閑區鏈表中去。

存儲管理之段頁式存儲管理

  由於每個進程都有自己獨立的進程空間,那操作系統是如何管理進程的空間呢?

  • 頁式存儲管理:
    • 將進程邏輯空間等分為若干大小的頁面
    • 相應的把物理內存空間分成與頁面大小的物理塊
    • 以頁面為單位把進程空間裝進物理內存中分散的物理塊

      頁表:頁表記錄了進程邏輯空間與物理空間的映射

現代計算機系統中,可以支持非常大的邏輯地址空間(\(2^{32}\)~\(2^{64}\)),這樣,頁表就變得非常大,要佔用非常大的內存空間,如具有32位邏輯地址空間的分頁系統,規定頁面大小為4KB,則在每個進程頁表中的頁表項可達1M(32位系統進程的尋址空間為4G,4G/4KB=1M)個,如果每個頁表項佔用1Byte,故每個進程僅僅頁表就要佔用1M的內存空間。

  • 段式存儲管理
    • 將進程邏輯空間劃分成若干段(非等分)
    • 段的長度由連續邏輯的長度決定
    • 主函數MAIN,子程序段X,系函數Y等

  段式存儲和頁式存儲都離散地管理了進程的邏輯空間。頁是物理單位,段是邏輯單位,分頁是為了合理利用空間,分段是為了滿足客戶需求;頁大小由硬件空間,段長度可動態變化;頁表信息是一維的,段表信息是二維的。

  • 段頁式存儲管理:由於分頁可以有效提高內存利用率(雖然說存在內存碎片),分段可以滿足用戶需求,我們可以將兩者結合,形成段頁式存儲管理。
    • 先將邏輯空間按段式管理分成若干段
    • 再把段內空間按頁式管理等分成若干頁

存儲管理之虛擬內存

  思考:一個遊戲十幾個G,物理內存只有4G,那這個遊戲是如何運行起來的呢?

  有些進程實際需要的內存很大,超過物理內存的容量,多道程序設計,使得每個進程可用物理內存更加稀缺,不可能無限增加物理內存,物理內存總有不夠的時候,這個時候就需要虛擬內存了。虛擬內存是操作系統內存管理的關鍵技術,使得多道程序運行和大程序運行成為現實,把程序使用內存劃分,將部分暫時不使用的內存放置在輔存。

程序的局部性原理:局部原理是指CPU訪問存儲器時,無論是存取指令還是存取數據,所訪問的存儲單元都趨於集中在一個較小的連續區域中。

  • 程序運行時,無需全部裝入內存,裝載部分即可
  • 如果訪問頁不在內存,則發出缺頁中斷,發起頁面置換
  • 從用戶層面看,程序擁有很大的空間,即是虛擬內存
  • 虛擬內存實際是對物理內存的補充,速度接近於內存,成本接近於輔存

虛擬內存的置換算法:和我在《計算機組成原理》這篇博客中的高速緩存的置換策略差不多,這裏就不詳細介紹了。

  • 先進先出算法(FIFO)
  • 最不經常使用算法(LFU)
  • 最近最少使用算法(LRU)

高速緩存的替換策略發生在Cache-主存層次,只要是為了解決速度問題;虛擬內存的替換策略發生在主存-輔存層次,主要是為了解決容量問題。

Linux的存儲管理

Buddy內存管理算法

  • Buddy算法是經典的內存管理算法
  • 算法基於計算機處理二進制的優勢具有極高的效率
  • 算法主要是為了解決內存外碎片的問題

頁內碎片:內部碎片是已經被分配出去(能明確指出屬於哪個進程)的內存空間大於請求所需的內存空間,不能被利用的內存空間就是內部碎片。
頁外碎片:外部碎片是指還沒有被分配出去(不屬於任何進程),但是由於大小而無法被分配給申請內存空間的新進程的內存空閑塊。
Buddy是夥伴的意思,這裏的”夥伴“指的是內存的”夥伴“,一片連續內存的”夥伴“是相鄰的另一片大小一樣的連續內存
Buddy內存管理算法執行過程:創建一系列空閑塊鏈表,每一種都是2的冪 –> 現在需要分配100kb內存 –> 回收剛才分配的內存

Linux的交換空間

  交換空間(Swap)是磁盤的一個分區,Linux物理內存滿時,會把一些內存交換至Swap空間,Swap空間是初始化系統時配置的。

冷啟動內存依賴:對於一些大型的應用程序,在啟動的過程中需要使用大量的內存,但是這些內存很大一部分只是在啟動的時候使用一下,在運行的時候很少使用到這部分內存,因此有了這個交換空間,系統就可以將這個部分不怎麼使用的內存數據保存在SWAP空間中,從而釋放跟多的物理內存,提供給這個系統使用。

系統睡眠依賴: 當Linux系統需要睡眠的時候,它就會把系統中的所有數據都保存在swap空間內,等下次這個系統需要啟動的時候,才把這些數據重新加載到內存中裏面,這樣就可以加快系統的啟動速度。

大進程空間依賴:有些進程確實需要使用大量的內存空間,但是物理內存不夠使用,因此需要把這些進程需要使用的內存暫時保存到交換空間中,使得這個大的進程也可以運行起來

Swap空間和虛擬內存的對比:

Swap空間 虛擬內存
存儲位置 Swap空間存在於磁盤 虛擬內存存在於磁盤
置換層次 Swap空間與主存發生置換 虛擬內存與主存發生置換
所屬概念 Swap空間是操作系統概念 虛擬內存是進程概念
解決的問題 Swap空間解決系統物理內存不足問題 虛擬內存解決進程物理內存不足的問題

操作系統的文件管理

文件的邏輯結構

  • 邏輯結構的文件類型
    • 有結構文件:例如文本文件、文檔、媒體文件等
      • 文件內容由定長記錄和可變記錄組成
      • 定長記錄存儲文件格式、文件描述等結構化數據項
      • 可變長記錄存儲文件具體內容等
    • 無結構文件:例如二進制文件、鏈接庫等
      • 也稱為流式文件,如exe文件、dll文件、so文件等
      • 文件內容長度以字節為單位
  • 順序文件
    • 順序文件是指按順序存放在存儲介質中的文件
    • 磁帶的存儲特性使得磁帶文件只能存儲順序文件
    • 順序文件是所有邏輯文件當中存儲效率最高的
  • 索引文件
    • 可變長文件不適合使用順序文件格式存儲
    • 索引文件是為解決可變長文件存儲而發明的一種文件格式
    • 索引文件需要配合索引表完成存儲的操作

輔存的存儲空間分配

  • 輔存的分配方式
    • 連續分配:順序讀取文件內容非常容易,速度很快,對存儲要求高,要求滿足容量的連續存儲空間
    • 鏈接分配:鏈接分配可以將文件存儲在離散的盤塊中,需要額外的存儲空間存儲文件的盤塊鏈接順序
      • 隱式鏈接:隱式分配的下一個鏈接指向存儲在當前盤塊內,隱式分配適合順序訪問,隨機訪問效率低,可靠性差,任何一個鏈接出問題都會影響整個文件
      • 显示鏈接:不支持高效的直接存儲(FAT記錄項多),檢索時FAT表佔用較大的存儲空間(需要將整個FAT表加載到內存)
    • 索引分配:把文件的所有盤塊集中存儲(索引),讀取某個文件時,將文件索引讀取進內存即可

      每個文件擁有一個索引塊,記錄所有盤塊信息,索引分配方式支持直接訪問盤塊,文件較大時,索引分配方式具有明顯優勢

  • 存儲空間管理
    • 空閑表:空閑盤區的分配與內存的分配相似,首次適應算法、循環適應算法等,回收過程也與內存回收類似
    • 空閑鏈表:空閑鏈表法把所有空閑盤區組成一個空閑鏈表,每個鏈表節點存儲空閑盤塊和空閑的數目
    • 位示圖:位示圖維護成本很低,可以非常容易找到空閑盤塊,位示圖使用0/1比特位,佔用空間小

目錄管理

  任何文件或目錄都只有唯一路徑。文件常見的描述信息有:文件標識符、文件類型、文件權限、文件物理地址、文件長度、文件連接計數、文件存取時間、索引節點編號、文件狀態、訪問計數、鏈接指針等。

Linux文件基本操作

Linux目錄

目錄 描述
/bin 存放二進制可執行文件(ls,cat,mkdir),常用的命令都在該目錄下
/etc 存放系統管理和配置文件
/home 存放所有用戶文件的根目錄,使用戶目錄的基點,比如用戶user的主目錄就是/home/user
/usr 用戶存放系統應用程序,比較重要的目錄/usr/local本地系統管理員軟件安裝目錄
/opt 額外安裝的可選應用程序包所放置的位置
/proc 虛擬文件系統目錄,是系統內存的映射,可直接訪問這個目錄來獲取系統信息
/root 系統管理員的主目錄
/sbin 存放二進制可執行文件,只有root才能當問
/dev 用於存放設備文件
/mnt 系統管理員安裝臨時文件系統的安裝點,系統提供這個目錄是讓用戶臨時掛載其他的文件系統
/boot 存放用於系統引導時使用的各種文件
/lib 存放跟文件系統種的程序運行所需要的共享庫及內核模塊
/var 用於存放運行時需要改變數據得文件

Linux文件常用操作

  創建文件:touch file 修改文件:vim file 查看文件:cat file 刪除文件:rm file 創建文件夾:mkdir dir 刪除文件夾:rm dir/ 該方式會提示,不能刪除文件夾 遞歸刪除文件夾:rm -r dir/ 進入文件后,通過ls -al 命令可以查看該文件的文件類型,即第一個字符

Linux文件類型

  Linux的文件類型有:套接字(s)、普通文件(-)、目錄文件(d)、符號鏈接(b、c)、設備文件、FIFO(p)

Linux文件系統

文件系統概覽

  • FAT(File Allocation Table):例如FAT16、FAT32等,微軟Dos/Windows使用的文件系統,使用一張表保存盤塊的消息
  • NTFS(New Technology File System):WindowsNT環境文件系統,NTFS對FAT進行了改進,取代了舊的文件系統
  • EXT(Extended file System):擴展文件系統,這個是Linux的文件系統,EXT2/3/4數字錶示第幾代。
    • Boot Selector:啟動扇區,安裝開機管理程序
    • Block Group:塊組,存儲數據的實際位置

EXT文件系統

  Inode Table是存放文件Inode的地方,每一個文件(目錄)都有一個Inode,是每一個文件(目錄)的索引節點。文件名不是存放在Inode節點上的,而是存放在目錄的Inode節點上,列出目錄文件的時候無需加載文件的Inode。Inode bitmap即Inode的位示圖,記錄已分配的Inode和未分配的Inode。Data block是存放文件內容的地方,每個block都有唯一的編號,文件的block記錄在文件的Inode上。Block bitmap功能與Inode bitmap類似,記錄Data block的使用情況。superblock是記錄整個文件系統相關信息的地方,包括block和Inode的使用情況,以及時間、控制信息等。

  命令 df -T:查看該系統所掛載的磁盤信息,查看文件系統的Inode信息:dumpe2fs 指定某個一設備,如 dumpe2fs /dev/sda2,使用超級管理員權限查看:sudo dumpe2fs /dev/sda2,查看文件的具體信息:stat dumpe2fs.log,文件重命名: mv dumpe2fs.log dumpe2fs.bak.log。Inode編號才是文件的唯一標記,文件名不是文件的唯一標記。

操作系統的設備管理

廣義的IO設備

  對CPU而言,凡是對CPU進行數據輸入的都是輸入設備;對CPU而言,凡是CPU進行數據輸出的都是輸出設備

  • 按使用特性分類
    • 存儲設備:U盤、內存、磁盤等
    • 交互IO設備:鍵盤、显示器、鼠標等
  • 按信息交換的單位分類
    • 塊設備:磁盤、SD卡
    • 字符設備:打印機、Shell終端
  • 按設備的共享屬性分類:獨佔設備、共享設備、虛擬設備
  • 按傳輸速率分類:底速設備、中速設備、高速設備

IO設備的緩衝區

  由於CPU與IO設備的速率不匹配,所以需要IO設備緩衝區,這樣可以減少CPU處理IO請求的頻率,提高CPU與IO設備之間的并行性。專用緩衝區只適用於特定的IO進程,當這樣的IO進程比較多時,對內存的消耗也很大,操作系統劃出可供多個進程使用的公共緩衝區,稱之為緩衝池。

SPOOLing技術

  SPOOLing技術是關於慢速字符設備如何與計算機主機交換信息的一種技術,利用高速共享設備將低速的獨享設備模擬為高速的共享設備。邏輯上,系統為每一個用戶都分配了一台獨立的高速共享設備。SPOOling技術把同步調用低速設備改為異步調用。SPOOLing技術在輸入、輸出之間增加了排隊轉儲環節(輸入井、輸出井),SPOOLing技術負責輸入(出)井與低速設備之間的調度,邏輯上,進程直接與高速設備交互,減少了進程的等待時間。

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

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

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

從輾轉相除法到求逆元,數論算法初體驗_網頁設計

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

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

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

今天是算法和數據結構專題的第22篇文章,我們一起來聊聊輾轉相除法。

輾轉相除法又名歐幾里得算法,是求最大公約數的一種算法,英文縮寫是gcd。所以如果你在大牛的代碼或者是書上看到gcd,要注意,這不是某某黨,而是指的輾轉相除法。

在介紹這個算法之前,我們先來看下最大公約數問題。

暴力解法

這個問題應該很明確了,我們之前數學課上都有講過。給我們紙筆讓我們求都沒有問題,分解因數找下共同的部分,很快就算出來了。但是用代碼實現怎麼做呢?

用代碼實現的話,首先排除分解因數的方法。因為分解因數複雜度太高了,也很容易想明白,既然要分解因數,那麼首先需要獲得一定量的質數吧。有了質數之後還要遍歷質數,將整數一點一點分解,顯然很麻煩,還不如直接暴力了。暴力解法並不複雜,我們直接從1開始遍歷,記錄下來同時能夠整除這兩個數的最大數即可。我們暴力的範圍也不大,從1到n。

很容易寫出代碼:

def gcd(a, b):
    ret = 0
    for i in range(min(a, b)):
        if a % i == 0 and b % i == 0:
            ret = i
    return ret

這個很簡單,也許你可能還會想出一些優化,比如說首先判斷一下a和b之間是否有倍數關係,如果有的話直接就可以得到結果了。再比如說我們i的遍歷範圍其實可以不用到min(a, b),如果a和b沒有倍數關係的話min(a, b) / 2就可以了。這些都是沒有問題的,但是即使加上了這些優化依然改變不了這是一個O(n)算法的本質。

比如說a是1e9,b是1e9-1,毫無疑問這樣的做法會超時。

輾轉相除法

接下來就輪到正主——輾轉相除法出場了,這個算法在《九章算術》當中曾經出現過,叫做更相減損術。不管叫什麼,原理都是一樣的,它的最核心本質是下面這個式子:

\[gcd(a, b) = gcd(b, r), a = bq + r \]

這個式子就是著名的歐幾里得定理,這裏的r可以看成是a對b取余之後的結果,也就是說a和b的最大公約數等於b和r的最大公約數。這樣我們就把a和b的gcd轉移成了b和r,然後我們可以繼續轉移,直到這兩個數之間存在倍數關係的時候就找到了答案。

在我們寫代碼之前,我們先來看一下這個定理的證明。

我們假設u同時整除a和b,顯然這樣的u一定存在,因為u至少可以是1,所以:

\[\begin{aligned} a = su, b = tu \\ r = a – bq = su – tuq = (s – tq) u\\ \end{aligned} \]

所以可以得到u也整除r,同樣我們可以證明能夠整除b和r的整數也可以整除a。我們假設v可以同時整除b和r:

\[\begin{aligned} b = sv, r = tv\\ a = bq + r = svq + tv = v(sq + t) \end{aligned} \]

這樣我們就得到了v也可以整除a。也就是說a和b的每一個因子都是b和r的因子,同樣b和r的每一個因子也是a和b的因子,那麼可以得出a和b的最大公約數就是b和r的最大公約數。

以上就是歐幾里得定理的簡單證明,如果看不懂也沒有關係,我們記住這個定理的內容就可以了。

接下來就是用代碼實現了,我們把這個公式套進遞歸當中非常容易:

def gcd(a, b):
    if a < b:
        a, b = b, a
        
   	if a % b == 0:
        return b
    return gcd(b, a % b)

我們首先判斷了a和b的大小關係,如果a小於b的話,我們就交換它們的值,保證a大於b。如果a和b取模的結果為0,那麼說明a已經是b的倍數了,顯然它們之間的最大公約數就是b。

但其實我們沒有必要判斷a和b的大小,我們假設a小於b,那麼顯然a % b = a,於是會遞歸調用b和a % b,也就是b和a,也就是說算法會自動調整兩者的順序。這麼一來,這個代碼還可以進一步簡化,只需要一行代碼

def gcd(a, b):
    return a if b == 0 else gcd(b, a % b)

所以聽到有人說自己用一行代碼實現了一個算法,不要覺得它在裝逼,有可能他真的寫了一個gcd。

拓展歐幾里得

拓展歐幾里得本質上就是gcd,只是在此基礎上做了一定的拓展,從而來解決不定方程。不定方程就是ax + by = c的方程,方程要有解充要條件是(a, b) | c,也就是說a和b的最大公約數可以整除c

也就是說求解ax + by = gcd(a, b)的解。假如說我們找到了這樣一組解x0和y0,那麼x0 + (b / gcd) * t和y0 – (a / gcd) * t也是方程的解,這裏的t可以取任意整數。

我們代入算一下即可:

\[\begin{aligned} a*(x_0 + (b / gcd) * t) + b*(yo-(a/gcd)*t) \\ a*x_0+ b*y_0 + abt / gcd – abt/gcd = gcd \end{aligned} \]

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

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

\[\begin{aligned} \end{aligned} \]

所以我們求出了這樣的x0和y0之後就相當於求出了無數組解,那麼這個x0和y0怎麼求呢,這就需要用到gcd算法了。

我們觀察一下gcd算法的遞歸代碼,可以發現算法的終止條件是a=gcd,b=0。對於這樣的a和b來說,我們已經找到了一組解使得ax+by=gcd,比如很明顯,x=1,y=0。實際上y可以為任何值,因為b=0。

我們回到遞歸的上一層的a和b,假設我們已經求出了b和a%b的最大公約數,並且求出了一組解x0和y0。使得b*x0 + (a%b)* y0 = gcd。那麼我們能不能倒推得到a和b時候的解呢?

因為a % b = a – (a/b)*b,這裏的/是整除計算的意思,我們代入:

\[\begin{aligned} gcd &= b*x_0 + (a\%b)*y_0 \\ &= b*x_0 + (a – (a/b)*b)*y_0 \\ &= b*x_0 + a*y_0 – (a/b)*b*y_0 \\ &= a*y_0 + b*(x_0 – (a/b)*b*y_0) \end{aligned} \]

顯然對於a和b來說,它的一組解就是y0和x0 – (a/b)*b*y0,我們把這幾行計算加在代碼當中即可,非常簡單:

def exgcd(a, b, x=1, y=0):
    # 當b=0的時候return
    if b == 0:
        return a, x, y
    # 遞歸調用,獲取b, a%b時的gcd與通項解
    gcd, x, y = exgcd(b, a%b, x, y)
    # 代入,得到新的通項解
    x, y = y, x - a//b*y
    return gcd, x, y

這裏我建議大家不要死記代碼,都去推導一下遞歸的這個推導公式。這個公式搞明白了,即使代碼記不住也沒有關係,後面臨時用到的時候再推導也可以。不然的話,即使背下來了代碼也不記得什麼意思,如果碰到的場景稍微變動一下,可能還是做不出來。

逆元與解逆元

拓展歐幾里得算法我們理解了,但是好像看不出來它到底有什麼用。一般情況下我們也碰不到讓我們計算通解的情況,但其實是有用的,用的最多的一個功能就是計算逆元

在解釋逆元之前先來看一個問題,我們有兩個數a和b,和一個模底數p。我們可以得到(a + b) % p = (a%p + b%p)%p,也可以得到 (a – b)%p = (a%p – b%p)%p。甚至還可以得到 (a*b)% p =(a%p * b%p) %p,這些都是比較明確的,但是(a / b) % p = (a % p / b % p) % p,這個式子成立嗎?

最後的式子是不成立的,因為模數沒有除法的傳遞性,我們可以很方便舉出反例。比如a是20, b是10,p是4,(a/b)%p=2,而(a %p / b%p) % p = 0。

這就導致了一個問題,假如說我們在一連串計算當中,由於最終的結果特別大,我們無法存儲精確的值,希望存儲它關於一個模底數取模之後的結果。但是我們的計算當中又涉及除法,這個時候應該怎麼辦?

這個時候就需要用到逆元了,逆元也叫做數論倒數。它其實起到一個和倒數類似的效果,假設a關於模底數p的逆元是x,那麼可以得到:ax = 1 (mod p)

所以我們想要算 (a / b) % p,可以先求出b的逆元假設是inv(b),然後轉化成(a%p * inv(b)%p)%p。

這個逆元顯然不會從天上掉下來,需要我們設計算法去求出來,這個用來求的算法就用到拓展歐幾里得,我們下面來看一下推導過程。

假設a和b互質,那麼gcd(a, b) = 1,代入:

\[\begin{aligned} ax + by &= 1\\ ax \% b + by \% b &= 1 \% b\\ ax\%b &= 1\%b\\ ax &= 1 \pmod b \end{aligned} \]

所以x是a關於b的逆元,反之可以證明y是b關於a的逆元。

這麼計算是有前提的,就是a和b互質,也就是說a和b的最大公約數為1。否則的話這個計算是不成立的,也就是說a沒有逆元。那麼整個求解逆元的過程其實就是調用拓展歐幾里得的過程,把問題說清楚花了很多筆墨,但是寫成代碼只有兩三行:

def cal_inv(a, m):
    gcd, x, y = exgcd(a, m)
    # 如果gcd不為1,那麼說明沒有逆元,返回-1
    return (x % m + m) % m if gcd == 1 else -1

在return的時候我們對x的值進行了縮放,這是因為x有可能得到的是負數,我們把它縮放到0到m的範圍當中。

逆元的求解方法除了拓展歐幾里得之外,還有一種算法,就是利用費馬小定理。根據費馬小定理,在m為質數的時候,可以得到

\[a^{m-1}\equiv 1 \pmod m \]

等式兩邊同時除以a,也就是乘上a的逆元,可以得到:

\[a^{m-2} \equiv inv(a) \pmod m \]

也就是說我們求出\(a^{m-2}\)然後再對m取模就得到了a的逆元,我們使用快速冪可以很方便地求出來。但是這個只有m為質數的時候才可以使用。

總結

今天我們聊了歐幾里得定理聊了輾轉相除法還聊了拓展歐幾里得和求解逆元,雖然這些內容單獨來看並不難,合在一篇文章當中量還是不小的。這些算法底層的基礎知識是數論,對於沒有參加過競賽的同學來說可能有些陌生,但是它也是算法領域一個很重要的分支。

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

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

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

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

曹工說mini-dubbo(2)–分析eureka client源碼,想辦法把我們的服務提供者註冊到eureka server(上)_網頁設計

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

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

前言

eureka是spring cloud Netflix技術體系中的重要組件,主要完成服務註冊和發現的功能;那現在有個問題,我們自己寫的rpc服務,如果為了保證足夠的開放性和功能完善性,那肯定要支持各種註冊中心。目前我們只支持redis註冊中心,即服務提供者,在啟動的時候,將自身的ip+端口信息寫入到redis,那,我們是否註冊到 eureka中呢?

這個想法可行嗎?可行。eureka client 和eureka server間,無非是網絡通信,既然是網絡通信,那就有網絡協議,那我們的應用,只要遵照eureka server的協議來,就可以接入。

另外,eureka server沒有採用spring mvc來實現,而是採用了jersey框架,這個框架啥意思呢,可以理解為對Restful的實現。我從網上找了一段(https://www.jianshu.com/p/88f97b90963c):

SpringMVC在開發REST應用時,是不支持JSR311/JSR339標準的。如果想要按照標準行事,最常用的實現了這兩個標準的框架就是Jersey和CxF了。但是,因為Jersey是最早的實現,也是JSR311參考的主要對象,所以,可以說Jersey就是事實上的標準(類似Hibernate是JPA的事實上的標準),也是現在使用最為廣泛的REST開發框架之一。

因為eureka server採用了jersey,所以eureka client最終也是使用了配套的jersey client來和服務端通信。

所以,eureka client,裏面其實依賴了一堆jersey的包:

注意,上面的jersey-client、jersey-core等包,其group id都是這樣的:

    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-client</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

但是,不知道為啥,eureka client中,最終並沒有完全使用jersey-client,而是使用了

    <dependency>
      <groupId>com.sun.jersey.contribs</groupId>
      <artifactId>jersey-apache-client4</artifactId>
      <version>1.19.1</version>
      <scope>runtime</scope>
    </dependency>

這個包,內部引入了:

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.1.1</version>
        </dependency>

這個包,你可以簡單理解為,jersey-client變成了一個接口,jersey-apache-client4是它的一個實現,實現里,用了httpClient去實現。

httpClient,沒有幾個java同學不知道吧?這麼做是可行的,因為你最終通信,還是http,不管你服務端框架,是jersey、還是spring mvc、甚至以前的struts,這都不重要。

所以,大家在下面的源碼中看到jersey的時候,腦海里可以有這麼一張圖。從上層到底層的接口,分別是:

CloudEurekaClient
       ...
DiscoveryClient  
	...
EurekaClient
	...
JerseyClient
	...
HttpClient	

在此之前,我們還是先分析下eureka client 註冊到eureka server的源碼。

源碼環境

minidubbo代碼和相關博文在:
曹工說mini-dubbo(1)–為了實踐動態代理,我寫了個簡單的rpc框架
https://gitee.com/ckl111/mini-dubbo

代碼很簡單,不過還是給個代碼鏈接吧:

https://gitee.com/ckl111/all-simple-demo-in-work-1/tree/master/eureka-client

主要就是在pom.xml中,引入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然後啟動類:

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

源碼分析

spring.factory支持自動配置

因為前面的pom,引入了如下jar包:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-netflix-eureka-client</artifactId>
		</dependency>

該jar包的META-INF\spring.factories中,有如下幾行:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

我們看到,key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是逗號分割的列表,這裏面都是需要被自動裝配的配置類,其中,我們看第三行的:

org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration

這個類,是自動裝配的配置類,我們可以簡單一覽:

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
		CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
public class EurekaClientAutoConfiguration {

裏面一堆@ConditionalOn***,主要是看該配置類是否生效。

我們不管,這裏條件是滿足的,所以,看具體java文件里有什麼要裝配的內容,裏面內容較多,我們關注我們需要關注的:

		@Bean(destroyMethod = "shutdown")
		@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
		@Lazy
		public EurekaClient eurekaClient(ApplicationInfoManager manager,
				EurekaClientConfig config, EurekaInstanceConfig instance,
				@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
			ApplicationInfoManager appManager;
			if (AopUtils.isAopProxy(manager)) {
				appManager = ProxyUtils.getTargetObject(manager);
			}
			else {
				appManager = manager;
			}
            // 1
			CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
					config, this.optionalArgs, this.context);
			cloudEurekaClient.registerHealthCheck(healthCheckHandler);
			return cloudEurekaClient;
		}

這裡會自動裝配一個EurekaClient類型的bean,(從返回值可以看出來),而具體的類型呢,從上面的1處,可以看出,具體類型是CloudEurekaClient。

所以,我們開始看1處,這個CloudEurekaClient是怎麼new出來的。

CloudEurekaClient的創建

先看看其繼承結構:

我們這個CloudEurekaClient,位於spring-cloud-netflix-eureka-client-2.1.5.RELEASE包。

而其父類DiscoveryClient和接口EurekaClient,位於eureka-client-1.9.13

大致能分析出,CloudEurekaClient的底層實現是eureka,其本身,是一個膠水,集成 spring 和 Netflix。

CloudEurekaClient的構造函數

	public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
			EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args,
			ApplicationEventPublisher publisher) {
        // 1
		super(applicationInfoManager, config, args);
        // 2
		this.applicationInfoManager = applicationInfoManager;
		this.publisher = publisher;
		this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,
				"eurekaTransport");
		ReflectionUtils.makeAccessible(this.eurekaTransportField);
	}

我們看1處,調用了父類的構造函數;2處下面的幾行,主要是對本類中的幾個field進行賦值,這幾個字段,我們不關心,所以,直接看父類的構造函數吧。

DiscoveryClient的構造函數

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
        this(applicationInfoManager, config, args, ResolverUtils::randomize);
    }

    public DiscoveryClient(ApplicationInfoManager applicationInfoManager, final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, EndpointRandomizer randomizer) {
        // 1
        this(applicationInfoManager, config, args, null, randomizer);
    }

上面兩個,都是重載。1處調用的,我們接下來會重點分析。

步驟1:一堆field賦值

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                    Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
    	// 0	
        if (args != null) {
            this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
            this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
            this.eventListeners.addAll(args.getEventListeners());
            this.preRegistrationHandler = args.preRegistrationHandler;
        } else {
            this.healthCheckCallbackProvider = null;
            this.healthCheckHandlerProvider = null;
            this.preRegistrationHandler = null;
        }
        
        this.applicationInfoManager = applicationInfoManager;
        InstanceInfo myInfo = applicationInfoManager.getInfo();
		// 1
        clientConfig = config;
        staticClientConfig = clientConfig;
        transportConfig = config.getTransportConfig();
        instanceInfo = myInfo;
        if (myInfo != null) {
            appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
        } else {
            logger.warn("Setting instanceInfo to a passed in null value");
        }
		...

這一堆都是根據入數,來對類中的field進行賦值。比如

0處,主要是一些健康檢查的東西;1處,config類型為 com.netflix.discovery.EurekaClientConfig,這裏主要是eureka client的一些配置,比如我們在yml中配置了eureka.client.*之類的,就會到這裏。

步驟2:判斷是否要獲取eureka server中的服務提供者信息

	// 1
	if (config.shouldFetchRegistry()) {
            this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

1處,可以看出來,是根據config中的shouldFetchRegistry進行判斷,是否要去獲取eureka server。

然後進行了一些監控指標的初始化。

步驟3:判斷是否要註冊到eureka server

    	// 1    
		if (config.shouldRegisterWithEureka()) {
            this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
        } else {
            this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
        }

同上。

步驟4:如果既不註冊,也不獲取,則處理基本結束

		// 1
		if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
            logger.info("Client configured to neither register nor query for data.");
            scheduler = null;
            heartbeatExecutor = null;
            cacheRefreshExecutor = null;
            eurekaTransport = null;
            instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion());

            DiscoveryManager.getInstance().setDiscoveryClient(this);
            DiscoveryManager.getInstance().setEurekaClientConfig(config);
			// 2
            return;  // no need to setup up an network tasks and we are done
        }
  • 1處,既不註冊,也不從eureka server獲取
  • 2處,直接結束

步驟5:定義三個線程池

            //1 default size of 2 - 1 each for heartbeat and cacheRefresh
            scheduler = Executors.newScheduledThreadPool(2,
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-%d")
                            .setDaemon(true)
                            .build());
			// 2
            heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
			// 3 
			cacheRefreshExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff
  • 1處,定義一個用於服務提供者信息的緩存刷新的定時線程池
  • 2處,定義一個心跳線程池
  • 3處,這個看起來也是用於緩存刷新的

步驟6:創建eurekaTransport對象

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
eurekaTransport = new EurekaTransport();
// 2
scheduleServerEndpointTask(eurekaTransport, args);
  • 1處,eurekaTransport是一個field,該類主要封裝了幾個後續通信要使用的底層client。

    
        private static final class EurekaTransport {
            private ClosableResolver bootstrapResolver;
            private TransportClientFactory transportClientFactory;
    		// 1.1
            private EurekaHttpClient registrationClient;
            private EurekaHttpClientFactory registrationClientFactory;
    		// 1.2
            private EurekaHttpClient queryClient;
            private EurekaHttpClientFactory queryClientFactory;
    

    1.1處,這個應該是註冊用的,也是我們需要的;

    1.2處,應該是查詢信息用的。

  • 調用了當前類的方法scheduleServerEndpointTask,且把eurekaTransport傳入了

步驟7:schedule周期任務

創建抽象工廠

因我們只是new了eurekaTransport,沒有對其field進行任何賦值,所以,這個scheduleServerEndpointTask總,有個地方對其field進行賦值。

com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
// 1
TransportClientFactories transportClientFactories =new Jersey1TransportClientFactories();

// 2
eurekaTransport.transportClientFactory = transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo(), sslContext, hostnameVerifier)

  • 1處,就是new了一個抽象工廠,抽象工廠,我個人理解是工廠的工廠,其產出的東西,不是直接的最終對象,而是另一種工廠。

    TransportClientFactories 是一個接口,主要包含了如下方法:

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

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

    	public TransportClientFactory newTransportClientFactory(
            	final EurekaClientConfig clientConfig,
            	final Collection<F> additionalFilters,
                final InstanceInfo myInstanceInfo,
                final Optional<SSLContext> sslContext,
                final Optional<HostnameVerifier> hostnameVerifier);
    

    主要5個參數,排除掉最後的倒數2個,可選參數,剩3個。分別是:eurekaClient的配置bean,額外的filter集合,當前實例信息。

具體工廠的職責

  • 2處,就是利用1處創建的抽象工廠,來生成我們需要的工廠。

    這裏,我們可以先看看,最終我們需要的工廠,是什麼樣的。

    /**
     * A low level client factory interface. Not advised to be used by top level consumers.
     *
     * @author David Liu
     */
    public interface TransportClientFactory {
    
        EurekaHttpClient newClient(EurekaEndpoint serviceUrl);
    
        void shutdown();
    
    }
    

    newClient這個方法,聽名字,就是一個創建客戶端的,創建客戶端,需要什麼參數呢?總得知道要連接到哪個eureka server服務器吧,服務器地址是啥吧?沒錯,參數EurekaEndpoint serviceUrl可以給我們提供需要的這些:

    package com.netflix.discovery.shared.resolver;
    
    public interface EurekaEndpoint extends Comparable<Object> {
    	// 1
        String getServiceUrl();
    	// 2
        String getNetworkAddress();
    	// 3
        int getPort();
    
        boolean isSecure();
    
        String getRelativeUri();
    
    }
    
    
    • 1處,獲取url
    • 2處,獲取網絡地址
    • 3處,獲取端口

    基本對於我們一個客戶端來說,需要的參數就這些。

    說完了newClient的參數,再來看看響應:

    
    /**
     * Low level Eureka HTTP client API.
     *
     * @author Tomasz Bak
     */
    public interface EurekaHttpClient {
    
        EurekaHttpResponse<Void> register(InstanceInfo info);
    
        EurekaHttpResponse<Void> cancel(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus);
    
        EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info);
    
        EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info);
    
        EurekaHttpResponse<Applications> getApplications(String... regions);
    
        EurekaHttpResponse<Applications> getDelta(String... regions);
    
        EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id);
    
        EurekaHttpResponse<InstanceInfo> getInstance(String id);
    
        void shutdown();
    }
    

    看到了嗎,各種註冊、取消、發送心跳、狀態更新啥的,這幾本涵蓋了eureka client的所有操作了,沒錯,我們就是需要這麼個東西。

創建具體工廠

看完了我們需要的工廠的功能,我們馬上來看看這麼厲害的工廠怎麼創建出來?

com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories#newTransportClientFactory(...)
        
	@Override
    public TransportClientFactory newTransportClientFactory(
        	EurekaClientConfig clientConfig,
            Collection<ClientFilter> additionalFilters,
        	InstanceInfo myInstanceInfo,
        	Optional<SSLContext> sslContext,
        	Optional<HostnameVerifier> hostnameVerifier) {
    	// 2.1
        final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
                clientConfig,
                additionalFilters,
                myInstanceInfo,
                new EurekaClientIdentity(myInstanceInfo.getIPAddr()),
                sslContext,
                hostnameVerifier
        );
        // 2.2
        final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);
		// 2.3
        return new TransportClientFactory() {
            @Override
            public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
                return metricsFactory.newClient(serviceUrl);
            }

            @Override
            public void shutdown() {
                metricsFactory.shutdown();
                jerseyFactory.shutdown();
            }
        };
    }
  • 2.1處,調用JerseyEurekaHttpClientFactory的create 靜態方法,生成了一個工廠
  • 2.2處,對生成的工廠,進行了包裝,看名稱,應該是包裝了統計相關信息。
  • 2.3處,對2.2處生成的工廠,用匿名內部類進行了包裝,調用匿名內部類的newClient時,直接代理給了metricsFactory;而shutdown方法,則主要是關閉 metricsFactory 和 jerseyFactory 工廠。

所以,我們現在要看看,2.1處,是怎麼創建工廠的。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory#create
    
    public static JerseyEurekaHttpClientFactory create(
    	EurekaClientConfig clientConfig,
        Collection<ClientFilter> additionalFilters,
    	InstanceInfo myInstanceInfo,                                                       		 AbstractEurekaIdentity clientIdentity) {
        // 1
    	boolean useExperimental = "true".equals(clientConfig.getExperimental("JerseyEurekaHttpClientFactory.useNewBuilder"));
		// 2
        JerseyEurekaHttpClientFactoryBuilder clientBuilder = (useExperimental ? experimentalBuilder() : newBuilder())
                .withAdditionalFilters(additionalFilters)
                .withMyInstanceInfo(myInstanceInfo)
                .withUserAgent("Java-EurekaClient")
                .withClientConfig(clientConfig)
                .withClientIdentity(clientIdentity);
    	// 3
        clientBuilder.withClientName("DiscoveryClient-HTTPClient");
		// 4
        return clientBuilder.build();
    }
  • 1處,砍斷是否要使用實驗性的builder
  • 2處,創建對應的builder,並把我們的參數,通過with*方法,設置進去
  • 3處,設置客戶端名稱
  • 4處,生成客戶端工廠
com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#build

    
        @Override
        public JerseyEurekaHttpClientFactory build() {
    		// 1
            Map<String, String> additionalHeaders = new HashMap<>();
    		// 2
            if (allowRedirect) {
                additionalHeaders.put(HTTP_X_DISCOVERY_ALLOW_REDIRECT, "true");
            }
            if (EurekaAccept.compact == eurekaAccept) {
                additionalHeaders.put(EurekaAccept.HTTP_X_EUREKA_ACCEPT, eurekaAccept.name());
            }
			
            // 3
            return buildLegacy(additionalHeaders, systemSSL);
        }

這裏就是弄了個hashmap,設置了幾個header進去,然後3處,調用buildLegacy。

com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory.JerseyEurekaHttpClientFactoryBuilder#buildLegacy
    
        private JerseyEurekaHttpClientFactory buildLegacy(Map<String, String> additionalHeaders, boolean systemSSL) {
    		// 1
            EurekaJerseyClientBuilder clientBuilder = new EurekaJerseyClientBuilder()
                    .withClientName(clientName)
                    .withUserAgent("Java-EurekaClient")
                    .withConnectionTimeout(connectionTimeout)
                    .withReadTimeout(readTimeout)
                    .withMaxConnectionsPerHost(maxConnectionsPerHost)
                    .withMaxTotalConnections(maxTotalConnections)
                    .withConnectionIdleTimeout((int) connectionIdleTimeout)
                    .withEncoderWrapper(encoderWrapper)
                    .withDecoderWrapper(decoderWrapper);
			...
            
			// 2
            EurekaJerseyClient jerseyClient = clientBuilder.build();
    		// 3
            ApacheHttpClient4 discoveryApacheClient = jerseyClient.getClient();
            addFilters(discoveryApacheClient);
			// 4
            return new JerseyEurekaHttpClientFactory(jerseyClient, additionalHeaders);
        }
  • 1處,通過我們傳入的一些參數,以及該類自身的一些field,比如connectionTimeout、readTimeout、maxTotalConnections、maxConnectionsPerHost這些,構造一個builder。

    這些參數,已經看出來,是網絡通信所需要的東西了

  • 2處,通過1處的builder,調用build,拿到了EurekaJerseyClient類型的對象,可以說,這裏其實是已經把客戶端構造好了。也就是說,在構造這個工廠的過程中,其實已經在生成對應的產品了

  • 3處,對2處拿到的客戶端,做一些處理

  • 4處,將2處拿到的客戶端,封裝到了工廠的一些field中,後續調用工廠生產產品的時候,直接從field中取就行了。

        public JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient, Map<String, String> additionalHeaders) {
            this(jerseyClient, null, -1, additionalHeaders);
        }
    	private JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient,
                                              ApacheHttpClient4 apacheClient,
                                              long connectionIdleTimeout,
                                              Map<String, String> additionalHeaders) {
            this.jerseyClient = jerseyClient;
            this.apacheClient = jerseyClient != null ? jerseyClient.getClient() : apacheClient;
            this.additionalHeaders = additionalHeaders;
        }
    

所以,我們的重點,要放在2處的build身上。

	com.netflix.discovery.shared.transport.jersey.EurekaJerseyClientImpl.EurekaJerseyClientBuilder#build
	public EurekaJerseyClient build() {
            MyDefaultApacheHttpClient4Config config = new MyDefaultApacheHttpClient4Config();
            try {
                // 1
                return new EurekaJerseyClientImpl(connectionTimeout, readTimeout, connectionIdleTimeout, config);
            } catch (Throwable e) {
                throw new RuntimeException("Cannot create Jersey client ", e);
            }
        }

接下來看1處:

public EurekaJerseyClientImpl(int connectionTimeout, int readTimeout, final int connectionIdleTimeout,ClientConfig clientConfig) {
        try {
            jerseyClientConfig = clientConfig;
            // 1
            apacheHttpClient = ApacheHttpClient4.create(jerseyClientConfig);
            // 2
            HttpParams params = apacheHttpClient.getClientHandler().getHttpClient().getParams();

            HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
            HttpConnectionParams.setSoTimeout(params, readTimeout);
			
        } catch (Throwable e) {
            throw new RuntimeException("Cannot create Jersey client", e);
        }
    }
  • 1處,創建com.sun.jersey.client.apache4.ApacheHttpClient4類型的對象

    該類型,就位於:

        <dependency>
          <groupId>com.sun.jersey.contribs</groupId>
          <artifactId>jersey-apache-client4</artifactId>
          <version>1.19.1</version>
          <scope>runtime</scope>
        </dependency>
    
    
        public static ApacheHttpClient4 create(final ClientConfig cc) {
            return new ApacheHttpClient4(createDefaultClientHandler(cc), cc);
        }
    

    這裏的createDefaultClientHandler(cc),裏面會去創建HttpClient。

    private static ApacheHttpClient4Handler createDefaultClientHandler(final ClientConfig cc) {
    		...
    
    		// 1
            final DefaultHttpClient client = new DefaultHttpClient(
                    (ClientConnectionManager)connectionManager,
                    (HttpParams)httpParams
            );
    
            ...
    		
            return new ApacheHttpClient4Handler(client, cookieStore, preemptiveBasicAuth);
        }
    

    這裏面細節省略了部分,主要就是1處,創建了HttpClient,這個就是平時我們用來發http請求的那個。

  • 2處,設置一些參數,這裏的HttpParams,從哪兒取出來的?apacheHttpClient.getClientHandler().getHttpClient()。這裏取到的,已經是HttpClient了。

    到此為止,我們可以看看httpParams中有哪些header:

在具體工廠基礎上,對註冊用的工廠進行封裝

        com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
        // 1    
		if (clientConfig.shouldRegisterWithEureka()) {
            EurekaHttpClientFactory newRegistrationClientFactory = null;
            EurekaHttpClient newRegistrationClient = null;
            // 2
            newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                transportConfig
            );
            // 3
            newRegistrationClient = newRegistrationClientFactory.newClient();
            // 4
            eurekaTransport.registrationClientFactory = newRegistrationClientFactory;
            eurekaTransport.registrationClient = newRegistrationClient;
        }

我們前面的n步,已經把通信用的客戶端,及對應的工廠,都已經創建出來了,為啥這裏又要創建什麼工廠。

簡單來說,前面的工廠,造出來的客戶端,通信是沒問題了;但是,你通信失敗了,要重試嗎,重試的話,換哪一台呢?你每次通信是成功,還是失敗,還是超時,需要統計嗎?一個生產級的框架,是要有這些功能的。

所以,這裏主要是進行一些上層的封裝。

ok,繼續分析上面的代碼。

  • 1處,判斷是否要註冊到eureka
  • 2處,生成一個工廠,該工廠負責生產:註冊用的客戶端
  • 3處,使用2處拿到的工廠,創建註冊用的客戶端
  • 4處,把3處拿到的客戶端,存儲到eurekaTransport的field中。

繼續深入2處。

    com.netflix.discovery.shared.transport.EurekaHttpClients#canonicalClientFactory
	static EurekaHttpClientFactory canonicalClientFactory(
        final String name,
        final EurekaTransportConfig transportConfig,
        final ClusterResolver<EurekaEndpoint> clusterResolver,
        final TransportClientFactory transportClientFactory) {
		// 1
        return new EurekaHttpClientFactory() {
            // 2
            @Override
            public EurekaHttpClient newClient() {
                // 3
                return new SessionedEurekaHttpClient(
                        name,
                        RetryableEurekaHttpClient.createFactory(...),
                        transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000
                );
            }

            @Override
            public void shutdown() {
                wrapClosable(clusterResolver).shutdown();
            }
        };
    }
  • 1處,返回了一個工廠對象
  • 2處,工廠里重寫了newClient
  • 3處,返回了一個包裝過的EurekaClient。

可以看下這裏返回的SessionedEurekaHttpClient類。

這裏就是裝飾器模式,對enreka進行了層層封裝,和 java 的 io 流那樣理解就對了。

在具體工廠基礎上,對查詢用的工廠進行封裝

		// 1
		if (clientConfig.shouldFetchRegistry()) {
            EurekaHttpClientFactory newQueryClientFactory = null;
            EurekaHttpClient newQueryClient = null;
            // 2
            newQueryClientFactory = EurekaHttpClients.queryClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                clientConfig,
                transportConfig,
                applicationInfoManager.getInfo(),
                applicationsSource,
                endpointRandomizer
            );
            // 3
            newQueryClient = newQueryClientFactory.newClient();
            eurekaTransport.queryClientFactory = newQueryClientFactory;
            eurekaTransport.queryClient = newQueryClient;
        }

這裏的代碼,和上面基本相似。只不過,這裡是給查詢用的,所謂查詢,就是去eureka server獲取信息,比如服務提供者列表啥的。

  • 1處,判斷是否要去eureka server獲取
  • 2處,創建查詢用的工廠
  • 3處,利用2處拿到的工廠,創建查詢客戶端

步驟8:去eureka server獲取服務提供者信息

我們終於把步驟7講完了,實在有點長。

com.netflix.discovery.DiscoveryClient#DiscoveryClient(...)
    
// 1    
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
    // 2
    fetchRegistryFromBackup();
}

這裏1處,就是判斷要不要去獲取,如果要的話,就調用fetchRegistry(false)

2處,如果1處沒取到,則要從backup地方去取。這塊可以自己定製backup策略。

註冊到eureka server

        if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
            // 1
            if (!register() ) {
                throw new IllegalStateException("Registration error at startup. Invalid server response.");
            }
        }

這裡會判斷是否要註冊,是否要在初始化的時候註冊,如果要的話,進入1處,進行註冊。

初始化周期執行的任務

        // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
        initScheduledTasks();

看這裏註釋,初始化的任務包括:集群解析、心跳、實例信息註冊、周期從eureka server獲取信息等。

周期任務:獲取服務提供者信息

if (clientConfig.shouldFetchRegistry()) {
            // registry cache refresh timer
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ),
                    registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }

默認30s一次。

周期任務:定時發心跳,向eureka server進行renew

            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);

這個也是30s。

心跳包,基本就是個put請求,裏面攜帶了2個參數。

@Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {
        String urlPath = "apps/" + appName + '/' + id;
        ClientResponse response = null;
        try {
            WebResource webResource = jerseyClient.resource(serviceUrl)
                    .path(urlPath)
                    .queryParam("status", info.getStatus().toString())
                    .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());

周期任務:InstanceInfoReplicator

這個任務,默認也是30s執行一次。

            instanceInfoReplicator = new InstanceInfoReplicator(
                    this,
                    instanceInfo,
                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                    2); // burstSize

這個任務,其實現了runnable,註釋如下:


/**
 * A task for updating and replicating the local instanceinfo to the remote server. Properties of this task are:
 * - 1 configured with a single update thread to guarantee sequential update to the remote server
 * - 2 update tasks can be scheduled on-demand via onDemandUpdate()
 * - 3 task processing is rate limited by burstSize
 * - 4 a new update task is always scheduled automatically after an earlier update task.  However if an on-demand task is started, the scheduled automatic update task is discarded (and a new one will be scheduled after the new
 *   on-demand update).
 *
 *   @author dliu
 */
class InstanceInfoReplicator implements Runnable 
  • 1處,配置了一個單線程,保證向遠程eureka server,順序更新
  • 2處,通過本類的onDemandUpdate,可以強行插入一個任務,而無需通過定時執行
  • 3處,限流相關
  • 4處,執行完一個周期任務后,馬上會給自己安排下一個周期任務

其run方法:

    public void run() {
        try {
            // 1
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                // 2
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        }finally {
            // 3
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }
  • 1處,刷新實例信息
  • 2處,如果有需要的話,向eureka server進行註冊
  • 3處,調度下一次任務

初始化結束

基本,這個CloudEurekaClient構造就結束了,後續就依靠其開啟的一堆定時任務去進行工作。

總結

eureka client的初始化就講了這麼多,註冊還沒講,留帶下一講吧。

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

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

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

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上_網頁設計

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

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

Apple 在上個月推出了以 ARM 為基礎的 M1 晶片,現在應該全球的開發者都在日以繼夜地努力將自家的應用程導入這個新平台上,對於雙系統用戶來說,還是最關心 Windows 10 到底什麼時候可以支援 M1 晶片。日前 Microsoft 與 Parallels 開展合作,盡量讓大家不用久等就能享用。

Microsoft 與 Parallels 合作,要將 Windows 10 帶到 M1 版 Mac 上

對雙系統用戶來說,Parallels 絕對不陌生,該公司已經開始開發自己的軟體,要將 Windows 10 帶到 Apple Silicon 的世界,在不久前該公司才剛發布了第一個測試預覽版本,讓用戶能夠盡早試用該產品。事實證明,Parallels 並不是唯一一個在這方面使力的人,雖然不清楚兩家公司的合作方式為何,但 Microsoft 旗下 OneDrive 副總裁 Omar Shahine 在 Twitter 上面讚揚了此一合作。

Dang this is amazing! Windows 10 ARM running on MacBook Air M1. Great performance and battery life! Thanks @ParallelsMac and @Microsoft for releasing the ARM build of Windows! pic.twitter.com/BR8vZhJV7V

— Omar Shahine (@OmarShahine) December 23, 2020

有趣的是,在不久前,Apple 的高層主管曾表示將 Windows 引進 Apple Silicon 是僅有 Microsoft 自己能夠做決定,在彼時,Apple 軟體工程高級副總裁 Craig Federighi 曾表示 Apple 擁有 Microsoft 能做到這點的核心技術,可以直接運行 ARM 版本的 Windows,但又能反過來支援 X86 的應用程式,但這必須由 Microsoft 做出決定,將該技術授權給用戶在 M1 版 Mac 設備上運行。

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

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

於此同時,Apple Silicon 世界正在持續壯大,不久前也有傳聞 Microsoft 也正在為新晶片的軟體最佳化投入大量資金,Office、Teams 與其他 Microsoft 應用程式正陸陸續續開放支援 M1 晶片,以求一切都能在新的 Apple 設備上盡可能地平穩運作。

◎資料來源:SoftPedia

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

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

Caviar 推出奢侈黄金版 AirPods Max 、PlayStation 5 ,前者要價 300 萬元起_網頁設計

1{icon} {views}

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

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

月初我們才剛分享過 CAVIAR 這間來自俄羅斯的客製化品牌的 iPhone 12 Pro 鈦合金潛行款,隨著今年下半年兩款別具話題的產品 AirPods Max 與 PlayStation 5(PS5)開賣後, CAVIAR 這次也將腦筋動到了這兩款產品上。
一如既往, CAVIAR 選用了黃金和鱷魚皮作為客製化版本的材質,當然價格也不是一般消費者能負擔得起的程度,光是相對便宜的黃金版 AirPods Max 就要價約 300 萬元新台幣。

Caviar 推出奢侈黄金版 AirPods Max 、PlayStation 5 ,前者要價 300 萬元起

Caviar 近期宣布將於 2021 年推出度有純金的 AirPods Max ,售價高達 108,000 美元(約合新台幣 304.2 萬元)。據悉這款 AirPods Max 黃金版的耳罩部分將原本的鋁金屬材質改為 18K 金打造,頭帶部分則以鱷魚皮材質取代原本的網狀材質。

CAVIAR 將推出掰色和黑色兩種顏色的鱷魚皮搭配以黃金為主體的 AirPods Max 。雖然 CAVIAR 官方未公布黃金版本的 AirPods Max 機身重量有多重,不過回顧已經不算輕盈的標準版 AirPods Max  重量就有 384.8 公克,黃金版本的重量肯定沈重不少。

耳罩部分包括控制按鈕和旋鈕都以鍍金處理,更提升了整體的奢華感:

價格方面, AirPods Max 黃金版建議售價為 108,000 美元(約合新台幣 304.2 萬元),大約可等同 164 組標準版 AirPods Max 的價位。

與 AirPods Max 建議售價相當的 PlayStation 5 ,也同樣在 CAVIAR 這次黃金版客製化的名單中,黃金版 PS5 一樣採用 18K 黃金材質打造,總共使用了大約 20 公斤的黃金:

與以往 CAVIAR 客製化產品相同, PS5 黃金版主要在外殼上進行重新設計,不僅在機身換上兩塊 3D 打造的 18K 純金實心外殼:

CAVIAR 設計師表示這款 Golden Rock 的 PS5 靈感源自於金礦石的獨特幾何形狀和優美優美的岩石輪廓:

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

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

除此之外,在 Dual Sense 遊戲控制器除了黃金還有加入鱷魚皮等材質:

PS5 黃金版目前 CAVIAR 尚未公開價格,不過光是以黃金價格來估算就能推測他價格將非常驚人。

圖片/消息來源:CAVIAR

延伸閱讀:
iOS 應用小技巧:自動停止播放影片/音樂,睡前喜愛滑手機的人都必學!

Caviar 發表 iPhone 12 Pro 鈦合金「潛行款」,取消所有相機還加三倍價賣

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

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

為什麼國產品牌SUV能贏合資品牌?你買車會選誰?_網頁設計

1{icon} {views}

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

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

從是否引入全新車型,合資品牌內部便要經過長時間的項目討論。出於商業上的原因,合資品牌的外資方對於新車型的引入,是持極其謹慎的態度的。所以,儘管合資品牌的中方人員已經意識到這一問題,但往往也是處於干著急的情況,畢竟中方在大部分的情況下,是沒有獨立開發新車型的權力的。

根據乘聯會公布的數據显示,2016年上半年,國內SUV銷量共計391.5萬輛。而中國自主品牌以56.6%的絕對性優勢,成功稱霸了這個時下國內最為火熱的汽車細分市場。在上半年SUV銷量前10排名中,自主品牌共佔到了6個席位。這就不禁讓人有一個疑問:合資品牌SUV為什麼會一敗塗地?

SUV市場的火熱

是合資品牌做夢都沒有想到的

2003年,國內的SUV銷量僅為9.74萬輛,連如今哈弗H6半年的銷量都達不到。但十年過後的2013年,國內的SUV銷量迅速膨脹至266.13萬輛,堪稱是火箭式的增長。對於多年來一直注重發展轎車市場的合資品牌來說,SUV市場誇張式的增長以及市場規模的龐大程度一直是一件讓它們難以相信的事情。這種對市場形勢的誤判,為合資品牌在SUV市場上的糟糕市場表現埋下了深深的伏筆。

十個手指頭能數完的合資SUV

早在1994年的日內瓦車展上,豐田便發布了被譽為“城市SUV鼻祖”的SUV車型—RAV4。比越野車更時尚的外觀,比普通轎車能寬裕的空間,比轎車略為優秀的通過能力,從全世界的範圍來說,SUV都是一種具有深深吸引力的車型。但可惜的是,消費者對於SUV的追捧,並沒有讓合資品牌加深在SUV車型上的研發,特別是針對中國市場。

直至8年之後,也就是2002年,才有了第一款國產的合資SUV—東風本田CRV的出現。這8年間,竟然沒有一家合資品牌在中國市場推出一款SUV車型,這是一件如今讓人無法相信的事情。而即便有了第一款國產合資SUV的出現,但合資品牌對於中國的SVU市場依舊沒有太多的重視。即便是在中國汽車市場具有絕對品牌影響力的大眾汽車,把途觀引入國內生產的時間已經是2009年的事情。

自主品牌已經贏在了起跑線上

相反,不少的自主品牌已經在SUV真槍實彈地幹了起來。譬如,大家眾所周知的雙環汽車(沒錯,就是那家被本田告上法庭,還反坑本田1600萬元賠償的雙環汽車),在2004年便山寨本田的CRV,推出了大名鼎鼎的雙環SRV。而如今國內SUV的銷量冠軍—長城汽車,在2002年也依靠皮卡的平台基礎,生產出了自家的第一款SUV—長城賽弗。更不要說具有傳奇色彩,賣到南美市場的奇瑞瑞虎。所以說,從第一步棋開始,

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

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

合資品牌便已經輸在了起跑線上。

合資品牌龜速一樣的產品研發

意識到落後的合資品牌開始奮力直追,但要知道合資車企的內部運營遠沒有自主品牌靈活。從是否引入全新車型,合資品牌內部便要經過長時間的項目討論。出於商業上的原因,合資品牌的外資方對於新車型的引入,是持極其謹慎的態度的。

所以,儘管合資品牌的中方人員已經意識到這一問題,但往往也是處於干著急的情況,畢竟中方在大部分的情況下,是沒有獨立開發新車型的權力的。在確定要引入全新的SUV車型以後,還要進行一定的本土化適配、車輛測試等長時間的流程。比如國內著名的合資品牌–X安X特的一款SUV,整車試驗的流程,便有4000多項。所以,目前合資品牌的SUV研發以及生產的速度,完全可以用緩慢去形容。

自主品牌的地頭蛇優勢

但相比合資品牌,自主品牌完全就是地頭蛇一般的靈活。依靠國外成熟的SUV進行逆向研發,大大縮小研發所需的研發。再通過三菱、愛信等成熟的零部件供應商,完成最核心的動力組裝。當然,自主品牌不但在產品的設計、製造有着相當的速度優勢。在推出全新車型的決策上,同樣具有優勢。譬如國內的某知名的自主品牌,自今年3月推出緊湊型SUV以來,依次又在5月、8月,相繼發布了兩款全新的緊湊型SUV。新品推出的速度之快,不僅讓業內人士感到詫異,消費者同樣感到詫異。

神一般的速度

更著名的,如漢騰汽車,今年的5月份才宣布完成品牌的發布。僅僅過了4月的時候,旗下的首款緊湊型SUV—漢騰X7便騰空出世。從品牌的構建,到產品的設計研發,再到生產線的投產,漢騰僅用了4個月的時間。這幾乎是世界上任何一家合資品牌都無法完成的事情。

不過,有部分的自主品牌存在過於追求投產速度,希望更早地享受到SUV市場的紅利,而在生產、製造環節存在把控不嚴的情況。也正是這個原因,談到自主SUV,許多的消費者第一時間都會拋出諸如“耐用性怎麼樣?”“小問題多不多”的疑問。這也是這種運作的靈活性給自主品牌帶來的負面影響。

不過儘管合資品牌SUV在產品的投放速度不如自主品牌,但根據相關的數據显示,截止2015年底,17萬以上區間的SUV市場,98%的市場份額依然由合資品牌所垄斷。而自主品牌主要活躍在中端(13-17萬)、次中端(9-13萬)、低端(9萬以下)等SUV市場。也就是說,儘管自主品牌佔據了SUV的大部分市場份額,但更具有盈利能力的高端SUV市場依然是合資品牌的天下。如何在保障銷量漂亮的同時,改善產品的質量,以及提高自身品牌在高端SUV市場的影響力,是目前自主品牌亟待需要思考的問題。

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

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

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

Sony Mobile 新旗艦 Xperia 1 III 爆料規格曝光:將搭載 S888 處理器、4K HDR 螢幕亮度提升_網頁設計

2{icon} {views}

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

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

在首款搭載高通 Snapdragon 888 處理器的小米11 推出後,接下來在 2021 年包括華碩、黑鯊、聯想、LG、魅族、motorola、nubia、realme、OnePlus、OPPO、Sharp、vivo 與 ZTE 都確定將成為首批搭載的的手機品牌。而近期網傳 Sony Mobile  接下來的 Xperia 1 系列新旗艦 Xperia 1 III 也可能會搭載此處理器。

▲示意圖,圖為 Xperia 1 II

Sony Mobile 新旗艦 Xperia 1 III 爆料規格曝光:將搭載 S888 處理器、4K HDR 螢幕亮度提升

Sony Mobile 今年在台灣推出包括 Xperia 1 II 以及 Xperia 5 II 兩款旗艦級手機,而根據往年在 MWC(Mobile World Congress)也都會發表新機。然而受到疫情影響,接下來在 2021 年也還有許多變數。不過最近在 Twitter 也有人開始爆料 Xperia 1 系列下一代旗艦新機 Xperia 1 III(暫稱)的規格。

▲示意圖,圖為 Xperia 1 II

爆料指出 Xperia 1 III 將一樣採用 6.5 吋 4K OLED HDR 螢幕,不過螢幕亮度部分將比起 Xperia 1 II 提升 15% 。硬體規格方面,也機搭載高通 Snapdragon 888  5G 處理器、配備 8GB RAM 和 256GB ROM 。指紋辨識也延續採用側邊指紋辨識的方式,全機也支持 IP65/IP68 防塵防水等級。

▲示意圖,圖為 Xperia 1 II

而爆料尚未提到 Sony 一直以來強調的相機拍攝功能,若沒意外這次應繼續和蔡司認證的鏡頭,在相機鏡頭搭配、拍攝穩定性、夜拍方面進行升級。

▲示意圖,圖為 Xperia 1 II

不過爆料則有提到 Xperia 1 III 可能的售價,傳聞為 1,199 美元起(約合新台幣 33,770 元)。

▲圖片來源:Anthony (Twitter/@TheGalox_)

消息來源:Anthony (Twitter/@TheGalox_)

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

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

延伸閱讀:
realme 7 5G 開箱動手玩|天璣 800U 處理器、5G+5G 雙卡雙待、120Hz 更新率螢幕、5000mAh大電量與 30W Dart 閃充,萬元內 5G 超值選擇

小米11 Pro 傳聞規格首次曝光:預計 2021 上半年推出, Redmi K11 將為同期最便宜 S888 旗艦新機之一

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

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

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

竟然只要3毛2一公里 大尺寸又舒適!什麼車這麼牛_網頁設計

4{icon} {views}

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

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

首先是因為他的液晶儀錶盤。為什麼說液晶的儀錶盤會讓人覺得配置低,首先在於它的儀錶盤的設計。它雖然採用了一塊液晶儀錶盤,但是不支持多種界面風格的切換。這在小編美美接觸的使用全液晶儀錶盤的車型中是非常少見的。

前一段時間我們已經給大家做過騰勢電動車的試駕報告。這款比亞迪和戴姆勒合作的電動車它的底盤表現隔音水平以及獨特的車身結構都讓我們感嘆的一把,現在國內電動車市場魚龍混雜。騰勢電動車就算得上是一款比較務實的產品,我們都說一款產品,它的表現怎麼樣需要時間來檢驗。對一款電動車來說時間更能夠說明問題。

出於這樣的出發點考慮,公司決定對這台騰勢電動車進行長時間測試,看一看這款強強聯手打造出來的電動車到底能不能經受得住時間的考驗。而這個艱巨而光榮的任務就交給了小編美美。

拿到車鑰匙后小編美美內心是十分的矛盾的。到底要通過怎樣的方式來講述我們在長測的過程中出現的一些問題,或者是體驗心得呢。為了能夠讓枯燥的長測內容變得更加生動有趣一些。小編美美決定通過五個故事。來講述騰勢電動車使用中的一些問題。

首先說說使用層面的感受,對此在試駕中大部分內容已經做了詳細講解,關於騰勢的駕駛感受還是那幾個詞,安穩、平順、疲軟、舒適性也確實不錯,隔音很好,車子行駛起來有一定的高級感,有點奔馳的感覺,不過,也就是這樣一台車了,更多的是安逸與枯燥,作為代步工具或者通勤工具還是很OK的。

好了,談談大家最關心的充電和續航的問題

一、充電的問題

0%~100%充電時間:

南方電網7kw電樁15小時

騰勢50kw超級充70分鐘

一輛汽油車大家最關心的就是它的油耗表現以及他能夠適應的油品的範圍。但是作為一輛電動車來說它的電耗水平不是你最擔心的,它的有效續航里程和充電的便利性、充電的速度才是更加讓人關心的。我們長測的騰勢電動車官方宣稱它的續航里程是300公里,但是由於我們長壽的過程中處在炎熱的夏天,因此在開着空調的情況下它的續航里程大概在230公里左右。我們使用南方電網的充電樁充滿電需要15個小時左右。我們之前在這個充電樁對比亞迪秦EV300進行充電。充滿電只需六個多小時。

對此我們也諮詢了騰勢電動車的工程師。得到的答覆是這樣的:南方電網的充電樁可能沒有經過騰勢的認證,因此為了保證安全性,而降低了充電的功率。小編美美想說的是,安全性考慮得還是蠻周到的。

騰勢官網查找充電樁的地圖位置不是很方便,另外,像高德、百度這些主流地圖軟件也不能很好查找適合騰勢充電的地點,對於充電樁的分佈,我想電動車的車主都比較迷茫,這裏提一下,根據公關MM的介紹,每個騰勢4S店都有一個充電樁分布圖(文末有廣州的充電樁分布圖),但是對於常用地圖的年輕人來說,地圖上如果能直接查到充電樁分佈就再好不過了,這也算是對騰勢的一個小建議吧。

長測故事:傍晚7點鐘車子只有60km的續航了,10點還要去20公裡外的廣州南站接人,南方電網充電樁故障,只能抱着試一試的心態去廣州南站,回來20km續航了(車載續航里程显示很准)

已經晚上11點了,然後連夜去珠江新城冬廣場負4樓充電,1個24kw的充電樁還被人使用了,只能等到人家充完再充,晚上12點終於開始充電了。

充到1點半之後充了100km續航的電,太困了只好回家了。其實汽油車加個油只要2分鐘的事。

二、續航的問題

開空調230公里左右

不開空調270公里左右

之前我們說過夏天開空調它的續航里程大概在230公里左右。這樣的續航里程表現是一個什麼樣的概念呢?我如果從廣州開到深圳我就沒法再開回來了,有人說你開到深圳了再充電唄。第一我並不熟悉深圳的充電樁分佈。第二,我不確定我在app上查到的充電樁是否能用。

比如說今天我想去小王家可能過去得100公里,那麼我就得思考我能不能有充足的電開回來。如果不能開回來,那麼我就得考慮小王家附近是不是有充電樁。有沒有投入使用,充電樁即使能用,會不會充電樁的車位被汽油車所霸佔。

就在小編美美工作的園區,電動車的充電樁位就經常被汽油車所霸佔。因此基本上我都要等到他們下班開走才能夠充上電。

可靠性

30天的時間里大燈壞了一隻/音響共振

這台電動車在我們編輯部停留了一個多月的時間。總共進行了2000多公里的測試對於汽油車來說跑2000公里並不算什麼。但是2000公里對一台電動車來說已經算是一個比較長的里程了。你得充滿十多次電,並且每天開着上下班才能在一個月內完成這樣的里程數。

騰勢電動車的質量也是讓我們比較的擔心。一個月的測試時間正常的使用情況下它的大燈壞了一隻,音響產生了共振(估計是有一隻低音喇叭損壞)。並且車內也有一些細微的異響。因此對於騰勢電動車的可靠性小編並沒有什麼信心。不過也可能是由於編輯部的人開車都比較的粗暴的原因,或者是小編美美的運氣不好?嗚嗚~

那麼在小編美美看來這台電動車還有哪一些比較明顯的缺點呢?

電機抖動這個問題大部分試過騰勢電動車的人都會發現,因為它的抖動並不算細微。主要它的抖動主要體現在鬆開油門踏板後車輛自由滑行一段距離后再次踩下油門踏板的時候。會產生些抖動,類似於手動擋的車型在離合器接合的過程中發生的抖動一樣,而這個問題在低速的時候幾乎是一直存在的。

配置

蛋疼的超速提醒

其實平心而論這台電動車的配置不算低,但是在主觀感受上會覺得它的配置比較低。首先是因為他的液晶儀錶盤。為什麼說液晶的儀錶盤會讓人覺得配置低,首先在於它的儀錶盤的設計。它雖然採用了一塊液晶儀錶盤,但是不支持多種界面風格的切換。這在小編美美接觸的使用全液晶儀錶盤的車型中是非常少見的。它能夠显示的信息也十分少。除了車速功率表和電量表這些電動車的常規數據之外。它僅僅能夠显示續航里程、平均車速和行駛時間以及室外溫度,而且最讓人不解的是他的行車電腦显示屏分的小,並且功能設計也讓人有些不太理解。

比如說行車電腦只有在停車狀態下可以設置,就拿超速提醒來說,如果我在一條國道上行駛。這條國道的最高限速為60km/h。那麼我把車輛的超速提醒設置為60km/h。可是,跑完國道接着去跑高速。一旦我的車速超過60km/h車輛便會發出滴滴滴提示音,並且我在行駛的過程中無法調整限速的限速值,也不能關閉限速提醒,這也就是意味着我在每設定一個限速的時候。我就需要停一次車,你可以告訴我這是安全設計。但是你這樣設計的結果是讓我使用過一次超速提醒之後就再也不會使用這個功能。不過這些都是設計的問題,能夠通過系統升級之類的解決掉,相信在日後騰勢也會更加的完善產品吧。

而其他的例如真皮座椅、感應雨刷、自動大燈哈曼卡頓音響這些配置都是有的,但是都不需要你來操作。因此坐進車內你會覺得配置不高,可玩性也比較差。這裏我想重點表揚下的是感應雨刷,騰勢的雨刷採用對開式雨刷,雨刷的清水面積更大,

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

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

而感應雨刷在天氣多變的南方也十分的實用,一會大雨一會兒小雨的天氣下雨刷會自動根據雨量來調整工作頻率,感覺比自動大燈實用多了。

使用成本

3毛2一公里

雖然它是一輛電動車,可是我相信買新的電動車的人更多的是務實的消費者。因此這輛車的使用成本也是他們比較關心的一個方面。這台電動車單次充電47.5千瓦時,我們使用南方電網的充電樁,充電價格是一塊錢一度的電費,加每度電八毛錢的服務費。也就是說我們充電的價格是一塊八毛一度電。單次充滿電的續航里程在230公里左右,如果不開空調大概是270公里。我們按照

270公里算,47.5kwh*1.8=85.5元

85.5元/270公里=0.316元/公里

因此相比汽油車使用成本上還是有一些優勢的。相比較汽油車來說,電動車的用車成本可是低很多的。

不過又要說回來了,我們的騰勢發生了一些小小的磕碰(說下事故,其實是因為騰勢車太重,緊急躲閃的慣性太大了,偏移的幅度變大,才會蹭到的。一般車輛我估計閃一下再回舵是不會有那麼大動靜的),而維修價格是3000元,雖然是保險負責了,但是不得不感嘆這台和奔馳E級同樣收費標準的電動車還是不要惹,車主最好也買上保險,因為有點小貴。

優點也十分值得肯定

騰勢電動車的舒適性是值得大書特書的,騰勢電動車的底盤調教比較好,對路面震動過濾得十分到位,壓過大大小小的坑窪時車身的跳動不大,不過再單側車輪壓過大坑之類的時候車身的橫向擺動還是有些明顯,這也是舒適性上唯一值得吐槽的地方了,對於日常大部分的情況來說,這車的舒適性和靜謐性能夠把你伺候得舒舒服服的,由於沒有了傳統汽油車的發動機噪音,加上本身十分不錯的隔音功底,騰勢電動車的靜謐性十分優秀,即使在高速路上行駛在車內小聲交談也完全沒有問題,而舒服的座椅也是舒適性的一大工程,座椅寬大柔軟,細膩的皮革也很討人喜歡。

尤其是前段時間試駕完偏運動取向的寶馬2系之後再回到這台騰勢上才發現騰勢真的特別舒適,也難怪車子剛來的時候總編大大霸佔了好幾天,天天開回家呢~

電動車安全性

電動車安全性也是老生常談的一個問題,對於一台電動車來說,大家最關心的莫過於電池的安全性,其中最常見的問題包括電池的輻射啦,燃燒之類的,對此小編美美特意詢問了騰勢的工程師,得到了下面的答案:

電壓問題,騰勢的電池電壓十分高,在發生車禍後會不會對人造成傷害?

工程師:DENZA騰勢純電動汽車高壓設計採用戴姆勒高壓安全設計要求- MBNLV123,騰勢電池由144個單體電池組成,144個單體共同提供高壓電,一旦發生碰撞144個單體將分離,變成3V左右的144個單體電池,也就不存在高壓電了。

騰勢電動車的電池碰撞安全性如何,會不會燃燒?

工程師:電池做過嚴格的安全性測試,其中就包括了燃燒測試、穿刺、高壓擠壓等,保證了在發生碰撞時盡量減小電池燃燒的可能性,而電池包也能夠很好地保護電池。

騰勢電動車電池的輻射如何?會不會開了2個月頭髮鬍子都掉光了?

工程師:輻射是分等級的,只要把電池的輻射控制在合理的範圍內,就不會對人造成傷害。騰勢電動車經過嚴格的輻射測試,確保能夠保證人體安全。

騰勢電動車的電池壽命如何?會不會用了2年就壞了?

工程師:電池的循環壽命在2000次以上,標準充電使用可以達到7-8年,騰勢的電池設計是終生使用的。

關於騰勢400

現在已經能夠看到騰勢400的宣傳了,據聞騰勢300的電池重量是550kg,車身重量也達到了2090kg,之前就說過這麼大的自重對於動力剎車以及懸架來說都是個不小的壓力,開着騰勢300能夠明顯感覺到這個車重量不小,而騰勢400的電池重量達到了680kg,因此車身重量應該達到了2230kg,這個数字十分恐怖了。雖然頂配使用了達到184馬力的電動機,但是相信受限於整備質量,駕駛感受上依然不會好太多。不過這並不影響騰勢成為這個價位里最討人喜歡的電動車,十分成熟的電動車產品不多,騰勢算是少有的一個。

對於騰勢我是喜歡的,便宜的用車成本/舒適的駕駛感受,這是一台很務實的車,和編輯部其它的試駕車相比,它沒有什麼璀璨的地方,但是就是一個忠實可靠的夥伴,一切盡在不言中,對於那些需要指標的城市的人們來說,買一輛騰勢不失為一個高品質的選擇。

–THE END–

附送:

廣州市充電樁分布圖

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

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

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

運用惰性刪除和定時刪除實現可過期的localStorage緩存_網頁設計

1{icon} {views}

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

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

localStorage簡介

使用localStorage可以在瀏覽器中存儲鍵值對的數據。經常被和localStorage一併提及的是sessionStorage,它們都可以在當瀏覽器中存儲鍵值對的數據。但是它們之間的區別是:存儲在localStorage的數據可以長期保留;而當頁面會話結束(也就是當頁面被關閉)時,存儲在sessionStorage的數據會被清除。

另外需要注意的是,localStorage中的鍵值對總是以字符串的形式存儲,並且只能訪問當前域名下的數據,不能跨域名訪問。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

localStorage方法

可以通過setItem方法增加了一個鍵值對數據,比如:

localStorage.setItem('name', 'OneMore');

如果該鍵已經存在,那麼該鍵對應的值將被覆蓋。還可以使用getItem方法讀取對應鍵的值數據,比如:

var name = localStorage.getItem('name');

可以使用removeItem方法移除對應的鍵,比如:

localStorage.removeItem('name');

也可以使用clear方法移除當前域名下所有的鍵值對數據,比如:

localStorage.clear();

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

可過期的localStorage緩存

正如上面所提到的,localStorage只能用於長久保存整個網站的數據,保存的數據沒有過期時間,直到手動去刪除。所以要實現可過期的localStorage緩存的中重點就是:如何清理過期的緩存?

惰性刪除

惰性刪除是指,某個鍵值過期后,該鍵值不會被馬上刪除,而是等到下次被使用的時候,才會被檢查到過期,此時才能得到刪除。我們先來簡單實現一下:

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

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

var lsc = (function (self) {
    var prefix = 'one_more_lsc_'
    /**
     * 增加一個鍵值對數據
     * @param key 鍵
     * @param val 值
     * @param expires 過期時間,單位為秒
     */
    self.set = function (key, val, expires) {
        key = prefix + key;
        val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
        localStorage.setItem(key, val);
    };
    /**
     * 讀取對應鍵的值數據
     * @param key 鍵
     * @returns {null|*} 對應鍵的值
     */
    self.get = function (key) {
        key = prefix + key;
        var val = localStorage.getItem(key);
        if (!val) {
            return null;
        }
        val = JSON.parse(val);
        if (val.expires < new Date().getTime()) {
            localStorage.removeItem(key);
            return null;
        }
        return val.val;
    };
    return self;
}(lsc || {}));

上述代碼通過惰性刪除已經實現了可過期的localStorage緩存,但是也有比較明顯的缺點:如果一個key一直沒有被用到,即使它已經過期了也永遠存放在localStorage。為了彌補這樣缺點,我們引入另一種清理過期緩存的策略。

歡迎關注微信公眾號:萬貓學社,每周一分享Java技術乾貨。

定時刪除

定時刪除是指,每隔一段時間執行一次刪除操作,並通過限制刪除操作執行的次數和頻率,來減少刪除操作對CPU的長期佔用。另一方面定時刪除也有效的減少了因惰性刪除帶來的對localStorage空間的浪費。

每隔一秒執行一次定時刪除,操作如下:

  1. 隨機測試20個設置了過期時間的key。
  2. 刪除所有發現的已過期的key。
  3. 若刪除的key超過5個則重複步驟1,直至重複500次。

具體實現如下:

var lsc = (function (self) {
    var prefix = 'one_more_lsc_'
    var list = [];
    //初始化list
    self.init = function () {
        var keys = Object.keys(localStorage);
        var reg = new RegExp('^' + prefix);
        var temp = [];
        //遍歷所有localStorage中的所有key
        for (var i = 0; i < keys.length; i++) {
        	//找出可過期緩存的key
            if (reg.test(keys[i])) {
                temp.push(keys[i]);
            }
        }
        list = temp;
    };
    self.init();
    self.check = function () {
        if (!list || list.length == 0) {
            return;
        }
        var checkCount = 0;
        while (checkCount < 500) {
            var expireCount = 0;
            //隨機測試20個設置了過期時間的key
            for (var i = 0; i < 20; i++) {
                if (list.length == 0) {
                    break;
                }
                var index = Math.floor(Math.random() * list.length);
                var key = list[index];
                var val = localStorage.getItem(list[index]);
                //從list中刪除被惰性刪除的key
                if (!val) {
                    list.splice(index, 1);
                    expireCount++;
                    continue;
                }
                val = JSON.parse(val);
                //刪除所有發現的已過期的key
                if (val.expires < new Date().getTime()) {
                    list.splice(index, 1);
                    localStorage.removeItem(key);
                    expireCount++;
                }
            }
            //若刪除的key不超過5個則跳出循環
            if (expireCount <= 5 || list.length == 0) {
                break;
            }
            checkCount++;
        }
    }
    //每隔一秒執行一次定時刪除
    window.setInterval(self.check, 1000);
    return self;
}(lsc || {}));

完整源碼及使用示例

完整源碼及使用示例已上傳到我的GitHub(https://github.com/heihaozi/LocalStorageCache)上,感謝各位小夥伴的Star和Fork。

總結

一種策略可能會有自己的缺點,為了規避相應的缺點,我們可以合理運用多種策略,揚長避短就得到接近完美的解決方案。

微信公眾號:萬貓學社

微信掃描二維碼

獲得更多Java技術乾貨

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

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

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

如果我買了SUV 一定承諾給它一趟難忘的旅行_網頁設計

3{icon} {views}

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

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

有一個好消息是,明天七八月份,西藏拉薩到林芝地區的高等級公路基本能施工完畢並通車,路況非常地好,到時候不限於SUV車型能進藏,各種轎車或摩托車都完全可以從拉薩到林芝往返,大大降低了自駕游的成本和時間,重要的是增加了不少人身安全,進藏再也不是那麼難了。

最後說幾句:

此次參加雪佛蘭SUV最美中國行,是航班成都轉飛拉薩的,自駕游路線由拉薩市中心到林芝地區。自駕車的路線大部分都是土路,旁邊都是施工團隊在工作,經過不少村落,

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

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

遇到不少牛羊豬群,聽導遊說,撞死一隻牛三萬塊,撞死一隻藏香豬一萬塊,撞死還不能帶走…有一個好消息是,明天七八月份,西藏拉薩到林芝地區的高等級公路基本能施工完畢並通車,路況非常地好,到時候不限於SUV車型能進藏,各種轎車或摩托車都完全可以從拉薩到林芝往返,大大降低了自駕游的成本和時間,重要的是增加了不少人身安全,進藏再也不是那麼難了。祝願每個人的進藏夢,儘早實現!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

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

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