Java開發必備Linux 技能

1 編程相關

1.1 vim IDE 工具

1.2 Git 分佈式管理系統

1.2.1 Git 基礎

環境配置css

  • git config --global user.name your_name : 設置你的用戶名,提交會顯示
  • git config --global user.email your_email : 設置你的郵箱
  • git config core.quotepath false : 解決中文文件名顯示爲數字問題

基本操做html

  • git init : 初始化一個 git 倉庫
  • git add <filename> : 添加一個文件到 git 倉庫中
  • git commit -m "commit message": 提交到本地
  • git push [remote-name] [branch-name] : 把本地的提交記錄推送到遠端分支
  • git pull: 更新倉庫 git pull = git fetch + git merge
  • git checkout -- <file> : 還原未暫存 (staged) 的文件
  • git reset HEAD <file>... : 取消暫存,那麼還原一個暫存文件,應該是先 resetcheckout
  • git stash : 隱藏本地提交記錄,恢復的時候 git stash pop。這樣能夠在本地和遠程有衝突的狀況下,更新其餘文件

分支java

  • git branch <branch-name> : 基於當前 commit 新建一個分支,可是不切換到新分支
  • git branch -r : 查看遠程的全部分支(經常使用)
  • git checkout -b <branch-name> : 新建並切換分支
  • git checkout <branch-name> : 切換分支(經常使用)
  • git branch -d <branch-name> : 刪除分支
  • git push origin <branch-name> : 推送本地分支
  • git checkout -b <local-branch-name> origin/<origin-branch-name> : 基於某個遠程分支新建一個分支開發
  • git checkout --track origin/<origin-branch-name> : 跟蹤遠程分支(建立跟蹤遠程分支,Git 在 git push 的時候不須要指定 originbranch-name ,其實當咱們 clone 一個 repo 到本地的時候,master 分支就是 origin/master 的跟蹤分支,因此提交的時候直接 git push)。
  • git push origin :<origin-branch-name> : 刪除遠程分支

實踐 --- 主分支 Master/ 開發分支 Developnode

主分支只用來分佈重大版本,平常開發應該在另外一條分支上完成。咱們把開發用的分支,叫作 Develop。

# Git 建立 Develop 分支
git checkout -b develop master

# 將 Develop 分支發佈到 Master 分支

# 切換到 Master 分支
git checkout master

# 對 Develop 分支進行合併
git merge --no-ff develop
上一條命令的 --no-ff 參數是什麼意思。默認狀況下,Git 執行"快進式合併"(fast-farward merge),會直接將 Master 分支指向 Develop 分支。

# 刪除本地分支
git branch -d develop
複製代碼

