0%

前言

因為不小心手殘把 local 的 Hexo 設定砍了..
所以藉此來紀念我的手殘及記錄一下我的 Hexo blog 做了哪些設定


設定 deploy 到 GitHub page 上

安裝套件

1
npm install --save hexo-deployer-git

調整 _config.yml

1
2
3
deploy:
type: git
repo: <repository url>

NexT

安裝 NexT

1
npm install hexo-theme-next --save

將 NexT 的設定檔複製出來

1
cp node_modules/hexo-theme-next/_config.yml _config.next.yml

把 _config.yml 中的 theme 改成使用 NexT

1
theme: next

Sitemap

1
npm install hexo-generator-sitemap --save

外部連結 nofollow

1
npm install hexo-filter-nofollow --save

Minifier

安裝套件

1
npm install hexo-all-minifier --save

在 _config.yml 中加入這個

1
all_minifier: true

搜尋

1
npm install hexo-generator-searchdb --save

打開人數統計

把 busuanzi_count 的 enable 改成 true

1
2
busuanzi_count:
enable: true

使用 Gitalk 當留言板

[Gitalk]评论系统

新增 404 頁面

(23) 試著學 Hexo - SEO 篇 - 新增你的 404 頁面

文章置頂

1
npm install hexo-generator-topindex --save

參考資料

前言

在面試的時候被問到 GIL,雖然我是直接回答說我看不懂,但是仔細想想上一次看也是快一年前了,於是想趁報到前再來挑戰一次 GIL

主要會分兩個部分來講

  1. GIL 是什麼
  2. GIL 會造成什麼影響
  3. 如何避免 GIL 造成的問題
  4. 總結

GIL 是什麼?

In CPython, the global interpreter lock, or GIL, is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. The GIL prevents race conditions and ensures thread safety. A nice explanation of how the Python GIL helps in these areas can be found here. In short, this mutex is necessary mainly because CPython’s memory management is not thread-safe.

簡單來說,GIL 是一個 mutex,用來避免在 multi-threading 的情況下發生 race condition 並確保 thread safety

source: https://wiki.python.org/moin/GlobalInterpreterLock

這邊又出現了三個名詞:

  • mutex
  • race condition
  • thread safe

接著先讓我們搞懂這三個東西是什麼再接著說下去

因為其他兩個名詞都跟 race condition 有關,所以在講其他兩個之前我們得先了解 race condition 是什麼

Race Condition

multi-threading 的情況下,每個 thread 的資源是共享的,所以不同的 thread 可以同時讀寫同一個變數,這樣就會造成程式輸出的結果有可能會因為不同 thread 執行的先後而導致輸出完全不一樣,例如:

我們現在用兩個 thread 跑同樣的程式 count++,假設兩個 thread 分別是 t1, t2,執行程式的過程都是一樣,要經過下面這兩步

  1. 拿到 count 的值
  2. 把 count 的值 + 1 後存回去

那當執行順序是這樣的話

count 預設值為 0

  1. t1 拿 count 值 -> count = 0
  2. t2 拿 count 值 -> count = 0
  3. t1 把 count + 1 (0 + 1 = 1) 存回去 -> count = 1
  4. t2 把 count + 1 (0 + 1 = 1) 存回去 -> count = 1

我們原本的預期行為是 count = 2,但是因為變數共用再加上這樣的執行順序就有可能導致 count = 1,這樣的狀況我們就稱為 race condition

Mutex

Mutex 就是一種避免發生 race condition 的方式,它的做法就是把共用的區塊加上一把鎖,在有其中一個 thread 正在讀寫這個區塊時,其他的 thread 就不能讀寫這個區塊,加上去後上面的狀況就會變成這樣

  1. t1 拿 count 值,把 count 鎖起來 -> count = 0
  2. t2 拿 count 值,count 被鎖了,所以拿不到 count 值
  3. t1 把 count + 1 (0 + 1 = 1) 存回去,count 使用完了,把 count 的鎖打開 -> count = 1
  4. (因為 count 的鎖被解開了)t2 拿 count 值 -> count = 1
  5. t2 把 count + 1 (0 + 1 = 1) 存回去 -> count = 2

