2019年2月18日

Keras 手寫阿拉伯數字辨識

keras 是 python 語言的機器學習套件,後端能使用 Google TensorFlow, Microsoft CNTKTheano 運作。其中 Theano 在 2017/9/28 就宣佈在 1.0 後就不再更新。一般在初學機器學習時,都是用手寫阿拉伯數字 MNIST 資料集進行測試,kaggle Digit Recognizer 有針對 MNIST data 的機器學習模型的評比,比較厲害的,都可以達到 100% 的預測結果。

CentOS 7 Keras, TensorFlow docker 測試環境

docker run -it --name c1 centos:latest /bin/bash

安裝一些基本工具,以及 openssh-server

#yum provides ifconfig

yum install -y net-tools telnet iptables sudo initscripts
yum install -y passwd openssl openssh-server

yum install -y wget vim

測試 sshd

/usr/sbin/sshd -D
Could not load host key: /etc/ssh/ssh_host_rsa_key
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
Could not load host key: /etc/ssh/ssh_host_ed25519_key

缺少了一些 key

ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
#直接 enter 即可

ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
#直接 enter 即可

ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key -N ""

ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

修改 UsePAM 設定

vi /etc/ssh/sshd_config
# UsePAM yes 改成 UsePAM no
UsePAM no

再測試看看 sshd

/usr/sbin/sshd -D&

修改 root 密碼

passwd root

離開 docker

exit

以 docker ps -l 找到剛剛那個 container 的 id

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
107fb9c3fc0d        centos:latest       "/bin/bash"         7 minutes ago       Exited (0) 2 seconds ago                       c1

將 container 存成另一個新的 image

docker commit 107fb9c3fc0d centosssh

以新的 image 啟動另一個 docker instance

(port 10022 是 ssh,15900 是 vnc)

(--privileged=true 是避免 systemd 發生的 Failed to get D-Bus connection: Operation not permitted 問題)

docker run -d -p 10022:22 -p 15900:5900 -e "container=docker" --ulimit memlock=-1 --privileged=true -v /sys/fs/cgroup:/sys/fs/cgroup --name test centosssh /usr/sbin/init

docker exec -it test /bin/bash

現在可以直接 ssh 登入新的 docker machine

ssh root@localhost -p 10022

修改 timezone, locale

timedatectl set-timezone Asia/Taipei

把 yum.conf 的 overrideinstalllangs 註解掉

vi /etc/yum.conf

#override_install_langs=en_US.utf8
yum -y -q reinstall glibc-common
localectl list-locales|grep zh
# 會列出所有可設定的 locale
zh_CN
zh_CN.gb18030
zh_CN.gb2312
zh_CN.gbk
zh_CN.utf8
zh_HK
zh_HK.big5hkscs
zh_HK.utf8
zh_SG
zh_SG.gb2312
zh_SG.gbk
zh_SG.utf8
zh_TW
zh_TW.big5
zh_TW.euctw
zh_TW.utf8

# 將 locale 設定為 zh_TW.utf8
localectl set-locale LANG=zh_TW.utf8

安裝視窗環境及VNC

ref: https://www.jianshu.com/p/38a60776b28a

yum groupinstall -y "GNOME Desktop"

# 預設啟動圖形介面
unlink /etc/systemd/system/default.target
ln -sf /lib/systemd/system/graphical.target /etc/systemd/system/default.target

# 安裝 vnc server
yum -y install tigervnc-server tigervnc-server-module 

# vnc 預設的port tcp 5900,則組態檔複製時在檔名中加入0,如vncserver@:0.service,如果要使用其他的port,就把0改為其他號碼
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@:0.service

vi /etc/systemd/system/vncserver@:0.service
# 修改中間的部分
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'
ExecStart=/usr/sbin/runuser -l root -c "/usr/bin/vncserver %i -geometry 1280x1024"
PIDFile=/root/.vnc/%H%i.pid
ExecStop=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'


# 執行 vncpasswd 填寫 vnc 密碼
su root
vncpasswd

# 退出 container
exit

# restart docker container
docker restart test

# 進入 docker container
docker exec -it test /bin/bash

# 啟動 service
systemctl daemon-reload
systemctl start vncserver@:0.service
systemctl enable vncserver@:0.service