標籤linux

  • git tag -a <tagname> -m <message> : 建立一個標籤(用 -a 指定標籤名,-m 指定說明文字) 如 git tag -a v1.0 -m "version 1.0 released mitaka"
  • git tag : 顯示已有的標籤
  • git show tagname: 顯示某個標籤的詳細信息
  • git push origin v1.0 push 到遠端倉庫 如git push -u ${PWD##*/} master v1.0
  • git checkout -b <tag-name> : 基於某個 tag 建立一個新的分支

Git shortcuts/aliasesnginx

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
複製代碼

1.2.2 知識點

基本命令讓你快速的上手使用 Git,知識點能讓你更好的理解 Git。git

文件的幾種狀態github

  • untracked: 未被跟蹤的,沒有歸入 Git 版本控制,使用 git add <filename> 歸入版本控制
  • unmodified: 未修改的,已經歸入版本控制,可是沒有修改過的文件
  • modified: 對歸入版本控制的文件作了修改,git 將標記爲 modified
  • staged: 暫存的文件,簡單理解:暫存文件就是 add 以後,commit 以前的文件狀態

理解這幾種文件狀態對於理解 Git 是很是關鍵的(至少能夠看懂一些錯誤提示了)。web

快照和差別正則表達式

詳細可看:Pro Git: Git 基礎 中有講到 直接記錄快照,而非差別比較,這裏只講我我的的理解。

Git 關心的是文件數據總體的變化,其餘版本管理系統(以 svn 爲例)關心的某個具體文件的差別。這個差別是好理解的,也就是兩個版本具體文件的不一樣點,好比某一行的某個字符發生了改變。

Git 不保存文件提交先後的差別,不變的文件不會發生任何改變,對於變化的文件,先後兩次提交則保存兩個文件。舉個例子:

SVN:

  1. 新建 3 個文件 a, b, c,作第一次提交 -> version1 : file_a file_b file_c
  2. 修改文件 b, 作第二次提交(真正提交的是 修改後的文件 b 和修改前的 file_b 的 diff) -> version2: diff_b_2_1
  3. 當我要 checkout version2 的時候,實際上獲得的是 file_a file_b+diff_b_2_1 file_c

Git:

  1. 新建 3 個文件 a, b, c,作第一次提交 -> version1 : file_a file_b file_c
  2. 修改文件 b (獲得file_b1), 作第二次提交 -> version2: file_a file_b1 file_c
  3. 當我要用 version2 的時候,實際上獲得的是 file_a file_b1 file_c

上面的 file_a file_b1 file_c 就是 version2 的 快照

Git 數據結構

Git 的核心數是很簡單的,就是一個鏈表(或者一棵樹更準確一些?無所謂了),一旦你理解了它的基本數據結構,再去看 Git,相信你有不一樣的感覺。繼續用上面的例子(全部的物理文件都對應一個 SHA-1 的值)

當咱們作第一次提交時,數據結構是這樣的:

sha1_2_file_map:
    28415f07ca9281d0ed86cdc766629fb4ea35ea38 => file_a
    ed5cfa40b80da97b56698466d03ab126c5eec5a9 => file_b
    1b5ca12a6cf11a9b89dbeee2e5431a1a98ea5e39 => file_c

commit_26b985d269d3a617af4064489199c3e0d4791bb5:
    base_info:
        Auther: "JerryZhang(chinajiezhang@gmail.com)"
        Date: "Tue Jul 15 19:19:22 2014 +0800"
        commit_content: "第一次提交"
    file_list:
        [1]: 28415f07ca9281d0ed86cdc766629fb4ea35ea38
        [2]: ed5cfa40b80da97b56698466d03ab126c5eec5a9
        [3]: 1b5ca12a6cf11a9b89dbeee2e5431a1a98ea5e39
        pre_commit: null
    next_commit: null
複製代碼

當修改了 file_b, 再提交一次時,數據結構應該是這樣的:

sha1_2_file_map:
    28415f07ca9281d0ed86cdc766629fb4ea35ea38 => file_a
    ed5cfa40b80da97b56698466d03ab126c5eec5a9 => file_b
    1b5ca12a6cf11a9b89dbeee2e5431a1a98ea5e39 => file_c
    39015ba6f80eb9e7fdad3602ef2b1af0521eba89 => file_b1

commit_26b985d269d3a617af4064489199c3e0d4791bb5:
    base_info:
        Auther: "JerryZhang(chinajiezhang@gmail.com)"
        Date: "Tue Jul 15 19:19:22 2014 +0800"
        commit_content: "第一次提交"
    file_list:
        [1]: 28415f07ca9281d0ed86cdc766629fb4ea35ea38
        [2]: ed5cfa40b80da97b56698466d03ab126c5eec5a9
        [3]: 1b5ca12a6cf11a9b89dbeee2e5431a1a98ea5e39
    pre_commit: commit_a08a57561b5c30b9c0bf33829349e14fad1f5cff
    next_commit: null

commit_a08a57561b5c30b9c0bf33829349e14fad1f5cff:
    base_info:
        Auther: "JerryZhang(chinajiezhang@gmail.com)"
        Date: "Tue Jul 15 22:19:22 2014 +0800"
        commit_content: "更新文件 b"
    file_list:
        [1]: 28415f07ca9281d0ed86cdc766629fb4ea35ea38
        [2]: 39015ba6f80eb9e7fdad3602ef2b1af0521eba89
        [3]: 1b5ca12a6cf11a9b89dbeee2e5431a1a98ea5e39
    pre_commit: null
    next_commit: commit_26b985d269d3a617af4064489199c3e0d4791bb5
複製代碼

當提交完第二次的時候,執行 git log,實際上就是從 commit_a08a57561b5c30b9c0bf33829349e14fad1f5cff 開始遍歷而後打印 base_info 而已。

實際的 git 實際確定要比上面的結構 ((的信息)的)要複雜的多,可是它的核心思想應該是就是,每一次提交就是一個新的結點。經過這個結點,我能夠找到全部的快照文件。再思考一下,什麼是分支?什麼是 Tags,其實他們可能只是某次提交的引用而已(一個 tag_head_node 指向了某一次提交的 node)。再思考怎麼回退一個版本呢?指針偏移!依次類推,上面的基本命令均可以獲得一個合理的解釋。

理解 git fetch 和 git pull 的差別

上面咱們說過 git pull 等價於 git fetchgit merge 兩條命令。當咱們 clone 一個 repo 到本地時,就有了本地分支和遠端分支的概念(假定咱們只有一個主分支),本地分支是 master,遠端分支是 origin/master。經過上面咱們對 Git 數據結構的理解,masterorigin/master 能夠想成是指向最新 commit 結點的兩個指針。剛 clone 下來的 repo,masterorigin/master 指針指向同一個結點,咱們在本地提交一次,origin 結點就更新一次,此時 masterorgin/master 就再也不相同了。頗有可能別人已經 commit 改 repo 不少次了,而且進行了提交。那麼咱們的本地的 origin/master 就再也不是遠程服務器上的最新的位置了。 git fetch 乾的就是從服務器上同步服務器上最新的 origin/master 和一些服務器上新的記錄 / 文件到本地。而 git merge 就是合併操做了(解決文件衝突)。git push 是把本地的 origin/mastermaster 指向相同的位置,而且推送到遠程的服務器。

1.2.3 其餘操做

解決 GitHub commit 次數過多.git 文件過大

徹底重建版本庫

# rm -rf .git
# git init
# git add .
# git commit -a -m "[Update] 合併以前全部 commit"
# git remote add origin <your_github_repo_url>
# git push -f -u origin master
複製代碼

HTTP request failed

使用 git clone 失敗

[root@localhost ~]# git clone https://github.com/meetbill/Vim.git
Initialized empty Git repository in /root/Vim/.git/
error:  while accessing https://github.com/meetbill/Vim.git/info/refs

fatal: HTTP request failed
複製代碼

解決方法

#git config --global http.sslVerify false

複製代碼

設置 Wiki 只能本身編寫,其餘人員只讀

在項目中的設置中勾選Restrict editing to collaborators only

修改最後一次 commit 操做

有時候咱們提交完了才發現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛纔的提交操做,可使用 --amend 選項從新提交:

$ git commit -a -m 'initial commit'
$ git add forgotten_file
$ git commit --amend -m 'new commit'
複製代碼

1.2.4 Git tips

(1) 命令簡化

cd git_repo(替換爲項目名字)
git remote add ${PWD##*/} git@github.com:meetbill/${PWD##*/}.git
git push -u ${PWD##*/} master
複製代碼

(2) 提高 git 使用體驗

2 運維相關

2.1 sed

2.2 awk

AWK 是貝爾實驗室 1977 年搞出來的文本處理工具。

之因此叫 AWK 是由於其取了三位創始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符

2.2.1 基礎知識

分隔符

默認狀況下, awk 使用空格看成分隔符。分割後的字符串可使用 $1, $2 等訪問。

上面提到過,咱們可使用 -F 來指定分隔符。 fs 若是是一個字符,能夠直接跟在 -F 後面,好比使用冒號看成分隔符就是 -F: . 若是分隔符比較複雜,就須要使用正則表達式來表示這個分隔符了。 正則表達式須要使用引號引發來。 好比使用‘ab’ 看成分隔符,就是 -F 'ab' 了。 使用 a 或 b 做爲分隔符,就是 -F '[ab]' 了。 關於正則表達式這裏很少說了。

內建變量

$0	當前記錄(這個變量中存放着整個行的內容)
$1~$n	當前記錄的第 n 個字段,字段間由 FS 分隔
FS	輸入字段分隔符 默認是空格或 Tab
NF	當前記錄中的字段個數,就是有多少列
NR	已經讀出的記錄數,就是行號,從 1 開始,若是有多個文件話,這個值也是不斷累加中。
FNR	當前記錄數,與 NR 不一樣的是,這個值會是各個文件本身的行號
RS	輸入的記錄分隔符, 默認爲換行符
OFS	輸出字段分隔符, 默認也是空格
ORS	輸出的記錄分隔符,默認爲換行符
FILENAME	當前輸入文件的名字
複製代碼

轉義

通常字符在雙引號以內就能夠直接原樣輸出了。 可是有部分轉義字符,須要使用反斜槓轉義才能正常輸出。

\\   A literal backslash.
\a   The 「alert」 character; usually the ASCII BEL character.
\b   backspace.
\f   form-feed.
\n   newline.
\r   carriage return.
\t   horizontal tab.
\v   vertical tab.
\xhex digits
\c   The literal character c.
複製代碼

模式

~ 表示模式開始,與 == 相比不是精確比較
/ / 中是模式
! 模式取反
複製代碼

單引號

當須要輸出單引號時,直接轉義發現會報錯。 因爲 awk 腳本並非直接執行,而是會先進行預處理,因此須要兩次轉義。 awk 支持遞歸引號。單引號內能夠輸出轉義的單引號,雙引號內能夠輸出轉義的雙引號。

好比須要輸出單引號,則須要下面這樣:

> awk 'BEGIN{print "\""}'
" > awk 'BEGIN{print "'\''"}'
' 複製代碼

固然,更簡單的方式是使用十六進制來輸出。

awk 'BEGIN{print "\x27"}'
複製代碼

2.2.2 腳本

BEGIN{ 這裏面放的是執行前的語句 }
END {這裏面放的是處理完全部的行後要執行的語句 }
{這裏面放的是處理每一行時要執行的語句}
複製代碼

2.2.3 運算與編程

awk 是弱類型語言,變量能夠是串,也能夠是數字,這依賴於實際狀況。全部的數字都是浮點型。

例如

//9
echo 5 4 | awk '{ print $1 + $2 }'

//54
echo 5 4 | awk '{ print $1 $2 }'

//"5 4"
echo 5 4 | awk '{ print $1, $2 }'

0-1-2-3-4-5-6
echo 6 | awk '{ for (i=0; i<=$0; i++){ printf (i==0?i:"-"i); }printf "\n";}'
複製代碼

Example

假設咱們有一個日期 2014/03/27, 咱們想處理爲 2014-03-27. 咱們可使用下面的代碼實現。

echo "2014/03/27" | awk -F/  '{print $1"-"$2"-"$3}'
複製代碼

假設 處理的日期都在 date 文件裏。 咱們能夠導入文件來操做

文件名 date

2014/03/27
2014/03/28
2014/03/29
複製代碼

命令

awk -F/ '{printf "%s-%s-%s\n",$1,$2,$3}'  date
複製代碼

輸出

2014-03-27
2014-03-28
2014-03-29
複製代碼

統計

awk '{sum+=$5} END {print sum}'
複製代碼

2.2.4 AWK 中輸出外部變量

經過雙引號內加個單引號將外部變量進行輸出

wang="hello"
echo meetbill | awk '{print "'$wang' " $1}'
複製代碼

2.2.5 AWK if

必須用在{}中,且比較內容用 () 擴起來

awk -F: '{if($1~/mail/) print $1}'    /etc/passwd                           // 簡寫
awk -F: '{if($1~/mail/) {print $1}}'  /etc/passwd                           // 全寫
awk -F: '{if($1~/mail/) {print $1} else {print $2}}' /etc/passwd            //if...else...
複製代碼
  • 條件表達式
    • == != > >=
  • 邏輯運算符
    • && || 如:查看使用了 CPU 0 核的進程
# ps -eF,其中 PSR 就是 (processor that process is currently assigned to.) 或者 ps -eo pid,command,args,psr
ps -eF |awk '{if($7==0) print $0}'
複製代碼

2.3 find

2.3.1 linux 文件查找指定時間段的文件

touch -t 201710241800 t1
touch -t 201710252100 t2


查找排序(先舊後新),結果寫到文件

find ./ -type f -name "*.aof" -newer ./t1 ! -newer ./t2  |xargs ls -lrt  > /sdcard/amr/sort.txt
複製代碼

2.4 grep

2.4.1 grep 時出現錯誤 Binary file (standard input) matches

在使用 grep 命令時出現錯誤 Binary file (standard input) matches

解決方法 加上 -a

例如本來爲 grep hello

改成 grep -a hello

3 系統相關

3.1 screen

如今不少時候咱們的開發環境都已經部署到雲端了,直接經過 SSH 來登陸到雲端服務器進行開發測試以及運行各類命令,一旦網絡中斷,經過 SSH 運行的命令也會退出,這個發讓人發瘋的。

好在有 screen 命令,它能夠解決這些問題。我使用 screen 命令已經有三年多的時間了,感受還不錯。

3.1.1 screen 使用

新建一個 Screen Session

$ screen -S screen_session_name
複製代碼

將當前 Screen Session 放到後臺

$ CTRL + A + D
複製代碼

喚起一個 Screen Session

$ screen -r screen_session_name
複製代碼

分享一個 Screen Session

$ screen -x screen_session_name
複製代碼

一般你想和別人分享你在終端裏的操做時能夠用此命令。

終止一個 Screen Session

$ exit
$ CTRL + D
複製代碼

查看一個 screen 裏的輸出

當你進入一個 screen 時你只能看到一屏內容,若是想看以前的內容能夠以下:

$ Ctrl + a ESC
複製代碼

以上意思是進入 Copy mode,拷貝模式,而後你就能夠像操做 VIM 同樣查看 screen session 裏的內容了。

能夠 Page Up 也能夠 Page Down。

3.1.2 開啓 screen 狀態欄

#curl -o screen.sh https://raw.githubusercontent.com/meetbill/op_practice_code/master/Linux/tools/screen.sh
#sh screen.sh
複製代碼

4.2 dmesg

內核緩衝信息,在系統啓動時,顯示屏幕上的與硬件有關的信息

dmesg | grep -E 'error|fail'

dmesg - print or control the kernel ring buffer
dmesg is used to examine or control the kernel ring buffer.

The default action is to read all messages from kernel ring buffer.
複製代碼

eth1: Too much work at interrupt, IntrStatus=0x0001

通常類的提示

某網卡的中斷請求過多。若是隻是偶爾出現一次可忽略
但這條提示若是常常出現或是集中出現,那涉及到的可能性就比較多有可能須要進行處理了。可能性比較多,如網卡性能;服務器性能;網絡攻擊.. 等等。
複製代碼

IPVS: incoming ICMP: failed checksum from 61.172.0.X!

通常類的提示

服務器收到了一個校驗和錯誤的 ICMP 數據包。這類的數據包有多是非法產生的垃圾數據. 但從目前來看服務器收到這樣的數據很是多. 通常都忽略。
通常代理服務器在工做時會每秒鐘轉發幾千個數據包. 收到幾個錯誤數據包不會影響正常的工做。
複製代碼

NET: N messages suppressed.

通常類的提示

服務器忽略了 N 個數據包. 和上一條提示相似. 服務器收到的數據包被認爲是無用的垃圾數據數據。這類數據可能是由攻擊類的程序產生的。
這條提示若是 N 比較小的時候能夠忽略. 但若是常常或是長時間出現 3 位數據以上的這類提示. 就頗有多是服務器受到了垃圾數據類的帶寬攻擊了。
與這條信息相似的還有。
__ratelimit: N messages suppressed
__ratelimit: N callbacks suppressed
複製代碼

UDP: bad checksum. From 221.200.X.X:50279 to 218.62.X.X:1155 ulen 24 UDP: short packet: 218.2.X.X:3072 3640/217 to 222.168.X.X:57596 218.26.131.X sent an invalid ICMP type 3, code 13 error to a broadcast: 0.1.0.4 on eth0

通常類的提示

服務器收到了一個錯誤的數據包. 分別爲 UDP 校驗和錯誤;太短的 UDP 數據包;一個錯誤的 ICMP 類型數據。這類信息通常狀況下也是非法產生的。
但通常問題不大可直接忽略。
複製代碼

kernel: conntrack_ftp: partial 227 2205426703+13 FTP_NAT: partial packet 2635716056/20 in 2635716048/2635716075

通常類的提示

服務器在維持一條 FTP 協議的鏈接時出錯。這樣的提示通常均可以直接忽略。
複製代碼

NETDEV WATCHDOG: eth1: transmit timed out

eth1: link down
eth1: link up, 10Mbps, half-duplex, lpa 0x0000
eth2: link up, 100Mbps, full-duplex, lpa 0x41E1
setting full-duplex based on MII #24 link partner capability of 45e1

網絡通訊嚴重問題!

這些提示是網絡通訊中出現嚴重問題時纔會出現。故障基本和網絡斷線有關係。這幾條提示分別表明的含意是 某塊網卡傳送數據超時;網卡鏈接 down; 網卡鏈接 up, 鏈接速率爲 10/100Mbps, 全 / 半雙功。
這裏寫到的最後三行的提示比較相似。出現這類提示時必須注意網絡鏈接情況進行處理!!!
複製代碼

NIC Link is Up 100 Mbps Full Duplex

網絡通訊嚴重問題!

狀況和 kernel: eth1: link up,... 相同. 指某塊網卡適應的鏈接速率。通常認爲沒有說明哪一個網卡 down, 只是連續出現網卡適應速率也是通訊有問題。
若是是網線正常的斷接能夠忽略這類的信息。
複製代碼

eth0: Transmit timed out, status 0000, PHY status 786d, resetting... eth0: Reset not complete yet. Trying harder.

網絡通訊嚴重問題!

第一條提示 網卡關送數據失敗。復位網卡。第二條提示 網卡復位不成功.... 這些提示都屬於嚴重的通訊問題。

報警程序的提示
0001 ##WMPCheckV001## 2005-04-13_10:10:01 Found .(ARP Spoofing sniffer)! IP:183 MAC:5
0002 ##WMPCheckV001## 2005-04-07_01:53:32 Found .(MAC_incomplete)! IP:173 mac_incomplete:186
0003 ##WMPCheckV001## 2005-04-17_16:25:11 Found .(HIGH_synsent)! totl:4271 SynSent:3490
0004 ##WMPCheckV001## 20......
這是由報警程序所引發的提示。詳細的信息須要用報警程序的客戶端進行實時接收. 詳細狀況請查看"告警模塊和日誌".
複製代碼

eth1: Promiscuous mode enabled. device eth1 entered promiscuous mode device eth1 left promiscuous mode

通常類的提示

這幾行提示指。某塊網卡進入(離開)了混雜模式。通常來講混雜模式是當須要對通訊進行抓包時纔用到的。當使用維護或故障分析時會使用到(好比 consoletools 中的 countflow 命令). 正常產生的這類提示能夠忽略。
若是在前臺和遠端都沒有進行維護時出現這個提示卻是應該引發注意,但這種可能性不大。
複製代碼

keyboard: unknown scancode e0 5e

基本無關

鍵盤上接收到未定義的鍵值。若是常常出現. 有多是鍵盤有問題。linux 對於比較特殊的鍵或是組合鍵,有時也會出這樣的提示。
要看一下服務器的鍵盤是否是被壓住了。其它狀況通常忽略。
複製代碼

uses obsolete (PF_INET,SOCK_PACKET)

基本無關

系統內核調用了一部分功能模塊,在第一次調入時會出現。通常狀況與使用調試工具備關。可直接忽略。
複製代碼

Neighbour table overflow.

網絡通訊故障

出現這個提示. 通常都是由於局域網內有部分計算機被病毒感染。狀況嚴重時會影響通訊。必須處理內部網通訊不正常的計算機。
複製代碼

eth1: Transmit error, Tx status register 82.

Probably a duplex mismatch. See Documentation/networking/vortex.txt
Flags; bus-master 1, dirty 9994190(14) current 9994190(14)
Transmit list 00000000 vs. f7171580.
0: @f7171200 length 800001e6 status 000101e6
1: @f7171240 length 8000008c status 0001008c

這個提示是 3com 網卡特有的。感受若是出現量不大的話也不會影響很嚴重。目前看維一的解決辦法是更換服務器上的網卡。實在感受 3com 的網卡有些問題...
複製代碼

服務器 CPU 工做溫度太高

CPU0: Temperature above threshold
CPU0: Running in modulated clock mode

服務器系統嚴重故障

服務器 CPU 工做溫度太高。必須排除硬件故障。
複製代碼

磁盤故障

I/O error, dev hda, sector N
I/O error, dev sda, sector N
hda: dma_intr: status=0x51 { DriveReady SeekComplete Error }
hda: dma_intr: error=0x40 { UncorrectableError }, LBAsect=811562, sector=811560

服務器系統嚴重故障

服務器系統磁盤存儲卡操做失敗。這樣的問題通常不會使服務器直接中止工做,但會引發不少嚴重問題
複製代碼

4 網絡相關

4.1 curl

4.1.1 HTTP 請求

GET 和 POST 是 HTTP 請求的兩種基本方法,最直觀的區別就是 GET 把參數包含在 URL 中,POST 經過 request body 傳遞參數。

在萬維網世界,TCP 就像汽車,咱們用 TCP 來運輸數據,它很可靠,
歷來不會發生丟件少件的現象。可是若是路上跑的全是看起來如出一轍
的汽車,那這個世界看起來是一團混亂,送急件的汽車可能被前面滿載
貨物的汽車攔堵在路上,整個交通系統必定會癱瘓。爲了不這種狀況
發生,交通規則 HTTP 誕生了。HTTP 給汽車運輸設定了好幾個服務類別,
有 GET, POST, PUT, DELETE 等等,HTTP 規定,當執行 GET 請求的時候,
要給汽車貼上 GET 的標籤(設置 method 爲 GET),並且要求把傳送的數
據放在車頂上 (url) 以方便記錄。若是是 POST 請求,就要在車上貼上
POST 的標籤,並把貨物放在車箱裏。

    固然,你也能夠在 GET 的時候往車箱內偷偷藏點貨物,可是這是很不
光彩;也能夠在 POST 的時候在車頂上也放一些數據。
複製代碼

4.1.2 curl 基礎

在介紹前,我須要先作兩點說明:

  1. 下面的例子中會使用 httpbin.org ,httpbin 提供客戶端測試 http 請求的服務,很是好用,具體能夠查看他的網站。
  2. 大部分沒有使用縮寫形式的參數,例如我使用 --request 而不是 -X ,這是爲了好記憶。

下面開始簡單介紹幾個命令:

get

  • curl protocol://address:port/url?args

直接以個 GET 方式請求一個 url,輸出返回內容:

curl httpbin.org
複製代碼

返回

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='content-type' value='text/html;charset=utf8'>
  <meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
  <title>httpbin(1): HTTP Client Testing Service</title>
  <style type='text/css' media='all'> /* style: man */ body#manpage {margin:0} .mp {max-width:100ex;padding:0 9ex 1ex 4ex} .mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0} .mp h2 {margin:10px 0 0 0} ...... 複製代碼

post

  • curl --data "args" "protocol://address:port/url"
    • -d/--data HTTP POST 方式傳送數據   * --data-ascii 以 ascii 的方式 post 數據
    • --data-binary 以二進制的方式 post 數據

使用 --request 指定請求類型, --data 指定數據,例如:

curl httpbin.org/post --request POST --data "name=keenwon&website=http://keenwon.com"
複製代碼

返回:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "tomshine",
    "website": "http://tomshine.xyz"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "41",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  },
  "json": null,
  "origin": "121.35.209.62",
  "url": "http://httpbin.org/post"
}
複製代碼

這個返回值是 httpbin 輸出的,能夠清晰的看出咱們發送了什麼數據,很是實用。

form 表單提交

form 表單提交使用 --form,使用 @ 指定本地文件,例如咱們提交一個表單,有字段 name 和文件 f:

curl httpbin.org/post --form "name=tomshine" --form "f=@/Users/tomshine/test.txt"
複製代碼

返回:

{
  "args": {},
  "data": "",
  "files": {
    "f": "Hello Curl!\n"
  },
  "form": {
    "name": "tomshine"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "296",
    "Content-Type": "multipart/form-data; boundary=------------------------3bd3dc24dca6daf2",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  },
  "json": null,
  "origin": "112.95.153.98",
  "url": "http://httpbin.org/post"
}
複製代碼

4.1.3 curl 深刻

顯示頭信息

使用 --include 在輸出中包含頭信息,使用 --head 只返回頭信息,例如:

curl httpbin.org/post --include --request POST --data "name=tomshine"
複製代碼

返回:

HTTP/1.1 200 OK
Server: nginx
Date: Sun, 18 Sep 2016 01:23:28 GMT
Content-Type: application/json
Content-Length: 363
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "tomshine"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "13",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  },
  "json": null,
  "origin": "121.35.209.62",
  "url": "http://httpbin.org/post"
}
複製代碼

