分類
發燒車訊

回聲消除中的LMS和NLMS算法與MATLAB實現

  自適應濾波是数字信號處理的核心技術之一,在科學和工業上有着廣泛的應用領域。自適應濾波技術應用廣泛,包括回波抵消、自適應均衡、自適應噪聲抵消和自適應波束形成。回聲對消是當今通信系統中普遍存在的現象。聲回波引起的信號干擾會分散用戶的注意力,降低通信質量。本文重點介紹了LMS和NLMS算法的使用,以減少這種不必要的回聲,從而提高通信質量

關鍵詞:自適應濾波器,自適應算法,回聲消除

1  引言

  當音頻信號在真實環境中產生混響時,就會產生聲學回聲,從而導致原始信號加上信號[1]的衰減、延時圖像。本文將重點研究通信系統中聲學回波的產生。

  自適應濾波器是一種動態濾波器,它不斷地改變其特性以獲得最優的輸出。自適應濾波算法通過改變參數使期望輸出d (n)與實際輸出y (n)之間的差值最小化。該函數稱為自適應算法的代價函數(loss)。圖1显示了自適應回聲抵消系統的框圖。其中,濾波器H(n)表示聲環境的脈衝響應,W(n)表示用來抵消回波信號的自適應濾波器。自適應濾波器的目標是使輸出的y(n)與期望的d(n)(在回聲環境中混響的信號)相等。在每次迭代中,誤差信號e(n)=d (n)-y (n)被反饋回濾波器,濾波器的特性也隨之改變。

自適應回聲消除系統

  自適應濾波器的目標是計算期望信號與自適應濾波器輸出之間的差值e(n)。該誤差信號反饋到自適應濾波器,並通過算法改變其係數,以最小化該差值的函數,即代價函數。在聲回波消除的情況下,自適應濾波器的最優輸出與不需要的回波信號等值。當自適應濾波器輸出等於期望信號時,誤差信號為零。在這種情況下,回顯信號將被完全取消,遠用戶將不會聽到他們的任何原始語音返回給他們。

2. 最小均方(LMS)算法

  最小均方(LMS)算法是由Widrow和Hoff在1959年通過對模式識別的研究首次提出的。由此成為自適應濾波中應用最廣泛的算法之一。LMS算法是一種基於隨機梯度的自適應濾波算法,它利用濾波器權重的梯度來收斂到最優的維納解[2-4]。由於其計算簡單而廣為人知並被廣泛使用。正是這種簡單性使它成為判斷所有其他自適應濾波算法的基準。

  隨着LMS算法的每次迭代,自適應濾波器的濾波抽頭(tap)權值按照如下公式進行更新。

$$公式1:w(n+1)=w(n)2\mu e(n)x(n)$$

  這裏x(n)是延時輸入值的輸入向量,$x(n)=[x_1(n)x_2(n)…x_N(n)]^T=[x(n)x(n-1)…x(n-N+1)]^T$。向量$w(n)=[w_0(n)w_1(n)w_2(n)…w_{N-1}(n)]^T$代表自適應FIR濾波器抽頭(tap)權向量在時刻n的係數。參數μ被稱為步長參數和小正的常數。此步長參數控制更新因子的影響。μ必須選擇一個合適的值LMS算法的性能,如果該值太小自適應濾波器的收斂時間會太長;如果μ太大自適應濾波器變得不穩定,導致其輸出發散[5 – 8]

2.1 LMS算法的實現

LMS算法的每次迭代都需要三個不同的步驟,順序如下:

1. FIR濾波器的輸出y(n)用公式2計算。

$$公式2:y(n)=\sum_{i=0}^{N-1}w(n)x(n-1)=w^T(n)x(n)$$

2. 誤差估計的值按公式3計算。

$$公式3:e(n)=d(n)-y(n)$$

3.更新FIR向量的抽頭tap權值,為下一次迭代做準備,如公式4所示。

$$公式4:w(n+1)=w(n)+2\mu e(n)x(n)$$

  LMS算法在自適應濾波中得到廣泛應用的主要原因是其計算簡單,比其他常用的自適應算法更易於實現。LMS算法每次迭代需要2N加法和2N + 1次乘法(N用於計算輸出y(N)),另一個用於通過向量乘法計算標量[9]

clear;
clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.5;             % mu表示步長
N=1000;             % 橫坐標1000個採樣點
Loop=150;           % 150次循環
EE_NLMS=zeros(N,1); % 不同步長的初始化誤差
for nn=1:Loop       % epoch=150
    % 權重初始化w
    win_NLMS=zeros(1,order);         % NLMS四種步長測試,四個權重——1
    error_NLMS=zeros(1,N)';     % 初始化誤差
    % 均勻分佈的輸入值
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分佈-0.5,sign(n)>0=1;<0=-1
    % 輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 將白高斯噪聲添加到信號中

    % N=1000,每個採樣點
    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個數據進行處理
      e_NLMS = output(i)-win_NLMS*input;
      win_NLMS=win_NLMS+e_NLMS*input'/(input'*input);   % NLMS更新權重
      error_NLMS(i)=error_NLMS(i)+e_NLMS^2;
    end
    
    EE_NLMS=EE_NLMS+error_NLMS;     % 把總誤差相加
end
% 對總誤差求平均值
error_NLMS=EE_NLMS/Loop;

figure;
error_NLMS=10*log10(error_NLMS(order:N));
plot(error_NLMS,'r');       % 紅色
axis tight;                 % 使用緊湊的坐標軸
legend('NLMS算法');           % 圖例
title('NLMS算法誤差曲線');     % 圖標題
xlabel('樣本');                     % x軸標籤
ylabel('誤差/dB');                  % y軸標籤
grid on;                            % 網格線

3 歸一化最小均方(NLMS)算法

  LMS算法的主要缺點之一是每次迭代都有一個固定的步長參數。這需要在開始自適應濾波操作之前了解輸入信號的統計信息。實際上,這是很難實現的。即使我們假設自適應回聲抵消系統的唯一輸入信號是語音,但仍有許多因素如信號輸入功率和振幅會影響其性能[10-12]

  歸一化最小均方算法(NLMS)是LMS算法的擴展,LMS算法通過計算最大步長值來繞過這個問題。步長值的計算公式如下

$$Step\ size = \frac{1}{dot\ product(input\ vector,\ input\ vector)}$$

這個步長與輸入向量x(n)的係數的瞬時值的總期望能量的倒數成正比。輸入樣本的期望能量之和也等於輸入向量與自身的點積,以及輸入向量自相關矩陣的跡R[13-15]。

$$公式5:tr[R]=\sum_{i=0}^{N-1}E[x^2(n-i)]\\ \quad\quad =E[\sum_{i=0}^{N-1}x^2(n-i)]$$

NLMS算法的遞歸公式如式6所示

$$公式6:w(n+1)=w(n)+\frac{1}{x^T(n)x(n)}e(n)x(n)$$

3.1 NLMS算法的實現

  NLMS算法已在Matlab中實現。由於步長參數是根據當前的輸入值來選擇的,因此NLMS算法在未知信號下具有更大的穩定性。該算法具有良好的收斂速度和相對簡單的計算能力,是實時自適應回波抵消系統[16]的理想算法

  由於NLMS是標準LMS算法的擴展,因此NLMS算法的實際實現與LMS算法非常相似。NLMS算法的每次迭代都需要按照以下順序執行這些步驟。

1. 計算了自適應濾波器的輸出

$$公式7:y(n)=\sum_{i=0}^{N-1}w(n)x(n-i)=w^T(n)x(n)$$

2. 誤差信號等於期望信號和濾波器輸出之間的差值。

$$公式8:e(n)=d(n)-y(n)$$

3.計算了輸入向量的步長值。

$$公式9:\mu(n)=\frac{1}{x^T(n)x(n)}$$

4. 濾波器抽頭權重更新,為下一次迭代做準備。

$$公式10:w(n+1)=w(n)+\mu(n)e(n)x(n)$$

NLMS算法的每次迭代都需要3N+1次乘法,僅比標準LMS算法多N次。考慮到所獲得的穩定性和回波衰減增益,這是一個可接受的增加。

clear;
clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
% Hn是濾波器權重
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.5;             % mu表示步長
N=1000;             % 橫坐標1000個採樣點
Loop=150;           % 150次循環
% 不同步長的初始化誤差
EE_LMS = zeros(N,1);
EE_NLMS=zeros(N,1);
for nn=1:Loop       % epoch=150
    win_LMS = zeros(1,order);   % 權重初始化w
    error_LMS=zeros(1,N)';      % 初始化誤差
    % 均勻分佈的語音數據輸入
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分佈-0.5,sign(n)>0=1;<0=-1
    % 輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 真實輸出=將白高斯噪聲添加到信號中

    % N=1000,每個採樣點
    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個數據進行處理
      e_LMS = output(i)-win_LMS*input;
      
      mu=0.02;      % 步長
      win_LMS = win_LMS+2*mu*e_LMS*input';
      error_LMS(i)=error_LMS(i)+e_LMS^2;
    end
    % 把總誤差相加
    EE_LMS = EE_LMS+error_LMS;

end
% 對總誤差求平均值
error_LMS = EE_LMS/Loop;

figure;
error1_LMS=10*log10(error_LMS(order:N));
plot(error1_LMS,'b.');  % 藍色
axis tight;         % 使用緊湊的坐標軸
legend('LMS算法');       % 圖例
title('LMS算法誤差曲線');  % 圖標題
xlabel('樣本');                     % x軸標籤
ylabel('誤差/dB');                  % y軸標籤
grid on;                            % 網格線

4 LMS算法的結果

  利用Matlab對LMS算法進行了仿真。圖2显示的是通過麥克風從計算機系統收集到的輸入語音信號。圖3显示了從輸入信號派生出的所需回波信號。圖4显示了自適應濾波器的輸出,它將減少輸入信號的回波信號。圖5显示了由濾波器輸出信號計算出的均方誤差信號。圖6是由回波信號對誤差信號的分割得到的衰減。

  自適應濾波器為1025階FIR濾波器。步長設置為0.02。MSE表明,隨着算法的發展,代價函數的平均值逐漸減小。

5 NLMS算法的結果

  用Matlab對NLMS算法進行了仿真。圖7显示了輸入信號。圖8显示了所需的信號。圖9显示了自適應濾波器輸出。圖10显示了均方誤差。圖11显示了衰減。

  自適應濾波器為1025階FIR濾波器。步長設置為0.1。

 

 NLMS算法在均方誤差和平均衰減方面優於LMS算法,其性能總結如表1所示。

 

6 結論

  由於其簡單性,LMS算法是最流行的自適應算法。然而,LMS算法存在收斂速度慢和數據依賴的問題。

  NLMS算法是LMS算法的一個同樣簡單但更健壯的變體,它在簡單性和性能之間表現出比LMS算法更好的平衡。由於其良好的性能,NLMS在實時應用中得到了廣泛的應用。

7. 參考

文章翻譯自論文《2011_adaptive algorithms for acoustic echo cancellation in speech processing》

[1]. Homana, I.; Topa, M.D.; Kirei, B.S.; “Echo cancelling using adaptive algorithms”, Design and Technology of Electronics Packages, (SIITME) 15th International Symposium., pp. 317-321, Sept.2009.