# 開啟防火牆允許VNC的連線,以及重新load防火牆,這邊多開放了port 5909。
firewall-cmd --permanent --add-service="vnc-server" --zone="public"
#firewall-cmd --add-port=5909/tcp --permanent
firewall-cmd --reload

vncserver -list

# 以 vnc client 連線,連接 localhost:15900

如果用 vnc 連線到 docker 機器,後面測試時,matplotlib 可直接把圖形畫在視窗上,就不用存檔。

安裝 TensorFlow, python 3.6 開發環境

yum -y install centos-release-scl
yum -y install rh-python36

python --version
# Python 2.7.5

# 目前還是 python 2.7,必須 enable 3.6
scl enable rh-python36 bash

python --version
# Python 3.6.3

但每次登入都還是 2.7

vi /etc/profile.d/rh-python36.sh

#!/bin/bash
source scl_source enable rh-python36

接下來每次登入都是 3.6

安裝 TensorFlow

pip3 install --upgrade tensorflow

# 更新 pip
pip3 install --upgrade pip

簡單測試,是否有安裝成功

# python
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

# 會列印出這樣的結果
# b'Hello, TensorFlow!'

#---------

python -c "import tensorflow as tf; tf.enable_eager_execution(); print(tf.reduce_sum(tf.random_normal([1000, 1000])))"
# 會列印出這樣的結果
# tf.Tensor(12.61731, shape=(), dtype=float32)

再安裝 keras, matplotlib (需要 tk library)

pip3 install keras

yum -y install rh-python36-python-tkinter
pip3 install matplotlib

阿拉伯數字辨識

MNIST 是一個包含 60,000 training images 及 10,000 testing images 的手寫阿拉伯數字的測試資料集。資料集的每個圖片都是解析度為 28*28 (784 個 pixel) 的灰階影像, 每個像素為 0~255 之數值。

One-Hot Encoding 就是一位有效編碼,當有 N 種狀態,就使用 N 位狀態儲存器的編碼,每一個狀態都有固定的位置。

例如阿拉伯數字就是 0 ~ 9,就使用這樣的編碼方式

[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.]    代表 0
...
[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]    代表 5
...
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]    代表 9

程式處理的步驟如下:

  1. 取得訓練資料:目前是直接使用既有的 MNIST 資料集,利用這些既有的資料,進行機器學習。
  2. 機器訓練,取得模型:進行機器訓練,取得訓練後結果的模型,未來就可以利用這個模型,判斷新進未知的資料
  3. 評估:利用 MNIST 資料集的測試資料,評估模型判斷後的結果跟正確結果的差異,取得這個模型的準確率。
  4. 預測:未來可利用這個模型,判斷並預測新進資料的結果。當然這會因為上一個步驟的準確度,有時候會失準,不一定會完全正確。

以下這個例子是使用 Sequential 線性的模型,input layer 是 MNIST 60000 筆訓練資料,中間是一層有 256 個變數的 hidden layer,最後是 10 個變數 (0~9) 的 output layer。機器學習就是在產生 input layer 到 hidden layer,以及 hidden layer 到 output layer 中間的 weight 權重。

input layer --- W(i,j) ---> hidden layer (256 個變數) --- W(j, k) ---> output layer (0~9) 

測試程式 test.py

import numpy as np
from keras.models import Sequential
from keras.datasets import mnist
from keras.layers import Dense, Dropout, Activation, Flatten
# 用來後續將 label 標籤轉為 one-hot-encoding
from keras.utils import np_utils
from matplotlib import pyplot as plt

# 載入 MNIST 資料庫的訓練資料,並分為 training 60000 筆 及 testing 10000 筆 data
(x_train, y_train), (x_test, y_test) = mnist.load_data()


# 將 training 的 label 進行 one-hot encoding,例如數字 7 經過 One-hot encoding 轉換後是 array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0.], dtype=float32),即第7個值為 1
y_train_onehot = np_utils.to_categorical(y_train)
y_test_onehot = np_utils.to_categorical(y_test)

# 將 training 的 input 資料轉為 28*28 的 2維陣列
# training 與 testing 資料數量分別是 60000 與 10000 筆
# X_train_2D 是 [60000, 28*28] 的 2維陣列
x_train_2D = x_train.reshape(60000, 28*28).astype('float32')
x_test_2D = x_test.reshape(10000, 28*28).astype('float32')