再例如,只顯示頭信息的話:

curl httpbin.org --head
複製代碼

返回:

HTTP/1.1 200 OK
Server: nginx
Date: Sun, 18 Sep 2016 01:24:29 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 12150
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
複製代碼

詳細顯示通訊過程

使用 --verbose 顯示通訊過程,例如:

curl httpbin.org/post --verbose --request POST --data "name=tomshine"
複製代碼

返回:

*   Trying 54.175.219.8...
* Connected to httpbin.org (54.175.219.8) port 80 (#0)
> POST /post HTTP/1.1
> Host: httpbin.org
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Length: 13
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 13 out of 13 bytes
< HTTP/1.1 200 OK
< Server: nginx
< Date: Sun, 18 Sep 2016 01:25:03 GMT
< Content-Type: application/json
< Content-Length: 363
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
<
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "tomshine"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "13",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  },
  "json": null,
  "origin": "121.35.209.62",
  "url": "http://httpbin.org/post"
}
* Connection #0 to host httpbin.org left intact
複製代碼

設置頭信息

使用 --header 設置頭信息,httpbin.org/headers 會顯示請求的頭信息,咱們測試下:

curl httpbin.org/headers --header "a:1"
複製代碼

返回:

{
  "headers": {
    "A": "1",
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  }
}
複製代碼