[2]. Paleologu, C.; Benesty, J.; Grant, S.L.; Osterwise, C.; “Variable step-size NLMS algorithms for echo cancellation” 2009 Conference Record of the forty-third Asilomar Conference on Signals, Systems and Computers., pp. 633-637, Nov 2009.

[3]. Soria, E.; Calpe, J.; Chambers, J.; Martinez, M.; Camps, G.; Guerrero, J.D.M.; “A novel approach to introducing adaptive filters based on the LMS algorithm and its variants”, IEEE Transactions, vol. 47, pp. 127-133, Feb 2008.

[4]. Tandon, A.; Ahmad, M.O.; Swamy, M.N.S.; “An efficient, low-complexity, normalized LMS algorithm for echo cancellation”, IEEE workshop on Circuits and Systems, 2004. NEWCAS 2004, pp. 161-164, June 2004.

[5]. Eneman, K.; Moonen, M.; “Iterated partitioned block frequency-domain adaptive filtering for acoustic echo cancellation,” IEEE Transactions on Speech and Audio Processing, vol. 11, pp. 143-158, March 2003.

[6]. Krishna, E.H.; Raghuram, M.; Madhav, K.V; Reddy, K.A; “Acoustic echo cancellation using a computationally efficient transform domain LMS adaptive filter,” 2010 10th International Conference on Information sciences signal processing and their applications (ISSPA), pp. 409-412, May 2010.

[7]. Lee, K.A.; Gan,W.S; “Improving convergence of the NLMS algorithm using constrained subband updates,” Signal Processing Letters IEEE, vol. 11, pp. 736-739, Sept. 2004.

[8]. S.C. Douglas, “Adaptive Filters Employing Partial Updates,” IEEE Trans.Circuits SYS.II, vol. 44, pp. 209-216, Mar 1997.

[9]. D.L. Duttweiler, “Proportionate Normalized Least Mean Square Adaptation in Echo Cancellers,” IEEE Trans. Speech Audio Processing, vol. 8, pp. 508-518, Sept. 2000.

[10]. E. Soria, J. Calpe, J. Guerrero, M. Martínez, and J. Espí, “An easy demonstration of the optimum value of the adaptation constant in the LMS algorithm,” IEEE Trans. Educ., vol. 41, pp. 83, Feb. 1998.

[11]. D. Morgan and S. Kratzer, “On a class of computationally efficient rapidly converging, generalized NLMS algorithms,” IEEE Signal Processing Lett., vol. 3, pp. 245–247, Aug. 1996.

[12]. G. Egelmeers, P. Sommen, and J. de Boer, “Realization of an acoustic echo canceller on a single DSP,” in Proc. Eur. Signal Processing Conf. (EUSIPCO96), Trieste, Italy, pp. 33–36, Sept. 1996.

[13]. J. Shynk, “Frequency-domain and multirate adaptive filtering,” IEEE Signal Processing Mag., vol. 9, pp. 15– 37, Jan. 1992.

[14]. Ahmed I. Sulyman and Azzedine Zerguine, “Echo Cancellation Using a Variable Step-Size NLMS Algorithm”, Electrical and Computer Engineering Department Queen’s University.

[15]. D. L. Duttweiler, “A twelve-channel digital echo canceller,” IEEE Trans. Commun., vol. 26, no. 5, pp. 647–653, May 1978.

[16]. J. Benesty, H. Rey, L. Rey Vega, and S. Tressens, “A nonparametric VSS NLMS algorithm,” IEEE Signal Process. Lett., vol. 13, pp. 581–584, Oct. 2006.

 

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

分類
發燒車訊

寶馬投資Scoop拼車應用公司進軍汽車共享市場

據《路透社》報導,寶馬集團透露,其旗下風險投資部門iVentures已經為加利福尼亞拼車應用公司Scoop提供了一筆投資金額,具體數目未知。

Scoop公司目前位於三藩市港灣區,通過智慧手機支援為乘客提供拼車服務,其開發出的應用軟體可將相鄰社區及工作區域的人們連接起來,共同拼車。寶馬此舉為汽車製造商為初創公司投資的最新一次行動,使消費者在未擁有私人車輛或經常駕駛的情況下也能夠出行。

此外,寶馬還分別為車隊管理軟體公司RideCell、泊車點定位服務Zirx、簡化公共交通系統服務Moovit、從智慧手機上集成資料教授司機如何安全駕駛的Zendrive公司進行了投資。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

分類
發燒車訊

特斯拉超級電池工廠Gigafactory 7月正式開業 目前仍在建

據外媒體報導,特斯拉稱其位於內華達州里諾市郊的超級電池工廠Gigafactory將於7月29日晚上舉行開業典禮,目前該工廠仍處於建設之中。

特斯拉在發給客戶的一封電子郵件中披露,為特斯拉成功推薦的客戶超過5位元的人將會受邀參加此次開業典禮。特斯拉已經通過電子郵件向這些符合資格的客戶通知了開業典禮時間。

事實上,在未來2個月內Gigafactory工廠不可能完全竣工,特斯拉僅僅是決定在已建成區域舉行小型典禮。在今年3月份,特斯拉、SolarCity在該廠區接待了內華達州議員,探討了太陽能業務在該州的發展。

據悉,Gigafactory工廠建設造價逾50億美元,到今年5月初僅建成14%。此外,松下向特斯拉的超級電池工廠供應電池生產設備,並提供部分資金。

儘管電池廠還在建設之中,但是工廠已經投產。工廠目前生產的太陽能電池被用於公共設施、企業以及各個地產業主。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

分類
發燒車訊

北汽規劃打造四大海外基地 南非新工廠將投產

據媒體報導,從北汽集團官方獲悉,北汽將斥資50億元在南非建設總裝 廠,該工廠擬於下月開始建設,計畫於2017年11月建成投產。按照規劃,北汽將打造包括南非、伊朗在內的四大海外運營基地並輻射周邊市場,形成四大屬地化產業運營集團。

未來北汽將海外市場拓展劃分了三條線,歸納為“一帶一路一洲 哥倫布航線”,其中“一帶一路”符合大環境下的海外發展趨勢,“一洲”則是“一帶一路”中涉及到的非洲,在此基礎上,北汽增加了“哥倫布航線”沿途的中南美地區。繼續細化,北汽將以“南非、伊朗、東南亞、墨西哥”四大重點專案為引領,建設輻射周邊市場的四大海外運營基地,逐步實現從“旅行者”到“定居者”的角色轉變。

與跨國車企在中國的發展要尋求本地車企進行合作類似,北汽在南非新建的工廠將由北汽集團與南非工業開發公司的合資企業負責運營。新工廠總投資金額達到50億元(7.73億美元),計畫於下月開始建設,有望於2017年11月建成投產,計畫年產能為10萬輛。

據介紹,南非工廠將作為試點,為後續北汽加速海外涉及12個國家的19個KD(散件組裝廠)專案建設積累經驗。北汽集團已於2013年在南非開設了一家小型SKD廠,位於斯普林斯鎮,該廠生產小型麵包計程車,此次在南非斥鉅資打造新工廠並作為試點也就不難理解。

北京汽車國際發展公司擁有五大核心業務,包括自主品牌整車和零部件產品的出口,技術、設備、整車的進口,此外還有產品改裝,境外投資以及國際合作,初期投放海外市場的產品也將以北汽自主品牌為主。這意味著即將建成的南非汽車生產廠將有望投產包括北京紳寶、北京牌和北汽威旺三大產品系列,初步形成對當地市場佈局的同時還將進行他國出口,擴大南非及周邊國家和地區市場份額。

在北汽擴大海外市場佈局後,未來市場銷量將成為企業諸多努力的體現。“十三五”期間北汽制定了“2030”戰略,戰略中指出企業要在2020年實現20萬輛整車出口,完成3個建設,包括國際化運營隊伍建設、合作夥伴隊伍建設、體系能力建設,從而實現品牌高端市場的突破。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

分類
發燒車訊

Zabbix-(五)監控Docker容器與自定義jvm監控項

Zabbix-(五)監控Docker容器與自定義jvm監控項

一.前言

前文中講述了Zabbix對服務器硬件方面的監控功能,本文將講述利用Zabbix監控Docker容器中的Java Web服務,並通過自定義監控項,監控JVM老年代使用情況以及GC信息。Zabbix其實提供了,自帶了JMX模板能夠直接監控JVM信息,本文主要側重於自定義參數與自定義監控項,關於JMX會在之後的文章中介紹。

準備

  • Zabbix Server (Zabbix 4.4) (ip:192.168.152.140)
  • 運行Java應用的主機 以下簡稱Server-A (已被Zabbix監控) (ip:192.168.152.142)

二.開啟agent用戶自定義參數配置

  1. 修改配置

    使用自定義參數時,首先需要修改Server-A的agent配置

    # vim /etc/zabbix/zabbix_agentd.conf

    修改配置 UnsafeUserParameters=1

    UnsafeUserParameters=1
  2. 重啟zabbix-agent

    # systemctl restart zabbix-agent

三.運行tomcat容器

在Server-A運行tomcat容器

# docker run --name tomcat -p 8080:8080 -dit tomcat:jdk8-adoptopenjdk-hotspot

將zabbix賬號添加到docker組。參考

# sudo gpasswd  -a zabbix docker

外部訪問測試一下

四.創建自定義Docker模板

我們可以定義一個比較通用的Docker模板,有服務需要被監控時,直接鏈接該模板即可。

  1. 創建群組

    點擊【配置】-【主機群組】-【創建主機群組】

    定義一個組名 Docker Group

    配置項
    * 組名 Docker Group
  2. 創建模板

    創建一個自定義模板,模板名稱Docker Template,選擇上步驟創建的Docker Group群組

    配置項
    * 模板名稱 Docker Template
    * 群組 Docker Group

五.編寫腳本與自定義監控參數