x_train_norm = x_train_2D/255
x_test_norm = x_test_2D/255


# 建立簡單的線性執行的模型
model = Sequential()
# Add Input layer, 隱藏層(hidden layer) 有 256個輸出變數
model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu'))
# Add output layer
model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))

# 編譯: 選擇損失函數、優化方法及成效衡量方式
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])


# 進行 model 訓練, 訓練過程會存在 train_history 變數中
# 將 60000 張 training set 的圖片,用 80% (48000張) 訓練模型,用 20% (12000張) 驗證結果
# epochs 10 次,就是訓練做了 10 次
# batch_size 是 number of samples per gradient update,每一次進行 gradient descent 使用幾個 samples
# verbose 是 train_history 的 log 顯示模式,2 表示每一輪訓練,列印一行 log
train_history = model.fit(x=x_train_norm, y=y_train_onehot, validation_split=0.2, epochs=10, batch_size=800, verbose=2)

# 用 10000 筆測試資料,評估訓練後 model 的成果(分數)
scores = model.evaluate(x_test_norm, y_test_onehot)
print()
print("Accuracy of testing data = {:2.1f}%".format(scores[1]*100.0))

# 預測(prediction)
X = x_test_norm[0:10,:]
predictions = model.predict_classes(X)
# get prediction result
print()
print(predictions)

# 模型訓練結果 結構存檔
from keras.models import model_from_json
json_string = model.to_json()
with open("model.config", "w") as text_file:
    text_file.write(json_string)

# 模型訓練結果 權重存檔
model.save_weights("model.weight")


# 顯示 第一筆訓練資料的圖形,確認是否正確
#plt.imshow(x_train[0])
#plt.show()
#plt.imsave('1.png', x_train[0])

plt.clf()

plt.plot(train_history.history['loss'])
plt.plot(train_history.history['val_loss'])
plt.title('Train History')
plt.ylabel('loss')
plt.xlabel('Epoch')
plt.legend(['loss', 'val_loss'], loc='upper left')
#plt.show()
plt.savefig('loss.png')

執行結果

# python test.py
Using TensorFlow backend.
Train on 48000 samples, validate on 12000 samples
Epoch 1/10
 - 2s - loss: 0.7582 - acc: 0.8134 - val_loss: 0.3195 - val_acc: 0.9117
Epoch 2/10
 - 1s - loss: 0.2974 - acc: 0.9160 - val_loss: 0.2473 - val_acc: 0.9307
Epoch 3/10
 - 2s - loss: 0.2346 - acc: 0.9350 - val_loss: 0.2060 - val_acc: 0.9425
Epoch 4/10
 - 2s - loss: 0.1930 - acc: 0.9465 - val_loss: 0.1741 - val_acc: 0.9522
Epoch 5/10
 - 2s - loss: 0.1631 - acc: 0.9539 - val_loss: 0.1529 - val_acc: 0.9581
Epoch 6/10
 - 2s - loss: 0.1410 - acc: 0.9604 - val_loss: 0.1397 - val_acc: 0.9612
Epoch 7/10
 - 1s - loss: 0.1225 - acc: 0.9662 - val_loss: 0.1301 - val_acc: 0.9639
Epoch 8/10
 - 1s - loss: 0.1075 - acc: 0.9695 - val_loss: 0.1171 - val_acc: 0.9668
Epoch 9/10
 - 1s - loss: 0.0948 - acc: 0.9744 - val_loss: 0.1123 - val_acc: 0.9681
Epoch 10/10
 - 1s - loss: 0.0855 - acc: 0.9771 - val_loss: 0.1047 - val_acc: 0.9700
10000/10000 [==============================] - 1s 57us/step

Accuracy of testing data = 97.1%

[7 2 1 0 4 1 4 9 6 9]

Model Persistence

要儲存訓練好的模型,有兩種方式

  1. 結構及權重分開儲存

    儲存模型結構,可儲存為 JSON 或 YAML

    from keras.models import model_from_json
    json_string = model.to_json()
    with open("model.config", "w") as text_file:
      text_file.write(json_string)

    儲存權重

    model.save_weights("model.weight")

    讀取結構及權重

    import numpy as np  
    from keras.models import Sequential
    from keras.models import model_from_json
    with open("model.config", "r") as text_file:
      json_string = text_file.read()
      model = Sequential()
      model = model_from_json(json_string)
      model.load_weights("model.weight", by_name=False)
  2. 合併儲存結構及權重

    合併儲存時,檔案格式為 HDF5

    from keras.models import load_model
    
    model.save('model.h5')  # creates a HDF5 file 'model.h5'

    讀取模型

    from keras.models import load_model
    
    # 載入模型
    model = load_model('model.h5')