一樣的,可使用 --header 設置 User-Agent 等。

Referer 字段

設置 Referer 字段很簡單,使用 --referer ,例如:

curl httpbin.org/headers --referer http://tomshine.xyz
複製代碼

返回:

{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "Referer": "http://tomshine.xyz",
    "User-Agent": "curl/7.43.0"
  }
}
複製代碼

包含 cookie

使用 --cookie 來設置請求的 cookie,例如:

curl httpbin.org/headers --cookie "name=tomshine;website=http://tomshine.xyz"
複製代碼

返回:

{
  "headers": {
    "Accept": "*/*",
    "Cookie": "name=tomshine;website=http://tomshine.xyz",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  }
}
複製代碼

自動跳轉

使用 --location 參數會跟隨連接的跳轉,例如:

curl httpbin.org/redirect/1 --location
複製代碼

httpbin.org/redirect/1 會 302 跳轉,因此返回:

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.43.0"
  },
  "origin": "121.35.209.62",
  "url": "http://httpbin.org/get"
}
複製代碼

http 認證

當頁面須要認證時,可使用 --user ,例如:

curl httpbin.org/basic-auth/tomshine/123456 --user tomshine:123456
複製代碼

返回:

{
  "authenticated": true,
  "user": "tomshine"
}
複製代碼