這樣的值就跟我們預期中的一樣了

Thread Safety

可以簡單理解成,不會發生 race condition 的狀況就是 thread safety


GIL 會造成什麼影響

了解 GIL 是什麼後,我們就可以來看一下所以 GIL 造成什麼影響

這邊我們要先分成 I/O bound 跟 CPU bound 兩種不同類型的程式來看

  1. I/O bound
    在 I/O bound 的程式下,只要執行 blocking 的 system call,系統就會釋放 CPU 資源給其他 thread 使用,所以 GIL 對 I/O bound 的影響並不大

  2. CPU bound
    CPU bound 這邊要再分成單核跟多核的狀況來看

    在單核的狀況下就是不同的 thread 輪流使用 CPU 資源,整體上除了跑起來從原本預期的 parallel 變成 concurrent 之外沒什麼太大的影響

    但是在多核的狀況下,如果兩個執行 CPU bound 任務的 thread 在不同的核心上執行,也許會出現以下的情況,其中一個核心上的 thread 在拿到 GIL 後執行任務時,另一個核心上的 thread 不停的搶 GIL 失敗,於是該核心長期呈現瞎忙的狀況


如何避免 GIL 造成的問題

GIL 只有在 CPython 上有,所以可以換其他的 interpreter,如:PyPy、Jython,如果在不能換掉 CPython 的狀況下,可以改用 multi-processing 跑 CPU bound 的任務


總結

  1. GIL 的作用是避免產生 race condition 並確保 thread safety
  2. GIL 對 CPU bound 的任務影響比較大
  3. 可以透過換其他的 interpreter 或是改用 multi-processing 來避免 GIL 產生的效能問題

參考資料

前言

面試的時候被考到一題情境題覺得蠻有趣的,於是決定記錄下來


面試過程

面試官:今天有一個網站是 3-tier 的架構(前端、後端、資料庫),某天你發現首頁 load 不出來,這時候你會怎麼做?

我:打開瀏覽器的 console 看 network 那邊的 request 狀態是怎樣

面試官:你打開了,看到 request 沒有回傳 response,就卡著

我:這樣看起來是後端掛了,先檢查後端的 log

面試官:後端沒有 log,但是這個 controller 只有跟 db 撈資料,其他都沒有做

我:那應該是後端跟 db 溝通的時候卡住了,這張表很大嗎?

面試官:大概 10 萬筆

我:那應該還好,db 那台的 memory 跟 CPU 都是健康的嗎?

面試官:CPU 跟 memory 的 usage 都不超過 30%

我:db 是 RDS 嗎?

面試官:是

我:RDS 可以看每條 query 跑的狀況,這條 query 跑的狀況有異常嗎?

面試官:前幾次跑都沒問題,但是這次掛了

我:…

面試官:那現在是什麼問題?你需要一點提示嗎?

我:好,我要提示

面試官:你手動連接 db 發現連不進去了

我:那應該是 connection pool 沒設定好,需要檢查一下 connection pool 的設定

面試官:好,那今天你把 connection pool 調小一點,然後 bug 解掉了,但是過幾個月後流量暴增,現在服務又卡住了,你要怎麼優化?

我:SQL 是讀寫都有,還是只有讀而已?

面試官:只有讀

我:對 db 做 replica

面試官:OK,這樣問題解掉了,但是你的解法很浪費資源,因為效能瓶頸是卡在 db,不是後端流量被灌爆,但是你為了 replica,後端也要新開一台出來,但是大部分時間後端的利用率是很低的,你還有其他方法嗎?

我:有錢嗎?

面試官:老闆說,錢管夠

我:機器多開幾台

面試官:這是一個方法沒錯,還有其他的方法嗎?

我:優化 SQL 的寫法,然後加上 cache

面試官:你的 cache 打算怎麼做?

我:在後端做 cache

面試官:cache 的 key 是什麼

我:我應該會用頁面當作 key

面試官:OK 看起來應該差不多了


感想

這是一次蠻新鮮的面試經驗,以前從沒被這樣考過,但是我還挺喜歡這種考法的,很貼近現實,而且考得層面很廣,考完一次大概就可以知道你大概懂哪些東西,哪邊地方比較弱,雖然這次考得算是我比較弱的 db 部分,但是在面試官的引導下還算是過關了