References

【深度學習框架 Theano 慘遭淘汰】微軟數據分析師:為何曾經熱門的 Theano 18 個月就陣亡?

【Python】CentOS7 安裝 Python3

Install TensorFlow with pip

撰寫第一支 Neural Network 程式 -- 阿拉伯數字辨識

MyNeuralNetwork/0.py

改善 CNN 辨識率

mnist-cnn/mnist-CNN-datagen.ipynb

深度學習 TensorFlow

2019年2月11日

erlang 如何支援多個設定檔

通常 erlang project 會將設定的資料放在 sys.config 裡面,同樣的,很多 library 也會在說明文件裡面提到,要將該 library 相關的設定資訊,寫在 sys.config 裡面,在規模稍大的 project 就會看到一個很冗長複雜的設定檔。

但其實在 elang config 的標準文件 sys.config 說明裡面有提到,sys.config 裡面的語法是

[{Application, [{Par, Val}]} | File].

File 的部分就是附加的設定檔,erlang 會將所有設定檔裡面,對於某個 application 相關的所有參數,依照檔案順序整合在一起

首先利用 rebar 產生一個測試的 application myapp,並進行編譯

$ rebar create-app appid=myapp
==> erltest (create-app)
Writing src/myapp.app.src
Writing src/myapp_app.erl
Writing src/myapp_sup.erl

$ rebar compile
==> erltest (compile)
Compiled src/myapp_app.erl
Compiled src/myapp_sup.erl

然後依照以往的做法,將 myapp 的參數寫在 sys.config 裡面

[
    { myapp, [
        {par1,val1},
        {par2,val2}
    ]},
    { myapp, [
        {par1,newval1},
        {par3,newval3}
    ]}
].

注意,我們刻意將 myapp 的參數分成兩個部分撰寫,然後我們可以在 console 啟動 myapp,並利用 application:get_all_env(myapp). 取得 myapp 所有的參數進行測試。

$ erl -pa ebin -config sys
1> application:get_all_env(myapp).
[]
2> application:load(myapp).
ok
3> application:get_all_env(myapp).
[{par2,val2},
 {par1,newval1},
 {included_applications,[]},
 {par3,newval3}]

透過測試結果可以發現 myapp 的三個參數,是依照參數定義的順序累加並覆蓋得到的。


接下來,我們建立一個新的設定檔 conf/node1.config,內容為

[
    { myapp, [
        {par1,node1_val1},
        {par2,node1_val2},
        {parN,node1_valN}
    ]}
].

project 的目錄及檔案是這樣,也就是增加了一個 conf 的目錄

conf\
    node1.config
ebin\
src\
    myapp_app_erl
    myapp_sup.erl
    myapp_app.src
sys.config

然後修改 sys.config,把 node1.config 的設定附加在後面

[
    { myapp, [
        {par1,val1},
        {par2,val2}
    ]},
    { myapp, [
        {par1,newval1},
        {par3,newval3}
    ]}
    , "conf/node1.config"
].

這時候測試得到的結果會發現,node1.config 的設定覆蓋了原本 sys.config 裡面的設定

$ erl -pa ebin -config sys
1> application:get_all_env(myapp).
[]
2> application:load(myapp).
ok
3> application:get_all_env(myapp).
[{par2,node1_val2},
 {par1,node1_val1},
 {included_applications,[]},
 {par3,val3},
 {parN,node1_valN}]

我們可以將 sys.config 的機制,運用在兩個地方

  1. 分離 library 的設定檔 將某些 library 的設定,以獨立的設定檔方式撰寫
  2. 覆寫獨立的 node 的設定檔 將新的 node 的設定檔,也就是每一個 node 不同的設定資料,不修改既有的 sys.config 而是寫在 node 的設定檔,覆寫既有的設定