4.2 tcpdump

4.2.1 tcp 三次握手和四次揮手

三次握手

A 主動打開鏈接,B 被動打開鏈接

(1) 第一次握手:創建鏈接時,客戶端 A 發送 SYN 包 (SYN=x) 到服務器 B,並進入 SYN_SEND 狀態,等待服務器 B 確認。
(2) 第二次握手:服務器 B 收到 SYN 包,必須確認客戶 A 的 SYN(ACK=x+1),同時本身也發送一個 SYN 包 (SYN=y),即 SYN+ACK 包,此時服務器 B 進入 SYN_RECV 狀態。
(3) 第三次握手:客戶端 A 收到服務器 B 的 SYN+ACK 包,向服務器 B 發送確認包 ACK(ACK=y+1),此包發送完畢,客戶端 A 和服務器 B 進入 ESTABLISHED 狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據。
複製代碼

爲何 A 還要發送一次確認呢?

主要是爲了防止已失效的鏈接請求報文段忽然有傳送到 B,於是產生錯誤。

正常狀況

A 發出鏈接請求,可是由於鏈接請求報文丟失爲未收到確認。因而 A 在重傳一次鏈接請求,後來收到了確認,創建了鏈接。數據傳輸完畢後,就釋放了鏈接。A 共發送兩個鏈接請求報文段,其中第一個丟失第二個到達了 B。