以下是我把對話中的問題整理過,然後需要哪些方面的知識

  1. 網頁 load 不出來 -> 需要知道前端掛掉時怎麼找到前端的 log 跟 request 現在的狀態
  2. 後端沒有 response -> 這就代表了不是前端的問題,主要是卡在後端的 controller 那層
  3. 後端的 code 只有跟 db 撈資料 -> 需要知道 db 出狀況有哪些方式可以找到 db 的 log 或是該怎麼檢查 db 的狀態
  4. db 連不進去 -> 需要知道造成 db 連不進去有可能的原因有哪些
  5. 優化整個服務的效能 -> 需要先抓到整個服務的效能瓶頸在哪,然後針對這個環節有哪些優化的手段,還有手上有哪些資源可以使用(人/錢)

HTTP 的 method 中最常被用到的應該就是 GET 及 POST 了
但是 GET 與 POST 除了使用場景不一樣之外,到底還有哪些區別呢?

我們知道 GET 通常都透過 query string 來傳遞參數,POST 則是使用 request body
但是其實 GET 也可以透過 request body 來傳遞參數,同理 POST 其實也能使用 query string(只是上述兩種方式都不推薦)

所以具體的差別不在於透過什麼方式傳遞參數,而在於以下幾點

  1. POST 不會被 cache,GET 會
  2. POST 不會出現在瀏覽器的訪問記錄裡,GET 會
  3. POST 不能被存成書籤,GET 能

HTTPS 會加密 path, query string, request body
但是 domain name 不一定會加密(如果沒有使用 SNI 才會把 domain name 加密)

  1. 在 browser 輸入網址
  2. browser 跟 OS 要網址的 IP address
  3. OS 向 DNS 查詢 IP address(如果有 cache 會直接回傳 IP address 給 OS)
  4. DNS 向 Name Server 查詢 IP address
  5. OS 將從 DNS/Name Server 查詢到的 IP address 回傳給 browser

  1. browser 拿到 IP address 後對 IP address 做 HTTP/HTTPS request
    1. 如果使用 HTTPS 會先使用 TLS 對憑證做認證並產生加密的 session key,並對 request 的資料內容作加密
    2. browser 與 Server 做 TCP 的三項交握確認 Server 現在可以接收資料
  2. Server 上的 Nginx/Apache 接收到 request,並將 request pass 到 code base 中(Python/PHP/JavaScript)
  3. code base 將 request 處理完之後回傳 response
  4. browser 接收到 response 後根據 response 的內容將頁面 render 出來

前言

最近常常在用 Python 寫爬蟲
就好奇 BeautifulSoup 不同的 Parser 之間有什麼差別
於是寫了這篇文來記錄一下


1
2
3
4
5
6
7
8
9
import requests
from bs4 import BeautifulSoup

url = "https://google.com/"

resp = requests.get(url)
soup = BeautifulSoup(resp.text, "html.parser")
# or
soup = BeautifulSoup(resp.text, "lxml")

網路上的爬蟲教學常常會看到以上兩種寫法,可以看到差別就是 html.parser 跟 lxml
這個其實是在跟 BeautifulSoup 說我們要用哪種 Parser 去解析 HTML
但是到底 BeautifulSoup 支援多少種 Parser,及每種 Parser 到底差在哪?
於是就隨手 google 了一下發現了 StackOverflow 上的這篇及 BeautifulSoup 的 doc
以下是不同的 Parser 的比較表格

tl;dr
速度最快:lxml
相容性最高:html5lib
剩下用:html.parser

Parser優點缺點
html.parserPython 內建,不需額外安裝速度跟相容性都普通
lxml需要額外安裝(C dependency)
html5lib相容性最高,所有版本的 Python 都能用

參考資料

前言

最近在 build docker image 的時候遇到 No space left on device 的問題
因為在解的過程發現好像蠻多人都有遇過這個問題,於是就把解的過程記錄下來,希望能幫到其他人


今天在 build docker image 的時候出現這個錯誤