不過如果檔案的部分可以支援這樣的寫法 "conf/*.config",對於 library 設定檔的整合,會比較簡單。但如果支援了 *.config,就無法自定設定檔的引用順序,可能只能依照字母的順序排序了。

References

A little known fact about Erlang's sys.config

An easy way to handle configuration parameters in Erlang

節點啟動後自動連接其它配置節點

[erlang-questions] Multiple application configurations in multiple files

2019年1月28日

Google TCP BBR Congestion Control

自 1980 年代網際網路崛起,到 1990 年代快速發展開始,就產生了 TCP 以及 UDP 的 IP 網路,1983年1月1日,ARPANet要求未來所有的網路傳輸都使用 IP 網路,統一了開放網路的規格。

網路通道就像是一條水管/水道,這條通道用來傳送資料,因為沒有即時調節流量的機制,TCP 透過接收端發送確認已經收到封包的 ACK,來判斷是否發送的速度太快,流量控制就是在控制資料發送端的發送速度。

BBR (Bottleneck Bandwidth and Round-trip propagation time) 演算法是 Google 在 2016 年提出的流量控制演算法,他透過有效頻寬偵測機制,並降低網路節點的 buffer 使用量,藉由減低重傳的效能消耗問題,提高頻寬使用率。

流量控制是在做什麼工作

把網路傳輸通道 TCP 想像為一條從山頂的水庫到目的地海洋的河流或水管,水庫有大量水資源,希望能運用水道以最短時間送到海洋,但是水庫並不知道這個水道有多寬,能以多少速度的水量放水。如果水放得太慢,無法享用水道的總流量,水放得太急,有可能會超過水道容量,而讓水漫出水道,造成淹水的情況。

流量控制,就是控制水庫放水速度的一個演算法,因為水道容水量的情況是隨時都在改變的,問題在於,並沒有即時回報的機制,可以隨時調節流量,另外,由於發送端貪婪的本性,他會希望盡可能完全佔用網路水道的所有容水量。

但總不能一直送一直送,也不管接收端有沒有收到資料吧。TCP 的機制是,在接收到收到封包時,會回覆一個 ACK 封包,確認已經收到了,透過 ACK 的偵測,就可以知道現在發送的速度,是不是已經超過了網路通道的容量,如果發生封包遺失的狀況時,發送端就知道要降低發送速度了。

雖然是開放網路,公平競爭,但實際上還是看誰最會搶佔網路資源,因此有些演算法強調搶佔的特性,會侵蝕掉使用其他演算法的 TCP 連線。

但從提供網路服務這一端來看,他希望所有來使用服務的使用者,可以公平地使用伺服器對外的網路通道,而且要盡可能減少因為 TCP 重傳的機制,造成無效的網路資源浪費,這時候,採用強調發送端公平使用網路的演算法,會比較有利。

因為網路通道本身不穩定的特性,如果中間有遇到無線網路時,這種情況會更嚴重,因此網路的路由器本身,通常都會加上 Buffer 的機制,能夠讓暫時無法發送的網路資料,存放在 Buffer 裡面,希望能藉此改善整體網路的效能。就像是水道中間,會加上一些滯洪池的機制,預防水量瞬間增加的問題。

然而這些 Buffer 卻因為 TCP 流量控制貪婪的本性,而被濫用,因為 TCP 希望自己能盡可能使用到網路的最大流量,所以也會盡可能將自己的封包,把路由器的 Buffer 塞滿,讓自己的速度更快,這也會造成 Bufferbloat 的問題。

網路上有許多網路節點,除了控制路由以外,有些節點還增加了 QoS(Quality of Service) 或是 Traffic Shaping 的機制,這也是基於剛剛提到的 Buffer 而提供的功能,因為有了 Buffer,路由器可以先將需要傳送的資料,放入不同等級的 Buffer 裡面,保留固定的傳送頻寬給具有高傳輸權限的網路封包。

BBR 演算法

BBR 演算法的細節,以這兩篇文章的說明比較清楚。

TCP BBR擁塞控制算法解析

Linux Kernel 4.9 中的 BBR 算法與之前的 TCP 擁塞控制相比有什麼優勢?

要不然就要看原始發表的論文

BBR: Congestion-Based Congestion Control Measuring bottleneck bandwidth and round-trip propagation time