異常狀況 A 發出的第一個鏈接請求報文段並無丟失,而是在某些網絡節點長時間滯留,致使延誤到鏈接釋放以後纔到達了 B,原本這是一個早已經失效的報文段,可是 B 收到此失效的鏈接請求報文段以後,誤覺得是 A 又發出一次新的鏈接請求,因而就向 A 發送確認報段,贊成創建鏈接。假如不採用三次握手,那麼只要 B 發出確認,新的鏈接就創建了。因爲如今 A 並無發出創建鏈接的請求,所以不會理睬 B 的確認,也不會向 B 發送數據,但 B 卻覺得新的運輸鏈接已經創建了,而且一直等待 A 發來數據。B 的資源就這樣白白的浪費了, 採用三次握手的辦法能夠防止上述現象的發生。例如剛纔的狀況,A 不會向 B 的確認發出確認。B 因爲接收不到確認,就知道 A 並無要求創建鏈接。

使用 tcpdump 來驗證 TCP 的三次握手

使用 ssh localhost 來鏈接主機,使用使用 tcpdump 來驗證 TCP 的三次握手

[root@localhost apue]# tcpdump -i lo tcp -S
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
1 15:08:03.039511 IP6 localhost.44910 > localhost.ssh: Flags [S], seq 3120401438, win 32752, options [mss 16376,sackOK,TS val 1319756 ecr 0,nop,wscale 7], length 0
2 15:08:03.039546 IP6 localhost.ssh > localhost.44910: Flags [S.], seq 404185237, ack 3120401439, win 32728, options [mss 16376,sackOK,TS val 1319756 ecr 1319756,nop,wscale 7], length 0
3 15:08:03.039576 IP6 localhost.44910 > localhost.ssh: Flags [.], ack 404185238, win 256, options [nop,nop,TS val 1319756 ecr 1319756], length 0
4 15:08:03.064809 IP6 localhost.ssh > localhost.44910: Flags [P.], seq 404185238:404185259, ack 3120401439, win 256, options [nop,nop,TS val 1319781 ecr 1319756], length 21
15:08:03.064944 IP6 localhost.44910 > localhost.ssh: Flags [.], ack 404185259, win 256, options [nop,nop,TS val 1319781 ecr 1319781], length 0
複製代碼

第一行就是第一次握手,客戶端向服務器發送 SYN 標誌 (Flags [S]),其中 seq = 3120401438; 第二行就是第二次握手,服務器向客戶端進行 SYN+ACK 響應 (Flags [S.]), 其中 seq 404185237, ack 3120401439(是客戶端的 seq+1 的值) 第三行就是第三次握手,客戶端向服務器進行 ACK 響應 (Flags [.]), 其中 ack 404185238(就是服務器的 seq+1 的值)。

四次揮手

通訊傳輸結束後,通訊的雙方均可以釋放鏈接,如今 A 和 B 都處於 ESTABLISHED 狀態。

因爲 TCP 鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個 FIN 來終止這個方向的鏈接。收到一個 FIN 只意味着這一方向上沒有數據流動,一個 TCP 鏈接在收到一個 FIN 後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。

(1) 客戶端 A 發送一個 FIN 包,用來關閉客戶 A 到服務器 B 的數據傳送。序列號 seq = u,它等於前面已傳送過的數據的最後一個字節的序號加 1. 這時 A 進入 FIN-WAIT-1(終止等待 1) 狀態,等待 B 的確認。

(2) 服務器 B 收到這個 FIN,它發回一個 ACK,確認序號是 ack = u +1。和 SYN 同樣,一個 FIN 將佔用一個序號。這個報文段本身的序號是 v,等於 B 前面已經傳送過的數據的最後一個字節的序號加 1。而後 B 進程進入 CLOSE-WAIT(關閉等待)狀態。TCP 服務器進程這時應通知高層應用進程,於是從 A 到 B 的這個方向的鏈接就釋放了,這時的 TCP 鏈接處於半關閉 (half-close) 狀態, 即 A 已經沒有數據要發送了,可是 B 若發送數據,A 仍要接收,也就是說從 B 到 A 這個方向的鏈接並無關閉,這個狀態可能會持續一些時間。A 收到來到 B 的確認後就進入 FIN-WAIT-2(終止等待 2) 狀態,等待 B 發出的鏈接釋放報文段。

(3) 若 B 已經沒有要向 A 發送的數據,其應用進程就會通知 TCP 釋放鏈接,這時 B 發出的鏈接釋放報文段必須使 FIN = 1。現假定 B 的序號爲 w(在半關閉狀態 B 可能要發送一些數據)。B 還必須重複上次發送過的確認號 ack = u +1。這時 B 就進入了 LAST-ACK(最後確認)狀態,等待 A 的確認。