我們需要編寫一個腳本,用於發現當前正在運行的docker容器(這裏使用容器名稱)。

  1. 在Server-A編寫發現運行容器的python腳本

    創建腳本

    # cd /data/zabbix
    # touch find_container.py
    # chmod a+x find_container.py
    # vim find_container.py

    腳本內容:

    #!/usr/bin/env python
    import os
    import json
    
    # 查看當前運行的docker容器
    t=os.popen(""" docker ps  |grep -v 'CONTAINER ID'|awk {'print $NF'} """)
    container_name = []
    for container in  t.readlines():
            r = os.path.basename(container.strip())
            container_name += [{'{#CONTAINERNAME}':r}]
    # 轉換成json數據
    print json.dumps({'data':container_name},sort_keys=True,indent=4,separators=(',',':'))

    運行腳本,查看一下json數據格式:

    {
        "data":[
            {
                "{#CONTAINERNAME}":"tomcat"
            }
        ]
    }
  2. 在Server-A自定義容器發現參數

    我們需要自定義一個鍵值對的配置類型,以便Zabbix可以通過鍵讀取到值。

    增加自定義參數

    # cd /etc/zabbix/zabbix_agentd.d
    # vim userparameter_find_container.conf
    docker.container /data/zabbix/find_container.py (腳本的運行結果)
    UserParameter=docker.container,/data/zabbix/find_container.py
  3. 在Server-A創建查看容器JVM GC情況的腳本

    我們可以使用jstat -gcutil 命令查看GC情況

    創建python腳本

    # cd /data/zabbix
    # touch monitor_gc.py
    # chmod a+x monitor_gc.py
    # vim monitor_gc.py

    腳本內容

    #!/usr/bin/python
    import sys
    import os
    
    def monitor_gc(container_name, keyword):
            cmd = ''' docker exec %s bash -c "jstat -gcutil 1" | grep -v S0 | awk '{print $%s}' ''' %(container_name, keyword)
            value = os.popen(cmd).read().replace("\n","")
            print value
    
    if __name__ == '__main__':
            # 參數1:容器的名稱
            # 參數2:查看第幾列(例如 Eden區在第3列傳入3,Full GC次數在第9列傳入9)
            container_name, keyword = sys.argv[1], sys.argv[2]
            monitor_gc(container_name, keyword)

    測試腳本,查看當前tomcat容器Full GC次數

    # /data/zabbix/monitor_gc.py 'tomcat' '9'

  4. 在Server-A自定義Zabbix JVM GC參數

    同樣,增加一個conf文件,表示自定義參數

    # cd /etc/zabbix/zabbix_agentd.d
    # touch userparameter_gc_status.conf
    # vim userparameter_gc_status.conf
    jvm.gc.status[*] /data/zabbix/monitor_gc.py $1 $2
    UserParameter=jvm.gc.status[*], /data/zabbix/monitor_gc.py $1 $2

    jvm.gc.status[*] 表示可以使用參數。其中$1表示參數1,即容器名稱;$2表示參數2,需要查看哪項GC信息,$1 $2都是通過Zabbix配置時傳遞的。

  5. 在Zabbix server上測試自定義參數

    為zabbix sever安裝zabbix-get

    # yum install -y zabbix-get

    測試自定義參數,如果有權限問題,可以參考

    # zabbix_get -s 192.168.152.142 -p 10050 -k docker.container
    # zabbix_get -s 192.168.152.142 -p 10050 -k "jvm.gc.status['tomcat', 9]"

六.Zabbix模板增加自動發現規則