BBR 解決問題的方案有兩點

  1. 因無法區分 congestion packet loss 及 error packet loss,BBR 考慮讓網路不產生 packet loss
  2. 因為把 buffer 塞滿可產生最大流量,但也會造成 RTT 降低,BBR 交替進行頻寬及 delay(RTT) 偵測

BBR 的四個狀態

  1. STARTUP 採用標準的 slow start 方式,指數增加發送速度,發現頻寬被佔滿時,就進入 DRAIN 的階段
  2. DRAIN 降低發送速度,將佔用的 buffer 排空
  3. PROBE_BW 改變發送速度進行頻寬偵測,在一個 RTT 內增加發送速度,如果 RTT 沒有改變,就降低發送速度,排空先前多送的封包,在六個 RTT 內使用這個發送速度
  4. PROBE_RTT 每經過 10s,如果沒有得到一個更低的延遲時間,就進入延遲偵測的階段,持續 200ms (或一個 RTT),這個階段固定發送 4 packets,偵測得到的最小 delay 時間作為最新的延遲時間

一些實測的結果

GCP採用新演算法TCP BBR 傳輸率將提高2700倍!

又一個 TCP BBR 的測試結果

spotify: Smoother Streaming with BBR

BBR 阻塞算法,真是黑科技

這些是使用了 BBR 以後的測試說明,全部都是正面,效能有改善的結果。

Google 在宣傳 BBR 時,都說明只要修改 Server 的部分,讓 Server 以 BBR 演算法運作,原因在於,流量控制演算法著眼的重點,是大量資料的發生源,會產生大量資料,發送出來的地方。只要修改 Server 的原因是他們是針對 Youtube 這樣提供串流服務的 Server 套用 BBR,換句話說,BBR 適用於 Sender Side。

因為串流影音的特性,就是需要一條長時間運作且傳輸量穩定的網路通道,這正好符合了 BBR 提供的流量結果,因為沒有使用到 Router 的 buffer,也沒有大量的重傳封包抵銷了網路的效能。

如果是類似 Hangouts Meeting 這樣的多人雙向影音的應用,因為客戶端 client side 如果沒有使用 BBR,就可能會產生不穩定的個人影音發生源,即使 Server Side 提供了 BBR,也無法形成一個有良好體驗的網路環境。

並不是所有人都認為 BBR 是有用的,以 令人躁動一時且令人不安的TCP BBR算法 這篇文章提出的論點來看,BBR 適合用在速度比較穩定的網路通道上,因為增速快,降速慢的特性,並不適用於忽快忽慢的網路。

我個人的想法是,只要決定了網路通道,固定了網路通道,那麼大部分的情況,網路是穩定的,該文章提出的問題,說明的並不恰當,如果通道上有些路徑的頻寬比較小,這會讓整條網路通道都因為這個最小頻寬的一段路而降速。因為流量控制只會根據接收端的 ACK 來調節,沒辦法知道中間經過每一個網路節點的速度。

會發生問題的地方,應該是網路忽快忽慢的情況,因為變化太大,演算法無法很快地調節到最佳的傳送速度,但這應該是所有演算法都會遇到的問題,BBR 改善的結果已經有顯著的效果了。

如何啟用 BBR

How to Deploy Google BBR on CentOS 7

開啟TCP BBR擁塞控制算法

因為 BBR 已經有在 linux kernel 4.9+ 的版本上實作,在各 linux distribution 的安裝方式,都是安裝新的 kernel repo,將 kernel 更新到 4.9+。

然後在 /etc/sysctl.conf 增加這兩行設定

net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
sysctl -p

用以下指令確認有沒有安裝成功

sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
lsmod | grep bbr

BBR 可以終結流量控制的問題嗎?

這個 wiki 上的漫畫說明了現實的狀況,原本 BBR 的開發者,想要設計一個新的演算法,打敗既有12種 TCP Congestion Control 演算法,一統江湖,三年後,終於在 Linux 4.9 版 kernel 實現了 TCP BBR,但還是有某些缺陷,而現在變成了有 13 種 TCP Congestion Control 演算法。

https://upload.wikimedia.org/wikipedia/commons/3/34/Comic_strips_Linux_BBR.svg

References

TCP BBR

TCP擁塞控制

Faster Networking with TCP BBR

TCP BBR : Magic dust for network performance

Google最新tcp擁塞控制算法BBR解析