(4)A 在收到 B 的鏈接釋放報文段後,必須對此發出確認。在確認報文段中把 ACK 置 1,確認號 ack = w + 1,而本身的序號是 seq = u + 1(根據 TCP 標準,前面發送過的 FIN 報文段要消耗一個序號),而後進入到 TIME-WAIT(時間等待)狀態。

此時的 TCP 尚未徹底的釋放掉。必須通過時間等待計時器 (TIME-WAIT timer) 設置的時間 2MSL 後,A 才進入到 CLOSED 狀態。

MSL 叫作最長報文段壽命,它是任何報文段被丟棄前在網絡內的最長時間。

2MSL 也就是這個時間的兩倍,RFC 建議設置爲 2 分鐘,可是 2 分鐘可能太長了,所以 TCP 容許不一樣的實現使用更小的 MSL 值。

所以從 A 進入到 TIME-WAIT 狀態後,要通過 4 分鐘才能進入 CLOSED 狀態,此案開始創建下一個新的鏈接。當 A 撤銷相應的傳輸控制塊 TCP 後,就結束了 TCP 鏈接。

使用 tcpdump 來驗證 TCP 的四次揮手

退出 ssh 鏈接的主機,使用使用 tcpdump 來驗證 TCP 的四次揮手

15:14:58.836149 IP6 localhost.44911 > localhost.ssh: Flags [P.], seq 1823848744:1823848808, ack 3857143125, win 305, options [nop,nop,TS val 1735551 ecr 1735551], length 64
15:14:58.836201 IP6 localhost.44911 > localhost.ssh: Flags [F.], seq 1823848808, ack 3857143125, win 305, options [nop,nop,TS val 1735551 ecr 1735551], length 0
15:14:58.837850 IP6 localhost.ssh > localhost.44911: Flags [.], ack 1823848809, win 318, options [nop,nop,TS val 1735554 ecr 1735551], length 0
15:14:58.842130 IP6 localhost.ssh > localhost.44911: Flags [F.], seq 3857143125, ack 1823848809, win 318, options [nop,nop,TS val 1735559 ecr 1735551], length 0
15:14:58.842150 IP6 localhost.44911 > localhost.ssh: Flags [.], ack 3857143126, win 305, options [nop,nop,TS val 1735559 ecr 1735559], length 0
複製代碼

seq start:end 意思是初始序列號:結束序列號,end = start + length, 可是接受包的結束序號應該是 end - 1。

好比 start = 1,length = 3,那麼真正的結束序號是 1+3-1 = 3, 由於開始序號也算在內的!

seq 1823848744:1823848808 意思是初始序列號:結束序列號,其實後面那個就是前面那個加上包長度 length = 64,實際上結束序列號是沒有使用的,真正使用的序號是開始序號到結束序號 -1,也就是 1823848807

第一次揮手中客戶端發送 FIN 即 [F.] seq u = 1823848808,也就是上次發送的數據的最後一個字節的序號加 1

第二次揮手中服務器發送 ACK 即 [.] ack 1823848809 = u + 1

第三次揮手中服務器發送 FIN 即 [F.] seq v = 3857143125, ack 1823848809 = u + 1

第四次揮手中客戶端發送 ACK 即 [.] ack 3857143126 = v + 1

  1. 默認狀況下(不改變 socket 選項),當你調用 close 函數時,若是發送緩衝中還有數據,TCP 會繼續把數據發送完。

  2. 發送了FIN 只是表示這端不能繼續發送數據(應用層不能再調用 send 發送),可是還能夠接收數據。

  3. 應用層如何知道對方關閉?

在最簡單的阻塞模型中,當調用 recv 時,若是返回 0,則表示對方關閉,

在這個時候一般的作法就是也調用 close,那麼 TCP 層就發送 FIN,繼續完成四次握手;

若是不調用 close,那麼對方就會處於 FIN_WAIT_2 狀態,而本端則會處於 CLOSE_WAIT 狀態;

  1. 在不少時候,TCP 鏈接的斷開都會由 TCP 層自動進行,例如你 CTRL+C 終止你的程序,TCP 鏈接依然會正常關閉。

4.2.2 tcpdump 使用

針對特定網口抓包 (-i 選項)

tcpdump -i eth0

指定抓包端口

tcpdump -i eth0 port 22

抓取特定目標 ip 和端口的包

tcpdump -i eth0 dst 10.70.121.92 and port 22

增長抓包時間戳 (-tttt 選項)

tcpdump -n -tttt -i eth0

4.3 nc

nc 檢測端口更方便,同時批量進行檢測端口的話是很是好的工具

4.3.1 語法

nc [-hlnruz][-g《網關...>][-G《指向器數目》][-i《延遲秒數》][-o《輸出文件》][-p《通訊端口》][-s《來源位址》][-v...][-w《超時秒數》][主機名稱]『通訊端口...] ``` 參數說明: -g《網關》 設置路由器躍程通訊網關,最丟哦可設置 8 個。 -G《指向器數目》 設置來源路由指向器,其數值爲 4 的倍數。 -h 在線幫助。 -i《延遲秒數》 設置時間間隔,以便傳送信息及掃描通訊端口。 -l 使用監聽模式,管控傳入的資料。 -n 直接使用 IP 地址,而不經過域名服務器。 -o《輸出文件》 指定文件名稱,把往來傳輸的數據以 16 進制字碼傾倒成該文件保存。 -p《通訊端口》 設置本地主機使用的通訊端口。 -r 亂數指定本地與遠端主機的通訊端口。 -s《來源位址》 設置本地主機送出數據包的 IP 地址。 -u 使用 UDP 傳輸協議。 -v 顯示指令執行過程。 -w《超時秒數》 設置等待連線的時間。 -z 使用 0 輸入 / 輸出模式,只在掃描通訊端口時使用。

```
複製代碼

4.3.2 TCP 端口掃描

```
# nc -v -z -w2 10.20.144.145 1-100
Connection to 10.20.144.145 22 port [tcp/ssh] succeeded!
nc: connect to 10.20.144.145 port 23 (tcp) failed: Connection refused
nc: connect to 10.20.144.145 port 24 (tcp) failed: Connection refused
nc: connect to 10.20.144.145 port 25 (tcp) failed: Connection refused
...
Connection to 10.20.144.145 80 port [tcp/http] succeeded!
...
掃描 10.20.144.145 的端口 範圍是 1-100
```
不加 -v 時僅輸出 succeeded 的結果
複製代碼

