环境配置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>...
: 取消暂存,那么还原一个暂存文件,应该是先 reset
后 checkout
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
的时候不须要指定 origin
和 branch-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
复制代码
基本命令让你快速的上手使用 Git,知识点能让你更好的理解 Git。git
文件的几种状态github
git add <filename>
归入版本控制理解这几种文件状态对于理解 Git 是很是关键的(至少能够看懂一些错误提示了)。web
快照和差别正则表达式
详细可看:Pro Git: Git 基础 中有讲到 直接记录快照,而非差别比较,这里只讲我我的的理解。
Git 关心的是文件数据总体的变化,其余版本管理系统(以 svn 为例)关心的某个具体文件的差别。这个差别是好理解的,也就是两个版本具体文件的不一样点,好比某一行的某个字符发生了改变。
Git 不保存文件提交先后的差别,不变的文件不会发生任何改变,对于变化的文件,先后两次提交则保存两个文件。举个例子:
SVN:
version1 : file_a file_b file_c
file_b
的 diff) -> version2: diff_b_2_1
file_a file_b+diff_b_2_1 file_c
Git:
version1 : file_a file_b file_c
file_b1
), 作第二次提交 -> version2: file_a file_b1 file_c
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 fetch
和 git merge
两条命令。当咱们 clone
一个 repo 到本地时,就有了本地分支和远端分支的概念(假定咱们只有一个主分支),本地分支是 master
,远端分支是 origin/master
。经过上面咱们对 Git 数据结构的理解,master
和 origin/master
能够想成是指向最新 commit 结点的两个指针。刚 clone
下来的 repo,master
和 origin/master
指针指向同一个结点,咱们在本地提交一次,origin
结点就更新一次,此时 master
和 orgin/master
就再也不相同了。颇有可能别人已经 commit 改 repo 不少次了,而且进行了提交。那么咱们的本地的 origin/master
就再也不是远程服务器上的最新的位置了。 git fetch
干的就是从服务器上同步服务器上最新的 origin/master
和一些服务器上新的记录 / 文件到本地。而 git merge
就是合并操做了(解决文件冲突)。git push
是把本地的 origin/master
和 master
指向相同的位置,而且推送到远程的服务器。
彻底重建版本库
# 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
复制代码
使用 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
复制代码
在项目中的设置中勾选Restrict editing to collaborators only
有时候咱们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操做,可使用 --amend 选项从新提交:
$ git commit -a -m 'initial commit'
$ git add forgotten_file
$ git commit --amend -m 'new commit'
复制代码
(1) 命令简化
cd git_repo(替换为项目名字)
git remote add ${PWD##*/} git@github.com:meetbill/${PWD##*/}.git
git push -u ${PWD##*/} master
复制代码
(2) 提高 git 使用体验
AWK 是贝尔实验室 1977 年搞出来的文本处理工具。
之因此叫 AWK 是由于其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符
分隔符
默认状况下, 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"}'
复制代码
BEGIN{ 这里面放的是执行前的语句 }
END {这里面放的是处理完全部的行后要执行的语句 }
{这里面放的是处理每一行时要执行的语句}
复制代码
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}'
复制代码
经过双引号内加个单引号将外部变量进行输出
wang="hello"
echo meetbill | awk '{print "'$wang' " $1}'
复制代码
必须用在{}中,且比较内容用 () 扩起来
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}'
复制代码
touch -t 201710241800 t1
touch -t 201710252100 t2
查找排序(先旧后新),结果写到文件
find ./ -type f -name "*.aof" -newer ./t1 ! -newer ./t2 |xargs ls -lrt > /sdcard/amr/sort.txt
复制代码
在使用 grep 命令时出现错误 Binary file (standard input) matches
解决方法 加上 -a
例如本来为 grep hello
改成 grep -a hello
如今不少时候咱们的开发环境都已经部署到云端了,直接经过 SSH 来登陆到云端服务器进行开发测试以及运行各类命令,一旦网络中断,经过 SSH 运行的命令也会退出,这个发让人发疯的。
好在有 screen 命令,它能够解决这些问题。我使用 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。
#curl -o screen.sh https://raw.githubusercontent.com/meetbill/op_practice_code/master/Linux/tools/screen.sh
#sh screen.sh
复制代码
内核缓冲信息,在系统启动时,显示屏幕上的与硬件有关的信息
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
服务器系统严重故障
服务器系统磁盘存储卡操做失败。这样的问题通常不会使服务器直接中止工做,但会引发不少严重问题
复制代码
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 的时候在车顶上也放一些数据。
复制代码
在介绍前,我须要先作两点说明:
--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"
}
复制代码
显示头信息
使用 --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"
}
复制代码
三次握手
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
默认状况下(不改变 socket 选项),当你调用 close 函数时,若是发送缓冲中还有数据,TCP 会继续把数据发送完。
发送了FIN 只是表示这端不能继续发送数据(应用层不能再调用 send 发送)
,可是还能够接收数据。
应用层如何知道对方关闭?
在最简单的阻塞模型中,当调用 recv 时,若是返回 0,则表示对方关闭,
在这个时候一般的作法就是也调用 close,那么 TCP 层就发送 FIN,继续完成四次握手;
若是不调用 close,那么对方就会处于 FIN_WAIT_2 状态,而本端则会处于 CLOSE_WAIT 状态;
针对特定网口抓包 (-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
nc 检测端口更方便,同时批量进行检测端口的话是很是好的工具
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 输入 / 输出模式,只在扫描通讯端口时使用。
```
复制代码
```
# 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 的结果
复制代码
```
# nc -u -z -w2 10.20.144.145 1-1000 // 扫描 10.20.144.145 的端口 范围是 1-1000
扫描指定端口
```
复制代码
sed -n '/2018-03-06 15:25:00/,/2018-03-06 15:30:00/p' access.log >25-30.log
复制代码
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,再处理就是个循环过程。
总之,编写模式匹配时候,匹配的模式为第二行中的内容
curl -o show-busy-Java-threads.sh https://raw.githubusercontent.com/meetbill/op_practice_code/master/Linux/op/show-busy-java-threads.sh
复制代码
用于快速排查Java
的CPU
性能问题 (top us
值太高),自动查出运行的Java
进程中消耗CPU
多的线程,并打印出其线程栈,从而肯定致使性能问题的方法调用。
PS,如何操做能够参见 @bluedavy 的《分布式 Java 应用》的【5.1.1 cpu 消耗分析】一节,说得很详细:
top
命令找出有问题Java
进程及线程id
:
CPU
使用率排序Java
进程id
及其CPU
高的线程id
id
做为参数,jstack
有问题的Java
进程id
成十六进制(能够用printf %x 1234
)id
(能够用grep
)查问题时,会要屡次这样操做以肯定问题,上面过程太繁琐太慢了。
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
复制代码
$ 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
实现逻辑和调用方式,以优化实现解决问题。
JAVA_HOME
的判断。 #15read -a
简化反复的awk
操做 #51jstack
非当前用户Java
进程的问题 #50