上述配置中,已經可以通過腳本獲取到已運行的容器信息,此步驟將通過Zabbix配置界面,在模板中添加自動發現規則,以發現被監控主機中正在運行的docker容器,並利用這些獲取的數據進一步監控容器中jvm數據。

  1. 創建自動發現規則

    點擊【配置】-【模板】-【Docker Template】

    點擊【自動發現規則】-【創建發現規則】

    先配置【自動發現規則】

    配置項
    * 名稱 發現正在運行的Docker容器規則
    類型 Zabbix 客戶端
    * 鍵值 docker.container (這是我們上述步驟中自定義的鍵值)
    其他配置 根據需要配置

    鍵值配置項是之前

    再配置【過濾器】

    則配置自定義腳本返回json數據中的

    配置項
    {#CONTAINERNAME}
  2. 添加監控項原型

    點擊新建的自動發現規則的【監控項原型】-【創建監控項原型】

    輸入參數

    配置項
    * 名稱 Tomcat Full GC次數監控項
    類型 Zabbix 客戶端
    * 鍵值 jvm.gc.status[{#CONTAINERNAME} , 9]
    其他配置項 根據需要填寫

    鍵值是定義的參數,{#CONTAINERNAME} 是jvm.gc.status的參數1,使用了自動發現規則,發現到的docker容器名稱(本文中即是 tomcat);參數2 9 則是表示需要查看FullGC次數,FGC列(第9列)

    除此之外,還可以添加Old老年代(對應第4列),Full GC時間(對應第10列)等監控項,這裏就不一一添加了,和上述過程基本一致,只需修改參數2即可(也可以利用剛新建的監控項原型進行【克隆】)。

七.鏈接模板

將上述鏈接到Server-A主機

八.DashBoard添加可視化圖形

回到Zabbix首頁可以為新增的自定義監控項,增加圖形(添加圖形步驟可以參考)

九.其他

部署問題

  • zabbix在執行腳本時,是使用的zabbix賬戶,因此可能要注意要給zabbix賬號賦予權限。

    例如,zabbix賬戶無法使用docker命令,將zabbix添加到docker組

    # sudo gpasswd -a zabbix docker
  • zabbix server無法執行agent自定義參數中的腳本

    為agent主機設置

    # setenforce 0

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

分類
發燒車訊

數組與鏈表

前言

數組和鏈表是兩種數據結構,數組非常簡單易用但是它有兩個非常大的缺點,一個是數組一旦創建無法擴展,另一個則是數組的查找和刪除的速度很慢.

鏈表改善了一些數組的缺點,但是同樣的鏈表自身也存在一些自己的缺點.

本篇博客將為大家介紹一下這數組和鏈表特點及各自的優缺點.

閱讀前的準備工作

,一種粗略的評價計算機算法效率的方法.後面的內容會用到表示效率的方法.

1. 數組

我們按數組中的數組是否排序對數組進行劃分,將數組分為無序數組和有序數組.無序數組中的數組是無序的,而有序數組中的數據則是升序或者降序排序的.

1.1 無序數組

因為無序數組中的數據是無序的,往數組中添加數據時不用進行比較和移動數據,所以往無序數組裡面添加數據很快.無論是添加第一個數據還是第一萬個數據所需的時間是相同的,效率為O(1).

至於查找和刪除速度就沒有那麼快了,以數組中有一萬個數據項為例,最少需要比較1次,最多則需要比較一萬次,平均下來需要比較5000次,即N/2次比較,N代表數據量,大O表示法中常數可以忽略,所以效率為O(N).

結論:

  1. 插入很快,因為總是將數據插入到數組的空餘位置.
  2. 查找和刪除很慢,假設數組的長度為N,那麼平均的查找/刪除的比較次數為N/2,並且還需要移動數據.

1.2 有序數組

無序數組中存放的數據是無序的,有序數組裡面存放的數據則是有序的(有可能是升序有可能是降序).

因為有序數組中的數據是按升序/降序排列的,所以插入的時候需要進行排序並且移動數據項,所有有序數組的插入速度比無序數組慢. 效率為O(N).

刪除速度和無序數組一樣慢 效率為O(N).

有序數組的查找速度要比無序數組快,這是因為使用了一個叫做二分查找的算法.

二分查找: 二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列.

有一個關於二分查找的形象類比 -> 猜數遊戲

假設要在0-100之間猜一個數,那麼你第一個要猜的数字就是100的一半50的時候,你的朋友會告訴你這個数字比要猜的数字是大還是小,如果比数字大,你接下來要猜的数字就是50的一半25,你的朋友說比這個数字要大,那麼你下面要猜的数字就是25-50中間的那個數37,以此類推…

使用二分查找可極大的提高查找的效率,假設一個有序數組有十億個數據,那麼查找到所需的数字,最多只需比較30次.

有序數組使用二分查找的效率為O(logN).有序數組也可以通過二分查找來新增和刪除數據以提高效率,但是依然需要在新增/刪除后移動數據項,所以效率依然會有影響.

總結:

  1. 有序數組的查找速度比無序數組高,效率為O(logN)
  2. 有序數組的刪除和新增速度很慢,效率為O(N)

1.3 數組總結

數組雖然簡單易用,但是數組有兩個致命的缺點:

  1. 數組存儲的數量有限,創建的過大浪費資源,創建的過小溢出
  2. 數組的效率比其他數據結構低
  • 無序數組插入效率為O(1)時間,但是查找花費O(N)時間
  • 有序數組查找花費O(logN)時間,插入花費O(N)時間
  • 刪除需要移動平均半數的數據項,所以刪除都是O(N)的時間

2. 鏈表

數組一經創建大小就固定住了,無法修改,鏈表在這方面做出了改善,只要內存夠用就可以無限制的擴大.

鏈表是繼數組之後應用最廣泛的數據結構.

2.1 鏈表的特點

鏈表為什麼叫鏈表呢? 因為它保存數據的方式就像一條鎖鏈

鏈表保存數據的方式很像上面的這一條鎖鏈,每一塊鎖鏈就是一個鏈節點,鏈節點保存着自己的數據同時通過自己的next()方法指向下一個鏈節點. 鏈表通過鏈節點不斷地調用next()方法就可以遍歷鏈表中的所有數據.

在鏈表中,每個數據項都被包含在”鏈節點”(link)中,一個鏈結點是某個類的對象,這個類可以叫做Link.因為一個鏈表中有許多類似的鏈結點,所以有必要用一個不同於鏈表的類來表達鏈結點.

每個Link對象中都包含一個對下一個鏈結點引用的字段(通常叫做next).

鏈表本身的對象中有一個字段指向對第一個鏈結點的引用.

數據與鏈表查找數據的區別: 在數組中查找數據就像在一個大倉庫裏面一樣,一號房間沒有,我們去二號房間,二號房間沒有我們去三號房間,以此類推.. 按照地址找完所有房間就可以了.

而在鏈表中查找數據就像單線彙報的地下工作者,你是孤狼你想要彙報點情報給你的頂級上司毒蜂,但是你必須先報告給你的接頭人豬剛鬣,豬剛鬣在報告給它的單線接頭人土行孫,最後由土行孫報告給毒蜂.只能一個找一個,這樣最終完成任務.

2.2 Java代碼

鏈節點類:


/**
 * @author liuboren
 * @Title: 鏈節點
 * @Description:
 * @date 2019/11/20 19:30
 */
public class Link {
    //  保存的數據
    public int data;

    // 指向的下一個鏈節點
    public Link nextLink;

    public Link(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public Link getNextLink() {
        return nextLink;
    }

    public void setNextLink(Link nextLink) {
        this.nextLink = nextLink;
    }
}

鏈表類


/**
 * @author liuboren
 * @Title: 鏈表類
 * @Description:
 * @date 2019/11/20 19:31
 */
public class LinkList {
    private Link first;

    public LinkList() {
        first = null;
    }

    // 新增鏈節點方法
    public void insertFirst(int data) {
        Link link = new Link(data);
        link.setNextLink(first);
        first = link;
    }
}

在新增節點的時候,新增的link的next方法指向原來的first節點,並將鏈表類的first指向新增的節點.

2.4 其他鏈表

剛剛介紹的鏈表是單向鏈表,只能從后往前遍歷,其他的鏈表還有雙端鏈表、雙向鏈表、有序鏈表.

再簡單介紹一下雙端鏈表吧.

雙端鏈表就是在單向鏈表的基礎上,新增一個成員變量指向鏈表的最後一個對象.

雙端鏈表代碼:

/**
 * @author liuboren
 * @Title: 鏈表類
 * @Description:
 * @date 2019/11/20 19:31
 */
public class LinkList {
    private Link first;
    private Link last;

    public LinkList() {
        first = null;
    }

    public boolean isEmpty() {
        return first == null;
    }

    // 新增鏈節點方法
    public void insertFirst(int data) {
        Link newLink = new Link(data);
        newLink.setNextLink(first);
        if (isEmpty()) {
            last = newLink;
        }
        first = newLink;

    }
}

雙向鏈表則是可以從first和last兩個方向進行遍歷,有序鏈表的數據都是按照關鍵字的順序排列的,本文不再展開了.

2.5 鏈表的效率

鏈表的效率:

  • 表頭插入和刪除速度都很快,花費O(1)的時間.
  • 平均起來,查找&刪除&插入在制定鏈節點後面都需要搜索一半的鏈節點需要O(N)次比較,雖然數組也需要O(N)次比較,但是鏈表讓然要快一些,因為不需要移動數據(只需要改變他們的引用)

3. 總結

鏈表解決了數組大小不能擴展的問題,但是鏈表自身依然存在一些問題(在鏈表的鏈節點後面查找&刪除&插入的效率不高),那麼有沒有一種數據結構即擁有二者的優點又改善了二者的缺點呢,答案是肯定的,下篇博客將為您介紹這種優秀的數據結構,敬請期待.

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

【其他文章推薦】

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

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

※帶您來看台北網站建置台北網頁設計,各種案例分享

小三通物流營運型態?

※快速運回,大陸空運推薦?

分類
發燒車訊

021.掌握Pod-Pod調度策略

一 Pod生命周期管理

1.1 Pod生命周期

Pod在整個生命周期過程中被系統定義了如下各種狀態。

狀態值 描述
Pending API Server已經創建該Pod,且Pod內還有一個或多個容器的鏡像沒有創建,包括正在下載鏡像的過程。
Running Pod內所有容器均已創建,且至少有一個容器處於運行狀態、正在啟動狀態或正在重啟狀態。
Succeeded Pod內所有容器均成功執行退出,且不會重啟。
Failed Pod內所有容器均已退出,但至少有一個容器退出為失敗狀態。
Unknown 由於某種原因無法獲取該Pod狀態,可能由於網絡通信不暢導致。

1.2 Pod重啟策略

Pod重啟策略(RestartPolicy)應用於Pod內的所有容器,並且僅在Pod所處的Node上由kubelet進行判斷和重啟操作。當某個容器異常退出或者健康檢查失敗時,kubelet將根據RestartPolicy的設置來進行相應操作。
Pod的重啟策略包括Always、OnFailure和Never,默認值為Always。

  • Always:當容器失效時,由kubelet自動重啟該容器;
  • OnFailure:當容器終止運行且退出碼不為0時,由kubelet自動重啟該容器;
  • Never:不論容器運行狀態如何,kubelet都不會重啟該容器。

       kubelet重啟失效容器的時間間隔以sync-frequency乘以2n來計算,例如1/2/4/8倍等,最長延時5min,並且在成功重啟后的10min后重置該時間。

Pod的重啟策略與控制方式關聯,當前可用於管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接管理kubelet管理(靜態Pod)。
不同控制器的重啟策略限制如下:

  • RC和DaemonSet:必須設置為Always,需要保證該容器持續運行;
  • Job:OnFailure或Never,確保容器執行完成后不再重啟;
  • kubelet:在Pod失效時重啟,不論將RestartPolicy設置為何值,也不會對Pod進行健康檢查。








Pod包含的容器數 Pod當前的狀態 發生事件 Pod的結果狀態
RestartPolicy=Always RestartPolicy=OnFailure RestartPolicy=Never
包含1個容器 Running 容器成功退出 Running Succeeded Succeeded
包含1個容器 Running 容器失敗退出 Running Running Failed
包括兩個容器 Running 1個容器失敗退出 Running Running Running
包括兩個容器 Running 容器被OOM殺掉 Running Running Failed

1.3 Pod健康檢查

對Pod的健康檢查可以通過兩類探針來檢查:LivenessProbe和ReadinessProbe。
LivenessProbe探針:用於判斷容器是否存活(running狀態),如果LivenessProbe探針探測到容器不健康,則kubelet將殺掉該容器,並根據容器的重啟策略做相應處理。若一個容器不包含LivenessProbe探針,kubelet認為該容器的LivenessProbe探針返回值用於是“Success”。
ReadineeProbe探針:用於判斷容器是否啟動完成(ready狀態)。如果ReadinessProbe探針探測到失敗,則Pod的狀態將被修改。Endpoint Controller將從Service的Endpoint中刪除包含該容器所在Pod的Eenpoint。
kubelet定期執行LivenessProbe探針來診斷容器的健康狀態,通常有以下三種方式:

  • ExecAction:在容器內執行一個命令,若返回碼為0,則表明容器健康。

示例:通過執行”cat /tmp/health”命令判斷一個容器運行是否正常。容器初始化並創建該文件,10s后刪除該文件,15s秒通過命令判斷,由於該文件已被刪除,因此判斷該容器Fail,導致kubelet殺掉該容器並重啟。

  1 [root@uk8s-m-01 study]# vi dapi-liveness.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: dapi-liveness-pod
  6   labels:
  7     test: liveness-exec
  8 spec:
  9   containers:
 10     - name: dapi-liveness
 11       image: busybox
 12       args:
 13       - /bin/sh
 14       - -c
 15       - echo ok > /tmp/health; sleep 10; rm -rf /tmp/health; sleep 600
 16       livenessProbe:
 17         exec:
 18           command:
 19           - cat
 20           - /tmp/health
 21 
 22 [root@uk8s-m-01 study]# kubectl describe pod dapi-liveness-pod

  • TCPSocketAction:通過容器的IP地址和端口號執行TCP檢查,若能建立TCP連接,則表明容器健康。

示例:

  1 [root@uk8s-m-01 study]# vi dapi-tcpsocket.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: dapi-healthcheck-tcp
  6 spec:
  7   containers:
  8     - name: nginx
  9       image: nginx
 10       ports:
 11       - containerPort: 80
 12       livenessProbe:
 13         tcpSocket:
 14           port: 80
 15         initialDelaySeconds: 30
 16         timeoutSeconds: 1
 17 
 18 [root@uk8s-m-01 study]# kubectl create -f dapi-tcpsocket.yaml


提示:對於每種探測方式,都需要設置如下兩個參數,其包含的含義如下:

initialDelaySeconds:啟動容器後進行首次健康檢查的等待時間,單位為s;

timeoutSeconds:健康檢查發送請求后等待響應的超時時間,單位為s,當超時發生時,kubelet會認為容器已經無法提供服務,將會重啟該容器。

二 Pod調度

Kubernetes中,Pod通常是容器的載體,一般需要通過Deployment、DaemonSet、RC、Job等對象來完成一組Pod的調度與自動控制功能。

2.1 Depolyment/RC自動調度

Deployment或RC的主要功能之一就是自動部署一個容器應用的多份副本,以及持續監控副本的數量,在集群內始終維持用戶指定的副本數量。
示例:

  1 [root@uk8s-m-01 study]# vi nginx-deployment.yaml
  2 apiVersion: apps/v1beta1
  3 kind: Deployment
  4 metadata:
  5   name: nginx-deployment-01
  6 spec:
  7   replicas: 3
  8   template:
  9     metadata:
 10       labels:
 11         app: nginx
 12     spec:
 13       containers:
 14       - name: nginx
 15         image: nginx:1.7.9
 16         ports:
 17         - containerPort: 80
 18 
 19 [root@uk8s-m-01 study]# kubectl get deployments
 20 NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
 21 nginx-deployment-01   3/3     3            3           30s
 22 [root@uk8s-m-01 study]# kubectl get rs
 23 NAME                             DESIRED   CURRENT   READY   AGE
 24 nginx-deployment-01-5754944d6c   3         3         3       75s
 25 [root@uk8s-m-01 study]# kubectl get pod | grep nginx
 26 nginx-deployment-01-5754944d6c-hmcpg   1/1     Running     0          84s
 27 nginx-deployment-01-5754944d6c-mcj8q   1/1     Running     0          84s
 28 nginx-deployment-01-5754944d6c-p42mh   1/1     Running     0          84s

2.2 NodeSelector定向調度

當需要手動指定將Pod調度到特定Node上,可以通過Node的標籤(Label)和Pod的nodeSelector屬性相匹配。
# kubectl label nodes <node-name> <label-key>=<label-value>
node節點創建對應的label后,可通過在定義Pod的時候加上nodeSelector的設置實現指定的調度。
示例:

  1 [root@uk8s-m-01 study]# kubectl label nodes 172.24.9.14 speed=io
  2 node/172.24.9.14 labeled
  3 [root@uk8s-m-01 study]# vi nginx-master-controller.yaml
  4 kind: ReplicationController
  5 metadata:
  6   name: nginx-master
  7   labels:
  8     name: nginx-master
  9 spec:
 10   replicas: 1
 11   selector:
 12     name: nginx-master
 13   template:
 14     metadata:
 15       labels:
 16         name: nginx-master
 17     spec:
 18       containers:
 19       - name: master
 20         image: nginx:1.7.9
 21         ports:
 22         - containerPort: 80
 23       nodeSelector:
 24         speed: io
 25 
 26 [root@uk8s-m-01 study]# kubectl create -f nginx-master-controller.yaml
 27 [root@uk8s-m-01 study]# kubectl get pods -o wide
 28 NAME                READY   STATUS    RESTARTS    AGE    IP            NODE
 29 nginx-master-7fjgj  1/1     Running   0           82s    172.24.9.71   172.24.9.14


提示:可以將集群中具有不同特點的Node貼上不同的標籤,實現在部署時就可以根據應用的需求設置NodeSelector來進行指定Node範圍的調度。

注意:若在定義Pod中指定了NodeSelector條件,但集群中不存在符合該標籤的Node,即使集群有其他可供使用的Node,Pod也無法被成功調度。

2.3 NodeAffinity親和性調度

親和性調度機制極大的擴展了Pod的調度能力,主要增強功能如下:

  1. 更具表達力,即更精細的力度控制;
  2. 可以使用軟限制、優先採用等限制方式,即調度器在無法滿足優先需求的情況下,會使用其他次條件進行滿足;
  3. 可以依據節點上正在運行的其他Pod的標籤來進行限制,而非節點本身的標籤,從而實現Pod之間的親和或互斥關係。

目前有兩種節點親和力表達:
requiredDuringSchedulingIgnoredDuringExecution:硬規則,必須滿足指定的規則,調度器才可以調度Pod至Node上(類似nodeSelector,語法不同)。
preferredDuringSchedulingIgnoredDuringExecution:軟規則,優先調度至滿足的Node的節點,但不強求,多個優先級規則還可以設置權重值。
IgnoredDuringExecution指:如果一個Pod所在的節點在Pod運行期間標籤發生了變化,不再符合該Pod的節點親和性需求,則系統將忽略Node上Label的變化,該Pod能繼續在該節點運行。
示例:
條件1:只運行在amd64的節點上;盡量運行在ssd節點上。

  1 [root@uk8s-m-01 study]# vi nodeaffinity-pod.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: with-node-affinity
  6 spec:
  7   affinity:
  8     nodeAffinity:
  9       requiredDuringSchedulingIgnoredDuringExecution:
 10         nodeSelectorTerms:
 11         - matchExpressions:
 12           - key: kubernetes.io/arch
 13             operator: In
 14             values:
 15             - amd64
 16       preferredDuringSchedulingIgnoredDuringExecution:
 17       - weight: 1
 18         preference:
 19           matchExpressions:
 20           - key: disk-type
 21             operator: In
 22             values:
 23             - ssd
 24   containers:
 25   - name: with-node-affinity
 26     image: gcr.azk8s.cn/google_containers/pause:2.0


NodeAffinity操作語法;In、NotIn、Exists、DoesNotExist、Gt、Lt。NotIn和DoesNotExist可以實現互斥功能。
NodeAffinity規則設置注意事項:

  • 若同時定義nodeSelector和nodeAffinity,則必須兩個條件都滿足,Pod才能最終運行指定在Node上;;
  • 若nodeAffinity指定多個nodeSelectorTerms,則只需要其中一個能夠匹配成功即可;
  • 若nodeSelectorTerms中有多個matchExpressions,則一個節點必須滿足所有matchExpressions才能運行該Pod。

2.4 PodAffinity親和性調度

PodAffinity根據節點上正在運行的Pod標籤而不是Node標籤來判斷和調度,要求對節點和Pod兩個條件進行匹配。
規則描述為:若在具有標籤X的Node上運行了一個或多個符合條件Y的Pod,則Pod應該(或者不應該)運行在這個Node上。
X通常為Node節點的機架、區域等概念,Pod是屬於某個命名空間,所以條件Y表達的是一個或全部命名空間中的一個Label Selector。
Pod親和性定義與PodSpec的affinity字段下的podAffinity字段里,互斥性定義於同一層次的podAntiAffinity子字段中。
舉例:

  1 [root@uk8s-m-01 study]# vi nginx-flag.yaml	#創建名為pod-flag,帶有兩個標籤的Pod
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: pod-affinity
  6 spec:
  7   affinity:
  8     podAffinity:
  9       requiredDuringSchedulingIgnoredDuringExecution:
 10       - labelSelector:
 11           matchExpressions:
 12           - key: security
 13             operator: In
 14             values:
 15             - S1
 16         topologyKey: kubernetes.io/hostname
 17   containers:
 18   - name: with-pod-affinity
 19     image: gcr.azk8s.cn/google_containers/pause:2.0

  1 [root@uk8s-m-01 study]# vi nginx-affinity-in.yaml	#創建定義標籤security=S1,對應如上Pod “Pod-flag”。
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: pod-affinity
  6 spec:
  7   affinity:
  8     podAffinity:
  9       requiredDuringSchedulingIgnoredDuringExecution:
 10       - labelSelector:
 11           matchExpressions:
 12           - key: security
 13             operator: In
 14             values:
 15             - S1
 16         topologyKey: kubernetes.io/hostname
 17   containers:
 18   - name: with-pod-affinity
 19     image: gcr.azk8s.cn/google_containers/pause:2.0
 20 
 21 [root@uk8s-m-01 study]# kubectl create -f nginx-affinity-in.yaml
 22 [root@uk8s-m-01 study]# kubectl get pods -o wide


提示:由上Pod親和力可知,兩個Pod處於同一個Node上。

  1 [root@uk8s-m-01 study]# vi nginx-affinity-out.yaml	#創建不能與參照目標Pod運行在同一個Node上的調度策略
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: anti-affinity
  6 spec:
  7   affinity:
  8     podAffinity:
  9       requiredDuringSchedulingIgnoredDuringExecution:
 10       - labelSelector:
 11           matchExpressions:
 12           - key: security
 13             operator: In
 14             values:
 15             - S1
 16         topologyKey: failure-domain.beta.kubernetes.io/zone
 17     podAntiAffinity:
 18       requiredDuringSchedulingIgnoredDuringExecution:
 19       - labelSelector:
 20           matchExpressions:
 21           - key: security
 22             operator: In
 23             values:
 24             - nginx
 25         topologyKey: kubernetes.io/hostname
 26   containers:
 27   - name: anti-affinity
 28     image: gcr.azk8s.cn/google_containers/pause:2.0
 29 
 30 [root@uk8s-m-01 study]# kubectl get pods -o wide	#驗證

2.5 Taints和Tolerations(污點和容忍)

Taint:使Node拒絕特定Pod運行;
Toleration:為Pod的屬性,表示Pod能容忍(運行)標註了Taint的Node。
Taint語法:$ kubectl taint node node1 key=value:NoSchedule
解釋:為node1加上一個Taint,該Taint的鍵為key,值為value,Taint的效果為NoSchedule。即除非特定聲明可以容忍此Taint,否則不會調度至node1上。
Toleration示例:

  1 tolerations:
  2 - key: "key"
  3   operator: "Equal"
  4   value: "value"
  5   effect: "NoSchedule"

  1 tolerations:
  2 - key: "key"
  3   operator: "Exists"
  4   effect: "NoSchedule"

注意:Pod的Toleration聲明中的key和effect需要與Taint的設置保持一致,並且滿足以下條件:

  • operator的值是Exists(無須指定value);
  • operator的值是Equal並且value相等;
  • 空的key配合Exists操作符能夠匹配所有的鍵和值;
  • 空的effect匹配所有的effect。


若不指定operator,則默認值為Equal。
taint說明:系統允許在同一個Node上設置多個taint,也可以在Pod上設置多個toleration。Kubernetes調度器處理多個taint和toleration的邏輯順序:首先列出節點中所有的taint,然後忽略pod的toleration能夠匹配的部分,剩下的沒有忽略掉的taint就是對pod的效果。以下是幾種特殊情況:
若剩餘的taint中存在effect=NoSchedule,則調度器不會把該Pod調度到這一節點上;
若剩餘的taint中沒有NoSchedule效果,但有PreferNoSchedule效果,則調度器會嘗試不把這個Pod指派到此節點;
若剩餘taint的效果有NoSchedule,並且這個Pod已經在該節點上運行,則會被驅逐,若沒有在該節點上運行,也不會再被調度到該節點上。
示例:

  1 $ kubectl taint node node1 key=value1:NoSchedule
  2 $ kubectl taint node node1 key=value1:NoExecute
  3 $ kubectl taint node node1 key=value2:NoSchedule
  4 tolerations:
  5 - key: "key1"
  6   operator: "Equal"
  7   value: "value"
  8   effect: "NoSchedule"
  9 tolerations:
 10 - key: "key1"
 11   operator: "Equal"
 12   value: "value1"
 13   effect: "NoExecute"


釋義:此Pod聲明了兩個容忍,且能匹配Node1的taint,但是由於沒有能匹配第三個taint的toleration,因此此Pod依舊不能調度至此Node。若該Pod已經在node1上運行了,那麼在運行時設置了第3個taint,它還能繼續在node1上運行,這是因為Pod可以容忍前兩個taint。
通常,若node加上effect=NoExecute的taint,那麼該Node上正在運行的所有無對應toleration的Pod都會被立刻驅逐,而具有相應toleration的Pod則永遠不會被驅逐。同時,系統可以給具有NoExecute效果的toleration加入一個可選的tolerationSeconds字段,表明Pod可以在taint添加到Node之後還能在此Node運行多久。

  1 tolerations:
  2 - key: "key1"
  3   operator: "Equal"
  4   value: "value"
  5   effect: "NoSchedule"
  6   tolerationSeconds: 3600

釋義:若Pod正在運行,所在節點被加入一個匹配的taint,則這個pod會持續在該節點運行3600s后被驅逐。若在此期限內,taint被移除,則不會觸發驅逐事件。
Taints和Tolerations常用場景:

  • 獨佔節點:

給特定的節點運行特定應用。
$ kubectl taint nodes 【nodename】 dedicated=groupName:NoSchedule
同時在Pod中設置對應的toleration配合,帶有合適toleration的Pod允許同時使用其他節點一樣使用有taint的節點。

  • 具有特殊硬件設備的節點

集群中部分特殊硬件(如安裝了GPU),則可以把不需要佔用GPU的Pod禁止在此Node上調度。

  1 $ kubectl taint nodes 【nodename】 special=true:NoSchedule
  2 $ kubectl taint nodes 【nodename】 special=true:PreferNoSchedule

  • 定義Pod驅逐行為

NoExecute的taint對節點上正在運行的Pod有以下影響:

    1. 沒有設置toleration的pod會被立刻驅逐;
    2. 配置了對應toleration的pod,若沒有為tolerationSeconds賦值,則會一直保留在此節點中;
    3. 配置了對應toleration的pod,且為tolerationSeconds賦值,則在指定時間后驅逐。

2.6 DaemonSet

DaemonSet是在每個Node上調度一個Pod的資源對象,用於管理集群中每個Node僅運行一份Pod的副本實例。
常見場景:
在每個Node上運行一個GlusterFS存儲的Daemon進程;
在每個Node上運行一個日誌採集程序,例如Fluentd;
在每個Node上運行一個性能監控程序,採集該Node的運行性能數據,例如Prometheus。
示例:

  1 [root@uk8s-m-01 study]# vi fluentd-ds.yaml
  2 apiVersion: extensions/v1beta1
  3 kind: DaemonSet
  4 metadata:
  5   name: fluentd-cloud-logging
  6   namespace: kube-system
  7   labels:
  8     k8s-app: fluentd-cloud-logging
  9 spec:
 10   template:
 11     metadata:
 12       namespace: kube-system
 13       labels:
 14         k8s-app: fluentd-cloud-logging
 15     spec:
 16       containers:
 17       - name: fluentd-cloud-logging
 18         image: gcr.azk8s.cn/google_containers/fluentd-elasticsearch:1.17
 19         resources:
 20           limits:
 21             cpu: 100m
 22             memory: 200Mi
 23         env:
 24         - name: FLUENTD_ARGS
 25           value: -q
 26         volumeMounts:
 27         - name: varlog
 28           mountPath: /var/log
 29           readOnly: false
 30         - name: containers
 31           mountPath: /var/lib/docker/containers
 32           readOnly: false
 33       volumes:
 34       - name: containers
 35         hostPath:
 36           path: /var/lib/docker/containers
 37       - name: varlog
 38         hostPath:
 39           path: /var/log

2.7 Job批處理調度

通過Kubernetes Job資源對象可以定義並啟動一個批處理任務,批處理任務通過并行(或者串行)啟動多個計算進程去處理一批工作項。根據批處理方式不同,批處理任務可以分為如下幾種模式:
Job Template Expansion模式:一個Job對象對應一個待處理的Work item,有幾個work item就產生幾個獨立的Job。通常適合Work item數量少、每個Work item要處理的數據量比較大的場景。
Queue with Pod Per Work Item模式:採用一個任務隊列存放Work item,一個Job對象作為消費者去完成這些Work item。此模式下,Job會啟動N個Pod,每個Pod都對應一個Work item。
Queue with Variable Pod Count模式:採用一個任務隊列存放Work item,一個Job對象作為消費者去完成這些Work item,但此模式下Job啟動的數量是可變的。
Kubernetes將Job氛圍以下三類:

  • Non-parallel Jobs

通常一個Job只啟動一個Pod,除非Pod異常,才會重啟該Pod,一旦此Pod正常結束,Job將結束。

  • Parallel Jobs with a fixed completion count

并行Job會啟動多個Pod,此時需要設定Job的.spec.completions參數為一個正數,當正常結束的Pod數量達至此參數設定的值后,Job結束。同時.spec.parallelism參數用來控制并行度,即同時啟動幾個Job來處理Work Item。

  • Parallel Jobs with a work queue

任務隊列方式的并行Job需要一個獨立的Queue,Work Item都在一個Queue中存放,不能設置Job的.spec.completions參數,此時Job具有以下特性:

    1. 每個Pod都能獨立判斷和決定是否還有任務項需要處理;
    2. 如果某個Pod正常結束,則Job不會再啟動新的Pod;
    3. 如果一個Pod成功結束,則此時應該不存在其他Pod還在工作的情況。它們應該都處於即將結束、退出的狀態;
    4. 如果所有Pod都結束了,且至少有一個Pod成功結束,則整個Jod成功結束。

2.8 Cronjob定時任務

表達式:Minutes Hours DayofMonth Month DayofWeek Year
Minutes:可出現”,”、”_”、”*”、”/”,有效範圍為0~59的整數;
Hours:出現”,”、”_”、”*”、”/”,有效範圍為0~23的整數;
DayofMonth:出現”,”、”_”、”*”、”/”、”L”、”W”、”C”,有效範圍為0~31的整數;
Month:可出現”,”、”_”、”*”、”/”,有效範圍為1~12的整數或JAN~DEC;
DayofWeek:出現”,”、”_”、”*”、”/”、”L”、”W”、”C”、”#”,有效範圍為1~7的整數或SUN~SAT;
*: 表示匹配該域的任意值, 假如在Minutes域使用“*”, 則表示每分鐘都會觸發事件。
/: 表示從起始時間開始觸發, 然後每隔固定時間觸發一次,例如在Minutes域設置為5/20, 則意味着第1次觸發在第5min時, 接下來每20min觸發一次, 將在第25min、 第45min等時刻分別觸發。
示例:*/1 * * * * #每隔1min執行一次任務

  1 [root@uk8s-m-01 study]# vi cron.yaml
  2 apiVersion: batch/v2alpha1
  3 kind: CronJob
  4 metadata:
  5   name: hello
  6 spec:
  7   schedule: "*/1 * * * *"
  8   jobTemplate:
  9     spec:
 10       template:
 11         spec:
 12           containers:
 13           - name: hello
 14             image: busybox
 15             args:
 16             - /bin/sh
 17             - -c
 18             - date; echo Hello from the Kubernetes cluster
 19           restartPolicy: OnFailure

  1 [root@master study]# kubectl create -f cron.yaml
  2 [root@master study]# kubectl get cronjob hello
  3 NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
  4 hello   */1 * * * *   False     0        <none>          29s
  5 [root@master study]# kubectl get pods
  6 NAME                     READY   STATUS      RESTARTS   AGE
  7 hello-1573378080-zvvm5   0/1     Completed   0          68s
  8 hello-1573378140-9pmwz   0/1     Completed   0          8s
  9 [root@node1 ~]# docker logs c7					#node節點查看日誌
 10 Sun Nov 10 09:31:13 UTC 2019
 11 Hello from the Kubernetes cluster
 12 [root@master study]# kubectl get jobs				#查看任務
 13 NAME               COMPLETIONS   DURATION   AGE
 14 hello-1573378500   1/1           8s         3m7s
 15 hello-1573378560   1/1           4s         2m7s
 16 hello-1573378620   1/1           6s         67s
 17 hello-1573378680   1/1           4s         7s
 18 [root@master study]# kubectl get pods -o wide | grep hello-1573378680	#以job任務查看對應的pod
 19 [root@master study]# kubectl delete cj hello			#刪除cronjob

2.9 初始化容器

在很多應用場景中, 應用在啟動之前都需要進行如下初始化操作。

  • 等待其他關聯組件正確運行( 例如數據庫或某個後台服務) 。
  • 基於環境變量或配置模板生成配置文件。
  • 從遠程數據庫獲取本地所需配置, 或者將自身註冊到某个中央數據庫中。
  • 下載相關依賴包, 或者對系統進行一些預配置操作。

示例:以Nginx應用為例, 在啟動Nginx之前, 通過初始化容器busybox為Nginx創建一個index.html主頁文件。同時init container和Nginx設置了一個共享的Volume, 以供Nginx訪問init container設置的index.html文件。

  1 [root@uk8s-m-01 study]# vi nginx-init-containers.yaml
  2 apiVersion: v1
  3 kind: Pod
  4 metadata:
  5   name: nginx
  6   annotations:
  7 spec:
  8   initContainers:
  9   - name: install
 10     image: busybox
 11     command:
 12     - wget
 13     - "-O"
 14     - "/work-dir/index.html"
 15     - http://kubernetes.io
 16     volumeMounts:
 17     - name: workdir
 18       mountPath: "/work-dir"
 19   containers:
 20   - name: nginx
 21     image: nginx:1.7.9
 22     ports:
 23     - containerPort: 80
 24     volumeMounts:
 25     - name: workdir
 26       mountPath: /usr/share/nginx/html
 27   dnsPolicy: Default
 28   volumes:
 29   - name: workdir
 30     emptyDir: {}

  1 [root@uk8s-m-01 study]# kubectl get pods
  2 NAME    READY   STATUS     RESTARTS   AGE
  3 nginx   0/1     Init:0/1   0          2s
  4 [root@uk8s-m-01 study]# kubectl get pods
  5 NAME    READY   STATUS    RESTARTS   AGE
  6 nginx   1/1     Running   0          13s
  7 [root@uk8s-m-01 study]# kubectl describe pod nginx		#查看事件可知會先創建init容器,名為install


init容器與應用容器的區別如下。
(1) init container的運行方式與應用容器不同, 它們必須先於應用容器執行完成, 當設置了多個init container時, 將按順序逐個運行, 並且只有前一個init container運行成功后才能運行后一個init container。 當所有init container都成功運行后, Kubernetes才會初始化Pod的各種信息, 並開始創建和運行應用容器。
(2) 在init container的定義中也可以設置資源限制、 Volume的使用和安全策略, 等等。 但資源限制的設置與應用容器略有不同。

  • 如果多個init container都定義了資源請求/資源限制, 則取最大的值作為所有init container的資源請求值/資源限制值。
  • Pod的有效(effective) 資源請求值/資源限制值取以下二者中的較大值。
    • 所有應用容器的資源請求值/資源限制值之和。
    • init container的有效資源請求值/資源限制值。
  • 調度算法將基於Pod的有效資源請求值/資源限制值進行計算,即init container可以為初始化操作預留系統資源, 即使後續應用容器無須使用這些資源。
  • Pod的有效QoS等級適用於init container和應用容器。
  • 資源配額和限制將根據Pod的有效資源請求值/資源限制值計算生效。
  • Pod級別的cgroup將基於Pod的有效資源請求/限制, 與調度機制

一致。
(3) init container不能設置readinessProbe探針, 因為必須在它們成功運行后才能繼續運行在Pod中定義的普通容器。在Pod重新啟動時, init container將會重新運行, 常見的Pod重啟場景如下。

  • init container的鏡像被更新時, init container將會重新運行, 導致Pod重啟。 僅更新應用容器的鏡像只會使得應用容器被重啟。
  • Pod的infrastructure容器更新時, Pod將會重啟。
  • 若Pod中的所有應用容器都終止了, 並且RestartPolicy=Always, 則Pod會重啟。

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

分類
發燒車訊

QQ是怎樣創造出來的?——解密好友系統的設計

本篇介紹筆者接觸的第一個後台系統,從自身見聞出發,因此涉及的內容相對比較基礎,後台大牛請自覺略過。

什麼是好友系統?

簡單的說,好友系統是維護用戶好友關係的系統。我們最熟悉的好友系統案例當屬QQ,實際上QQ是一款即時通訊工具,憑着好友系統沉澱了海量的好友關係鏈,從而鑄就了一個堅不可摧的商業帝國。好友系統的重要性可見一斑。

熟悉互聯網產品的人都知道,當產品有了一定的用戶量,往往會開發一個好友系統。其主要目的是增加用戶粘性(有了好友就會常來)或者增加社區活躍度(有了好友就會多交流)。

而我的後台開發生涯就是從這樣一個系統開始的。

那時候,好友系統對於我們團隊大部分人來說,都是一個全新的事物,因為我們大部分人都是應屆生。整個系統的架構自然不是我們一群黃毛小孩所能創造。當年的架構圖已經找不到了,但是憑着一點記憶和多年來的經驗積累,還是可以把當年的架構勾勒出來。

 

如圖,好友系統的架構是常見的3層結構,包括接入層、邏輯層和數據層。

我們先從數據層講起。

因為我們對QQ太熟悉了,我們可以很容易地列出好友系統的數據主要包括用戶資料、好友關係鏈、消息(聊天消息和系統消息)、在線狀態等。

互聯網產品往往要面對海量的請求併發,傳統的關係型數據庫比較難滿足讀寫需求。在存儲中,一般是讀多寫少的數據才會使用MySQL等關係型數據庫,而且往往還需要增加緩存來保證性能;NoSQL(Not Only SQL)應該是目前的主流。

對於好友系統,用戶資料和好友關係鏈都使用了kv存儲,而消息使用公司自研的tlist(可以用redis的list替代),在線狀態下面再介紹。

接着是邏輯層

在這個系統中複雜度最高的應該是消息服務(而這個服務我並沒有參与開發[捂臉])。

消息服務中,消息按類型分為聊天消息和系統消息(系統消息包括加好友消息、全局tips推送等),按狀態分為在線消息和離線消息。在實現中,維護3種list:聊天消息、系統消息和離線消息。聊天消息是兩個用戶共享的,系統消息和離線消息每個用戶獨佔。當用戶在線時,聊天消息和系統消息是直接發送的;如果用戶離線,就把消息往離線消息list存入一份,等用戶再次登錄時拉取。

這樣看來,消息服務並不複雜?其實不然,系統設計中常規的流程設計往往是比較簡單的,但是對於互聯網產品,異常情況才是常態,當把各種異常情況都考慮進來時,系統就會非常複雜。

這個例子中,消息發送丟包是一種異常情況,怎麼保證在丟包情況下,還能正常運行就是一個不小的問題。

常見的解決方法是收包方回復確認包,發送方如果沒收到確認包就重發。但是確認包又可能丟包,那又可以給確認包增加一個確認包,這是一個永無止境的確認。

解決方法可以參考TCP的重傳機制。那問題來了,我們為什麼不用TCP呢?因為TCP還是比較慢的,聊天消息的可靠性沒有交易數據要求那麼高,丟幾條消息並不會造成嚴重後果,但是如果用戶每次發送消息后都要等很久才能被收到,那體驗是很差的。

一個比較折中的方案是,收包方回復確認包,如果發送方在一定時間內沒有收到確認就重發;如果收包方收到兩個相同的包(自定義seq一樣),去重即可。

一個面試題引發的討論:

面試時我常常會問候選人一個問題:在分佈式系統中怎樣實現一個用戶同時只能有一個終端在線(用戶在兩個地方先後登錄賬號,后一次登錄可以把前一次登錄踢下線)?這是互聯網產品中非常基礎的一個功能,考察的是候選人基本的架構設計能力。

設計要先從接入服務器(下稱接口機)說起。接口機是好友系統對外的窗口,主要功能是維護用戶連接、登錄鑒權、加解密數據和向後端服務透傳數據等。用戶連接好友系統,首先是連接到接口機,鑒權成功后,接口機會在內存中維護用戶session,後續的操作都是基於session進行。

如圖所示,用戶如果嘗試登錄兩次,接口機通過session就可以將第一次的登錄踢下線,從而保證只有一個終端在線。

問題解決了嗎?

沒有。因為實際系統肯定不會只有一台接口機,在多台接口的情況下,上面的方法就不可行了。因為每個接口機只能維護部分用戶的session,所以如果用戶先後連接到不同的接口機,就會造成用戶多處登錄的問題。

 

自然可以想到,解決的方法就是要維護一個用戶狀態的全局視圖。在我們的好友系統中,稱為在線狀態服務。

在線狀態服務,顧名思義就是維護用戶的在線狀態(登錄時間、接口機IP等)的服務。用戶登錄和退出會通過接口機觸發這裏的狀態變更。因為登錄包和退出包都可能丟包,所以心跳包也用作在線狀態維護(收到一次心跳標記為在線,收不到n次心跳標記為離線)。

一種常用的方法是,採用bitmap存儲在線狀態,具體是指在內存中分配一塊空間,32位機器上的自然數一共有4294967296個,如果用一個bit來表示一個用戶ID(例如QQ號),1代表在線,0代表離線,那麼把全部自然數存儲在內存只要4294967296 / (8 * 1024 * 1024) = 512MB(8bit = 1Byte)。當然,實現中也可以根據需要給每個用戶分配更多的bit。

於是,踢下線功能如圖所示。

 

用戶登錄的時候,接口機首先查找本機上是否有session,如果有則更新session,接着給在線狀態服務發送登錄包,在線狀態服務檢查用戶是否已經在線,如果在線則更新狀態信息,並向上次登錄的接口機IP發送踢下線包;接口機在收到踢下線包時會檢查包中的用戶ID是否存在session,如果存在則給客戶端發送踢下線包並刪除session。

在實際中,踢下線功能還有很多細節問題需要注意。

又回到用戶先後登錄同一台接口機的情況:

 

圖中踢下線流程是正確的,但是如果步驟10和13調換了順序(在UDP傳輸中是常見的)會發生什麼?大家可以自己推演一下,後到的踢下線包會把第二次登錄的A’踢下線了。這不是我們期望的。怎麼辦呢?

解決方法分幾個細節,①接口機在收到13號登錄成功包時,先將session A替換成session A’,然後給客戶端A發生踢下線包(避免多處存活導致互相踢下線);②踢下線包中必須包含除用戶ID外的其他標識信息,session的唯一標識應該是ID+XXX的形式(我最開始採用的是ID+LoginTime),XXX是為了區分某次的登錄;③接口機在收到踢下線包的時候只要判斷ID+XXX是否吻合來決定是否給客戶端發踢下線包。

現實情況,問題總是千奇百怪的,好在辦法總比問題多。

比如我在項目中遇到過接口機和在線狀態服務時間漂移(差幾秒)的情況。這樣踢下線的唯一標識就不能是用戶ID+LoginTime的形式了。可以為每次的登錄生成一個唯一的UUID解決。類似的問題還有很多,不再贅述。

總結一下,本篇主要介紹了好友系統的整體架構和部分模塊的實現方式。分佈式系統中各個模塊的實現其實並不難,難點主要在於應對複雜網絡環境帶來的問題(如丟包、時延等)和服務器異常帶來的問題(如為了應對服務器宕機會增加服務器冗餘度,進而又會引發其它問題)。

好友系統雖然簡單,但麻雀雖小五臟俱全,架構設計的各種技術基本都有涉及。例如分層結構、負載均衡、平行擴展、容災、服務發現、服務器開發框架等方面,後面我會在各個不同的項目中介紹這些技術,敬請期待。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

分類
發燒車訊

從最近面試聊聊我所感受的職業天花板

## 特別特別嚴肅的申明 (正經的)

未免引起誤解,標題已修改。

我一開始寫這篇文章,也純粹是有感而發。實在沒想到會引起如此多的關注。甚至還被社區大佬翻牌。說實話,誠惶誠恐。

再次申明一遍,我寫的也僅僅只是我個人的感受,我就是萬萬千千的普通碼農中的一個,所寫文章也僅僅是從自我角度出發。

不具備任何普適性參考性。請大家看清標題,只是個人感受,不適用.net整個行業。尤其請大家不要拿我的經歷作為語言選擇的參考。

。net也好,java也好,每個語言都有自己的生態,選擇語言之前請先確認自己的能力,請不要抱怨語言害了你,請先審視自己是否一直在努力進步,還是只是在隨大流。

博主就是一個普通碼農,太高端的層面很難觸及,找工作的途徑也只有朋友內推和招聘網站。

那麼我在這兩種途徑下所接觸到的信息,就是我目前的薪資已經到了這兩種渠道下的頂薪。所以,我自我感覺是我現在觸到我的天花板。

那麼我向上突破的方法,要麼向社區大佬學習,努力突破到那個圈子,但是對於這個自己實在沒底,大佬的圈子太高,感覺自己能力不夠,對自己實在沒信心。

第二種就是多語言發展,修鍊技術內功,管理內功,我覺着在努努力踮踮腳還是能夠到30K、40K月薪的小尾巴。

最後奉勸各位園友,語言從來都不是決定自己前途的關鍵因素,更多的還是要修鍊自己的內功不斷向上突破。

博主寫這篇文章的初衷只是想表述自己目前所面臨的職業困境,實在沒想到會有如此多的關注,用詞上的不當,給大家造成困擾,萬分抱歉。

 

#0 前言

入職新公司沒多久,閑來無事在博客園閑逛,看到園友分享的面試經歷,正好自己這段時間面試找工作,也挺多感想的,乾脆趁這個機會總結整理一下。

博主13年開始實習,14年畢業。到現在也工作五六年了。今年面試最大的感受就是觸及了.net的天花板。坐標,杭州。

 #1 背景

今年九月份從一家創業公司離職,原因么自然是公司創業失敗倒閉。

當初以技術合伙人的身份進入,雄心勃勃,然後挨了一頓社會毒打,從此老實做人,面朝黃土背朝天,老老實實去搬磚。

九月份出來,已經是中旬,開始刷新簡歷,準備穩坐釣魚台,等着電話信息轟炸。然後,等了两天,等了一首涼涼。直到這個時候博主才意識到,形式不對。

我的思維還停留在兩三年前,工 作遍地,只要更新下簡歷就會有無數的面試邀請。同志門,情況變了呀,行業寒冬真不只是說說而已。

沒辦法,只好花錢,刷新下簡歷,瀏覽崗位,主動出擊。中間接到了好幾個獵頭電話, 但特么都是java。好想吐槽一下,簡歷上.net辣么大的字,你們真的不識字么,21世紀了啊喂。

#2 某建築類軟件公司

主營業務:建築軟件,公司已上市。

技術框架:.net平台,具體的不是特別了解

招聘崗位:.net高級開發工程師

面試:一共四輪面試。

第一輪:就是HR了,簡單聊了下情況,為什麼離職,之前薪資多少,期望薪資多少。

第二輪:他們某業務線的部門經理和技術主管共同面試。

基本面試情況就是我在說他們在聽,我主要講解了項目的設計方案,使用的技術,遇到的困難,最終的解決方案。

技術面試官就問了兩個問題,一是從.net升級到netcore中間碰到過哪些問題。

第二個基於rabbitmq的分佈式事務是怎麼做的。

然後他們部門經理問了些團隊管理的問題。如何做團隊成員的任務分配,有團隊成員向你提出離職或者漲薪你怎麼處理,團隊的代碼質量如果管控

第三輪:他們的CTO,然後開始又是自我介紹。

只好把之前的又重複一遍,巴拉巴拉。最後就問了一個分佈式事務的解決方案有那些,平時是怎麼使用的。

最後聊了一下我的定位,就是進去是負責他們的平台架構,包括一些公用業務的架構封裝,老架構的netcore升級

第四輪:最後是他們的公司董事長,上來又是先自我介紹。然後問了下職業規劃。

接着就是拿着我的簡歷說這個工作跳動比較頻繁,尤其是從上一家比較大的公司跳槽到一個創業公司是基於一個什麼樣的考慮呢,感覺個人穩定性和職業性規劃都不夠。

博主當時內心的os是黑人問號臉??????我能是基於什麼樣的考慮,我為了世界和平好不好。

然後被大佬教育了一頓,灌輸了一些個人和公司共同體,什麼共贏發展什麼共同成長的理念。

結果:通過,HR小姐姐來談薪資。

只能給到20K,然後還是18k基本工資+2K的級別補貼,說是我進去之後定的級別是T3,

然後每年三四月份和九十月份可以申請調薪調級,強制要求995?????? 我特么跳槽不漲薪就算了你還給我降薪,還995,PASS。

#3 某醫美集團下轄子公司

主營業務:醫美行業的sass軟件

技術框架:GRPC

面試:一輪,技術主管。

招聘崗位:.net架構師

主要問題:依賴注入的生命周期,在框架設計中的應用場景有那些。

在技術選型時主要考慮的因素。

在框架設計時會應用到那些設計模式,主要應用場景是什麼。

對於netcore中間件的理解。

應對系統高併發的解決方案。

聊一聊對微服務的理解,基於netcore的微服務架構是怎麼設計的。

面試結果:通過。但薪資只有20K,哎呦喂,你都對不起你招聘崗位的名字呀。

#4 某物業管理軟件公司

主營業務:做小區物業管理軟件,公司兩百多人。

技術框架:.net mvc 三層

招聘崗位:.net副總監

面試:一輪。總監面試,但是木有問任何技術問題,也木有問任何團隊管理問題。逮者我之前的離職原因各種問。

面試結果:未通過。一臉懵逼的出來,都不知道為啥沒通過。老子也是信了你的邪。

#5 某電商初創企業。

主營業務:拍賣類的電商平台。公司是初創,技術團隊都沒組建完整。

面試:兩輪。

第一輪是他們的一個技術負責人,只是看看了簡歷,然後問了一個讓我哭笑不得問題,就是如果你進入公司,發現周圍人技術都比較菜的時候,你是不是會看不起別人。 笑哭!!!

第二輪是老闆,老闆就是主要負責畫大餅,聊前景,聊機遇。

結果:通過。工資待遇給到稅前24K。

但是我了解到老闆之前做互金,然後平台清盤。具體情況不清楚,大佬,惹不起,躲了躲了。

在這裏一定奉勸各位園友,互金平台或者老闆有互金背景的千萬小心。

我身邊已經不少朋友,被坑到,即使現在沒事,也說不定什麼時候就會被警察找上門。

就有朋友,剛入職公司沒多久,而且公司業務也不是做互金的,結果沒幾天,警察上門,老闆帶走就因為老闆之前做互金,還是出事兒了。

#6 某社交類公司

主營業務:付費社交app,主打東南亞市場

技術框架:.net 三層

招聘崗位:.net高級開發工程師

面試:三輪。

第一輪:部門的CTO面試,互相聊得挺愉快。

主要問了之前的項目微服務怎麼做的,服務拆分的粒度怎麼規劃,整個服務的架構怎麼規劃用到哪些技術。

然後問了數據庫方面的分庫分表怎麼做的,用的什麼中間件,分庫分表後主鍵id如何生成。

應對高併發架構上是怎麼處理的。如何保證redis的高併發高可用。面對緩存穿透、雪崩、擊穿怎麼解決的。

消息隊列的高可用、消息的冪等性,面對消息積壓如何處理。

接着就是聊團隊管理,還是人員管理,任務分配,質量保證這些問題。

接手一個新團隊后如何摸清各成員能力,不同能力的人工作上應該怎麼安排。

還有一個,就是你作為團隊主管你的工作時間是碎片化的,但同時你作為技術leader又要把控技術方案,而做技術是需要時間的連續性,你如何協調這兩者之間的衝突。

挺有意思,只有技術管理一肩挑的團隊才會遇到這種問題了。

最後介紹了一下團隊目前的組織架構,技術方向。嗯,要做.net升級,要做微服務。嗯,最後要轉java。誒,是不是有什麼奇怪的東西,.netcore它不香么。

第二輪:人事面試。嗯,就是問問離職原因,然後介紹了下公司業務發展,前景規劃,入職后的主要工作職能,然後談了下期望薪資。

第三輪:boos面。老闆,沒問什麼問題,就是聊了聊職業規劃,然後么他介紹公司發展方向,前景規劃,我作為一個負責任的捧哏, 當然舔着嘍。

面試結果:通過。薪資談到稅前24K。但五險一金都是最低標準繳納。年終獎說是0到12個月,看績效。

#7 某汽車製造公司的外包崗

面試:外包公司有個技術經理做了一個簡單電話面試。然後就約着到甲方的公司進行面試。面試兩輪,是甲方的兩個平台架構師。問題都大同小異,不贅述了。

面試結果:通過。但博主內心相當糾結,因為對於外包,網上實在是沒有好的評價,但是和兩個面試官聊得蠻愉快。

當初去面試了,也純粹是因為好奇,反正當時面試邀請也少,閑着也是閑着么。

薪資談到23k,對方說還是走了一個特別申請,甲方那邊兒再高給不了。五險一金都是最低標準。

但是HR說這個崗位是甲方為了儲備人才招聘的,我當天面試過後,甲方就把這個崗位招聘關了,只招我一個,等到明年三四月份內部編製出來,我是妥妥轉到甲方。

而且進去之後的工作也是和面試我的那個架構師一起工作,負責他們平台架構規劃。

一開始去面試之前我都說了工資要求和最低標準,滿口說沒問題,結果面試完了就又不行了。你個糟老頭子,壞的很,我信你個鬼。

#8 寫在最後

  中間也還有面試有其他幾家公司,套路問題都差不多,就不在寫出來了。找工作一共花費兩周時間,面試了也有八九家,但真正能給到期望工資的就那麼兩三家。這之間自己在網上主動投遞過,但基本都沒有回信。兩周過去,在回過頭來看,卻發現網上再找不到其他合適的崗位了,不是已經面試過,就是投遞了沒反應。到最後發現,我能選擇的就只有那麼幾家公司。而且,最嚴重的一個感受就是,我翻遍了所有的招聘網站,我目前所要的工資,已經是.net行業的天花板,往上沒有空間了。.net高級開發也好、.net架構師也好、技術經理也罷,能給到工資25K就已經是到頂了,而且崗位特別少。然後做cs方向的,價格開的比bs方向的還能高一些,頂薪能到三萬。做服務的.net被java搶佔了太多市場,即便有很多公司,初期是用.net做的,即便現在netcore已經跨平台,但公司做微服務還是要轉java,我真的好想問一句netcore它不香么,vs它不香么,都咋想的。

#9 尾篇

  最後的最後。整理一下博主在做netcore微服務所用到的相關技術,做個整體的總結。後續會一點一點具體介紹,希望能形成一個系列,希望最後能堅持寫完。

  服務註冊/發現:consul或zookeeper,各有優劣,個人傾向consul

  分佈式通訊:restful api形式或rpc。

  分佈式事件總線:推薦使用cap。cap同時支持 RabbitMQ,Kafka,Azure Service Bus 等進行底層之間的消息發送,同時內置了TCC實現。

       網關、熔斷、降級、限流:ocelot網關,應該是當下netcore平台下最火熱的網關開源項目了。同時集成了polly來滿足熔斷、降級、限流的功能要求。

  配置中心:攜程的開源項目Apollo。博主之前是為了業務需求自己寫的,不具通用性。

  微服務監控:分佈式調用鏈跟蹤zipkin和skywalking,同時還可監控服務性能。推薦使用skywalking,對代碼無侵入。

        日誌監控ELK,這個不需要多介紹了,文章太多了。

  持續集成自動部署:GitLab+Jenkins+k8s

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

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

分類
發燒車訊

Java編程思想——第14章 類型信息(二)反射

六、反射:運行時的類信息

  我們已經知道了,在編譯時,編譯器必須知道所有要通過RTTI來處理的類。而反射提供了一種機制——用來檢查可用的方法,並返回方法名。區別就在於RTTI是處理已知類的,而反射用於處理未知類。Class類與java.lang.reflect類庫一起對反射概念進行支持,該類庫包含Field、Method以及Constructor(每個類都實現了Member接口)。這些類型是由JVM運行時創建的,用來表示未知類種對應的成員。使用Constructor(構造函數)創建新的對象,用get(),set()方法讀取和修改與Field對象(字段)關聯的字段,用invoke()方法調用與Method對象(方法)關聯的方法。這樣,匿名對象的類信息就能在運行時被完全確定下來,而在編譯時不需要知道任何事情。

  其實,當反射與一個未知類型的對象打交道時,JVM只是簡單地檢查這個對象,在做其他事情之前必須先加載這個類的Class對象。因此,那個類的.class文件對於JVM來說必須時可獲取的(在本地或網絡上)所以反射與RTTI的區別只在於:對於RTTI來說,編譯器在編譯時打開和檢查.class文件,而對於反射來說,.class文件在編譯時是不可獲得的,所以是運行時打開和檢查.class文件。反射在需要創建更動態的代碼時很有用。

七、動態代理

  代理是基本的設計模式:為其他對象提供一種代理,以便控制對象,而在對象前或后加上自己想加的東西。

interface Interface {
    void doSomething();

    void doSomeOtherThing(String args);
}

class RealObject implements Interface {

    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

    @Override
    public void doSomeOtherThing(String args) {
        System.out.println("doSomeOtherThing" + args);
    }
}

class SimpleProxy implements Interface {

    private Interface proxyId;

    public SimpleProxy(Interface proxyId) {
        this.proxyId = proxyId;
    }

    @Override
    public void doSomething() {
        //將原有的doSomething 方法添加上了一個輸出 這就是代理之後新增的東西
        //就好比某公司代理遊戲后加的內購
        System.out.println("SimpleProxy doSomething");
        proxyId.doSomething();
    }

    @Override
    public void doSomeOtherThing(String args) {
        proxyId.doSomeOtherThing(args);
        //新增的東西可以在原有之前或之後都行
        System.out.println("SimpleProxy doSomeOtherThing" + args);
    }
}

public class SimpleProxyDemo {
    static void consumer(Interface i) {
        i.doSomething();
        i.doSomeOtherThing(" yi gi woli giao");
    }

    public static void main(String[] args) {
        consumer(new RealObject());
        System.out.println("-----  -----  -----");
        consumer(new SimpleProxy(new RealObject()));
    }
}

結果:

doSomething
doSomeOtherThing yi gi woli giao
-----  -----  -----
SimpleProxy doSomething
doSomething
doSomeOtherThing yi gi woli giao
SimpleProxy doSomeOtherThing yi gi woli giao

  因為consumer()接受的Interface,所以無論是RealObject還是SimpleProxy,都可以作為參數,而SimpleProxy插了一腳 代理了RealObject加了不少自己的東西。

  java的動態代理更前進一步,因為它可以動態創建代理並動態地處理對所代理方法的調用。在動態代理上所做的所有調用都會被重定向到單一的調用處理器上,它的工作是揭示調用的類型並確定相應的對策。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Interface {
    void doSomething();

    void doSomeOtherThing(String args);
}

class RealObject implements Interface {

    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }

    @Override
    public void doSomeOtherThing(String args) {
        System.out.println("doSomeOtherThing" + args);
    }
}

class DynamicProxyHandler implements InvocationHandler {
    private Object proxyId;

    public DynamicProxyHandler(Object proxyId) {
        this.proxyId = proxyId;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("**** proxy:" + proxy.getClass() + ", method" + method + ", args:" + args);
        if (args != null) {
            for (Object arg : args) {
                System.out.println(" " + arg);
            }
        }
        return method.invoke(proxyId, args);
    }
}

public class SimpleProxyDemo {
    static void consumer(Interface i) {
        i.doSomething();
        i.doSomeOtherThing(" yi gi woli giao");
    }

    public static void main(String[] args) {
        RealObject realObject = new RealObject();
        consumer(realObject);
        System.out.println("-----  -----  -----");
     //動態代理 可以代理任何東西 Interface proxy
= (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(realObject)); consumer(proxy); } }

結果:

doSomething
doSomeOtherThing yi gi woli giao
-----  -----  -----
**** proxy:class $Proxy0, methodpublic abstract void Interface.doSomething(), args:null
doSomething
**** proxy:class $Proxy0, methodpublic abstract void Interface.doSomeOtherThing(java.lang.String), 
args:[Ljava.lang.Object;@7ea987ac  yi gi woli giao
doSomeOtherThing yi gi woli giao

通過Proxy.newProxyInstance()可以創建動態代理,這個方法需要三個參數:

1. 類加載器:可以從已經被加載的對象中獲取其類加載器;

2. 你希望該代理實現的接口列表(不可以是類或抽象類,只能是接口);

3. InvocationHandler接口的一個實現;

在 invoke 實現中還可以根據方法名處對不同的方法進行處理,比如:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("**** proxy:" + proxy.getClass() + ", method" + method + ", args:" + args);
        if (args != null) {
            for (Object arg : args) {
                System.out.println(" " + arg);
            }
        }
        if (method.getName().equals("doSomething")) { System.out.println("this is the proxy for doSomething"); } return method.invoke(proxyId, args);
    }

還可以對參數或方法進行更多的操作因為 你已經得到了他們 盡情的使用你的代理權吧 ~~ 先加它十個內購。

九、接口與類型信息

  interface關鍵字的一種重要目標就是允許程序員隔離構件,進而降低耦合。反射,可以調用所有方法,甚至是private。唯獨final是無法被修改的,運行時系統會在不拋任何異常的情況接受任何修改嘗試,但是實際上不會發生任何修改。

    void callMethod(Object a, String methodName) throws Exception {
        Method method = a.getClass().getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(a);
    }

 

 

  

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?