4.3.3 掃描 UDP 端口

```
# nc -u -z -w2 10.20.144.145 1-1000 // 掃描 10.20.144.145 的端口 範圍是 1-1000
掃描指定端口
```
複製代碼

5 日誌相關操做

5.1 截取某段時間內的日誌

sed -n '/2018-03-06 15:25:00/,/2018-03-06 15:30:00/p' access.log >25-30.log
複製代碼

5.2 處理日誌文件中上下關聯的兩行

awk 'pattern { action };pattern { action };'
複製代碼

凡是被 {} 包裹的,就是 action, 凡是沒有被{}包裹的,就是 pattern,

文件 d.txt 以下內容

ggg 1
portals: 192.168.5.41:3260
werew 2
portals: 192.168.5.43:3260
複製代碼

如何把文件 d.txt 內容變爲以下內容

ggg 192.168.5.41:3260
werew 192.168.5.43:3260
複製代碼

方法

awk '/port/{print a" "$2}{a=$1}' d.txt
複製代碼

處理第一行的時候,以 port 開頭嗎?很明顯,不以 port 開頭,因此那個 pattern 不匹配,action 不執行。但執行了後面的 a=$1

處理第二行的時候,以 port 開頭,打印出來 a 和本行 $2,再處理就是個循環過程。

總之,編寫模式匹配時候,匹配的模式爲第二行中的內容

6 應用服務相關

6.1 排查 java CPU 性能問題

show-busy-java-threads.sh

curl -o show-busy-Java-threads.sh https://raw.githubusercontent.com/meetbill/op_practice_code/master/Linux/op/show-busy-java-threads.sh
複製代碼

用於快速排查JavaCPU性能問題 (top us值太高),自動查出運行的Java進程中消耗CPU多的線程,並打印出其線程棧,從而肯定致使性能問題的方法調用。

PS,如何操做能夠參見 @bluedavy 的《分佈式 Java 應用》的【5.1.1 cpu 消耗分析】一節,說得很詳細:

  1. top命令找出有問題Java進程及線程id
    1. 開啓線程顯示模式
    2. CPU使用率排序
    3. 記下Java進程id及其CPU高的線程id
  2. 用進程id做爲參數,jstack有問題的Java進程
  3. 手動轉換線程id成十六進制(能夠用printf %x 1234
  4. 查找十六進制的線程id(能夠用grep
  5. 查看對應的線程棧

查問題時,會要屢次這樣操做以肯定問題,上面過程太繁瑣太慢了

6.1.1 用法

show-busy-java-threads.sh
# 從 全部的 Java 進程中找出最消耗 CPU 的線程(缺省 5 個),打印出其線程棧。

show-busy-java-threads.sh -c 《要顯示的線程棧數》

show-busy-java-threads.sh -c 《要顯示的線程棧數》 -p 《指定的 Java Process>

##############################
# 注意:
##############################
# 若是 Java 進程的用戶 與 執行腳本的當前用戶 不一樣,則 jstack 不了這個 Java 進程。
# 爲了能切換到 Java 進程的用戶,須要加 sudo 來執行,便可以解決:
sudo show-busy-java-threads.sh
複製代碼

6.1.2 示例

$ show-busy-java-threads.sh
[1] Busy(57.0%) thread(23355/0x5b3b) stack of java process(23269) under user(admin):
"pool-1-thread-1" prio=10 tid=0x000000005b5c5000 nid=0x5b3b runnable [0x000000004062c000]
   java.lang.Thread.State: RUNNABLE
    at java.text.DateFormat.format(DateFormat.java:316)
    at com.xxx.foo.services.common.DateFormatUtil.format(DateFormatUtil.java:41)
    at com.xxx.foo.shared.monitor.schedule.AppMonitorDataAvgScheduler.run(AppMonitorDataAvgScheduler.java:127)
    at com.xxx.foo.services.common.utils.AliTimer$2.run(AliTimer.java:128)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

[2] Busy(26.1%) thread(24018/0x5dd2) stack of java process(23269) under user(admin):
"pool-1-thread-2" prio=10 tid=0x000000005a968800 nid=0x5dd2 runnable [0x00000000420e9000]
   java.lang.Thread.State: RUNNABLE
    at java.util.Arrays.copyOf(Arrays.java:2882)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:572)
    at java.lang.StringBuffer.append(StringBuffer.java:320)
    - locked <0x00000007908d0030> (a java.lang.StringBuffer)
    at java.text.SimpleDateFormat.format(SimpleDateFormat.java:890)
    at java.text.SimpleDateFormat.format(SimpleDateFormat.java:869)
    at java.text.DateFormat.format(DateFormat.java:316)
    at com.xxx.foo.services.common.DateFormatUtil.format(DateFormatUtil.java:41)
    at com.xxx.foo.shared.monitor.schedule.AppMonitorDataAvgScheduler.run(AppMonitorDataAvgScheduler.java:126)
    at com.xxx.foo.services.common.utils.AliTimer$2.run(AliTimer.java:128)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
...
複製代碼

上面的線程棧能夠看出,CPU消耗最高的 2 個線程都在執行java.text.DateFormat.format,業務代碼對應的方法是shared.monitor.schedule.AppMonitorDataAvgScheduler.run。能夠基本肯定:

  • AppMonitorDataAvgScheduler.run調用DateFormat.format次數比較頻繁。
  • DateFormat.format比較慢。(這個能夠由DateFormat.format的實現肯定。)

多個執行幾回show-busy-java-threads.sh,若是上面狀況高几率出現,則能夠肯定上面的斷定。 # 由於調用越少代碼執行越快,則出如今線程棧的機率就越低。

分析shared.monitor.schedule.AppMonitorDataAvgScheduler.run實現邏輯和調用方式,以優化實現解決問題。

  • oldratlee
  • silentforce 改進此腳本,增長對環境變量JAVA_HOME的判斷。 #15
  • liuyangc3
    • 優化性能,經過read -a簡化反覆的awk操做 #51
    • 發現並解決jstack非當前用戶Java進程的問題 #50
相關文章
相關標籤/搜索