1
ERROR: Could not install packages due to an EnvironmentError: [Errno 28] No space left on device

因為是死在 pip install 這關,所以一開始先往 pip 的方向去找找到了以下這篇

[Errno 28] No space left on device #5816

裡面看起來是在說當不是用 root 安裝時,會遇到 tmp 資料夾空間不足的問題,裡面大概提供了兩種解法:

  1. 在家目錄底下開一個新的 tmp 資料夾,然後讓系統使用該資料夾來解決 tmp 空間不足的問題
  2. pip install –no-cache-dir

最後覺得第一個太麻煩了(因為我是在 docker 裡面),所以選第二個

但是問題沒解決,接著只把這段再加上 docker 拿去餵狗

1
No space left on device

接著看到好像真的是 docker 的問題,因為蠻多人都遇過的

看起來是因為 docker 會先配置一些空間用來暫存資料,然後這個空間被塞爆後就會報這個錯誤

於是我看了這篇

Docker error : no space left on device

然後先試了

1
2
3
4
5
6
7
> docker system prune
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache
Are you sure you want to continue? [y/N]

可以從輸入後的結果來看到這個指令將會刪除

  • all stopped containers
  • all networks not used by at least one container
  • all dangling images
  • all dangling build cache

如果上面這些東西還有要用的話這條路可能就行不通

總之我執行了之後空間好像還是不夠

接著照著這篇

https://success.docker.com/article/no-space-left-on-device-error

  1. 先找出哪些 container 的 log 檔最大

    1
    du -d1 -h /var/lib/docker/containers | sort -h

    大概會看到這種東西

    1
    2
    3
    4
    5
    6
    7
    8
    9
    36K	/var/lib/docker/containers/46d7350f4e7cd7c5c473ace07a58b74e20cb2edff7c2484c5a6e547f86ce17f3
    40K /var/lib/docker/containers/c88c6163611685271f853a130a411cb497faf232741a9266afe9534b43b7f9c9
    44K /var/lib/docker/containers/0819596cafb92e67b1f24890e6a19b91e1aad2ed77aba6a21e785d7f5d8319d1
    44K /var/lib/docker/containers/137ac90cc4d23fdbfc02231edb6224dee1f9e5c088ad9245b8b0909f9b7ae74c
    116K /var/lib/docker/containers/2384b475573521048658e4ef91f61ec3ef96d151ca9fab9acf751a74ae7bc8e3
    148K /var/lib/docker/containers/ea1cf3adba3d5f2451486f826cb3c24e9e443d719bbda2af7fe51de6a5597b74
    340K /var/lib/docker/containers/d920fa2ff4455d50d75e40fbce1fd8414b84b32f272718fc83abfd707bbcebd5
    668K /var/lib/docker/containers/8eec417b6620ff884f0a8777a0e22d70ef8560af888859bbb154db54e21b26ad
    166M /var/lib/docker/containers/ad0f644b4b97f93ca6fbfc367ee42780b2bd8bd056fb8457b922df7cf3e996f8
  2. 找出最佔空間的 log 檔,然後搭配

    1
    docker ps

    確定是哪個 container 的 log 檔且確定不需要了之後執行以下指令來清除 log 檔

    1
    cat /dev/null > /var/lib/docker/containers/<container_id>/<container_log_name>

    如果權限不足的話可能要用以下指令

    1
    sudo sh -c "cat /dev/null > /var/lib/docker/containers/<container_id>/<container_log_name>"

    刪完後再執行一次以下指令確認 log 檔已經被刪除

    1
    du -d1 -h /var/lib/docker/containers | sort -h

然後…還是失敗,於是決定換個思路

1
df

結果原來是 ubuntu 的空間快被吃光了..

然後照著這篇做

7 Simple Ways to Free Up Space on Ubuntu and Linux Mint

  1. 先清 apt

    1
    2
    sudo apt-get autoremove
    sudo apt-get autoclean
  2. 接著清 log

    1
    2
    3
    4
    5
    # 看 log 檔佔多少空間
    journalctl --disk-usage

    # 刪除超過三天的 log 檔
    sudo journalctl --vacuum-time=3d

再 build 一次就成功了QQ


參考資料