每天学习一个命令:awk

awk 是一个强大的文本分析工具,它是 Linux 中功能强大的数据处理引擎之一。相对于 grep 的查找,sed 的编辑,awk 在其对数据分析并生成报告时,显得尤为强大。本文只是简单的入门介绍,这个工具是什么,能够做什么,能够做到什么复杂的事情,更多强大的功能只能在日常使用中获得。

当使用 awk 命令处理一个或者多个文件时,它会依次读取文件的每一行内容, 然后对其进行处理,awk 命令默认从 stdio 标准输入获取文件内容, awk 使用一对单引号来表示一些可执行的脚本代码,在可执行脚本代码里面,使用一对花括号来表示一段可执行代码块,可以同时存在多个代码块。awk 的每个花括号内同时可以有多个指令,每一个指令用分号分隔,awk 其实就是一个脚本编程语言。

awk 命令和 sed 命令结构相同,通常情况下,awk 将每个输入行解释为一条记录而每一行中的内容(由空格或者制表符分隔)解释为每一个字段,一个或者多个连续空格或者制表符看做定界符。awk 中 $0 代表整个记录。

awk ' /MA/ { print $1 }' list

解释:打印包含 MA 的行中的第一个单词。

基本格式

awk 命令的基本格式

awk [options] 'script' file

options 这个表示一些可选的参数选项,script 表示 awk 的可执行脚本代码,这个是必须要有的。 file 这个表示 awk 需要处理的文件,注意是纯文本文件

awk 自定义分隔符

awk 默认的分割符为空格和制表符,可以使用 -F 参数来指定分隔符

awk -F ':' '{print $1}' /etc/passwd

解释:使用 -F 来改变分隔符为 : 上面的命令将 /etc/passwd 文件中的每一行用冒号 : 分割成多个字段,然后用 print 将第 1 列字段的内容打印输出

在 awk 中同时指定多个分隔符, 比如现在有这样一个文件 some.log 文件内容如下

Grape(100g)1980
raisins(500g)1990
plum(240g)1997
apricot(180g)2005
nectarine(200g)2008

现在我们想将上面的 some.log 文件中按照 “水果名称(重量)年份” 来进行分割

$ awk -F '[()]' '{print $1, $2, $3}' some.log
Grape 100g 1980
raisins 500g 1990
plum 240g 1997
apricot 180g 2005
nectarine 200g 2008

-F 参数中使用一对方括号来指定多个分隔符,awk 处理 some.log 文件时就会使用 “(“ 或者 “)” 来对文件的每一行进行分割。

awk 内置变量的使用

  • $0 这个表示文本处理时的当前行,$1 表示文本行被分隔后的第 1 个字段列,$2 表示文本行被分割后的第 2 个字段列,$3 表示文本行被分割后的第 3 个字段列,$n 表示文本行被分割后的第 n 个字段列
  • NR 表示文件中的行号,表示当前是第几行
  • NF 表示文件中的当前行列的个数,类似于 mysql 数据表里面每一条记录有多少个字段
  • FS 表示 awk 的输入分隔符,默认分隔符为空格和制表符,你可以对其进行自定义设置
  • OFS 表示 awk 的输出分隔符,默认为空格,你也可以对其进行自定义设置
  • FILENAME 表示当前文件的文件名称,如果同时处理多个文件,它也表示当前文件名称

比如我们有这么一个文本文件 fruit.txt 内容如下,我将用它来向你演示如何使用 awk 命令工具

peach    100   Mar  1997   China
Lemon    150   Jan  1986   America
Pear     240   Mar  1990   Janpan
avocado  120   Feb  2008   china

awk '{print $0}' fruit.txt   # 表示打印输出文件的每一整行的内容
awk '{print $1}' fruit.txt   # 表示打印输出文件的每一行的第 1 列内容
awk '{print $1, $2}' fruit.txt

文件的每一行的每一列的内容除了可以用 print 命令打印输出以外,还可以对其进行赋值

awk '{$2 = "***"; print $0}' fruit.txt

上面的例子就是表示通过对 $2 变量进行重新赋值,来隐藏每一行的第 2 列内容,并且用星号 * 来代替其输出

在参数列表中加入一些字符串或者转义字符之类的东东

awk '{print $1 "\t" $2 "\t" $3}' fruit.txt

像上面这样,你可以在 print 的参数列表中加入一些字符串或者转义字符之类的东东,让输出的内容格式更漂亮,但一定要记住要使用双引号。

awk 内置 NR 变量表示每一行的行号

awk '{print NR "\t" $0}' fruit.txt

1   peach    100   Mar  1997   China
2   Lemon    150   Jan  1986   America
3   Pear     240   Mar  1990   Janpan
4   avocado  120   Feb  2008   china

awk 内置 NF 变量表示每一行的列数

awk '{print NF "\t" $0}' fruit.txt

5   peach    100   Mar  1997   China
5   Lemon    150   Jan  1986   America
5   Pear     240   Mar  1990   Janpan
5   avocado  120   Feb  2008   china

awk 中 $NF 变量的使用

awk '{print $NF}' fruit.txt

上面这个 $NF 就表示每一行的最后一列,因为 NF 表示一行的总列数,在这个文件里表示有 5 列,然后在其前面加上 $ 符号,就变成了 $5 ,表示第 5 列

awk '{print $(NF - 1)}' fruit.txt

1997
1986
1990
2008

上面 $(NF-1) 表示倒数第 2 列, $(NF-2) 表示倒数第 3 列,依次类推。

awk 'NR % 6'        # 打印出了6倍数行之外的其他行
awk 'NR > 5'        # 打印第5行之后内容,类似 `tail -n +6` 或者 `sed '1,5d'`
awk 'NF >= 6'       # 打印大于等于6列的行
awk '/foo/ && /bar/'    # 打印匹配 `/foo/` 和 `/bar/` 的行
awk '/foo/ && !/bar/'   # 打印包含 `/foo/` 不包含 `/bar/` 的行
awk '/foo/ || /bar/'    # 或
awk '/foo/,/bar/'       # 打印从匹配 `/foo/` 开始的行到 `/bar/` 的行,包含这两行

awk 同时处理多个文件

awk '{print FILENAME "\t" $0}' demo1.txt demo2.txt

当你使用 awk 同时处理多个文件的时候,它会将多个文件合并处理,变量 FILENAME 就表示当前文本行所在的文件名称。

BEGIN 关键字的使用

在脚本代码段前面使用 BEGIN 关键字时,它会在开始读取一个文件之前,运行一次 BEGIN 关键字后面的脚本代码段, BEGIN 后面的脚本代码段只会执行一次,执行完之后 awk 程序就会退出

awk 'BEGIN {print "Start read file"}' /etc/passwd

awk 脚本中可以用多个花括号来执行多个脚本代码,就像下面这样

awk 'BEGIN {print "Start read file"} {print $0}' /etc/passwd

END 关键字使用方法

awk 的 END 指令和 BEGIN 恰好相反,在 awk 读取并且处理完文件的所有内容行之后,才会执行 END 后面的脚本代码段

awk 'END {print "End file"}' /etc/passwd
awk 'BEGIN {print "Start read file"} {print $0} END {print "End file"}' /etc/passwd

在 awk 中使用变量

可以在 awk 脚本中声明和使用变量

awk '{msg="hello world"; print msg}' /etc/passwd

awk 声明的变量可以在任何多个花括号脚本中使用

awk 'BEGIN {msg="hello world"} {print msg}' /etc/passwd

在 awk 中使用数学运算,在 awk 中,像其他编程语言一样,它也支持一些基本的数学运算操作

awk '{a = 12; b = 24; print a + b}' company.txt

上面这段脚本表示,先声明两个变量 a = 12 和 b = 24,然后用 print 打印出 a 加上 b 的结果。

请记住 awk 是针对文件的每一行来执行一次单引号 里面的脚本代码,每读取到一行就会执行一次,文件里面有多少行就会执行多少次,但 BEGIN 和 END 关键字后脚本代码除外,如果被处理的文件中什么都没有,那 awk 就一次都不会执行。

awk 还支持其他的数学运算符

+ 加法运算符
- 减法运算符
* 乘法运算符
/ 除法运算符
% 取余运算符

在 awk 中使用条件判断

比如有一个文件 company.txt 内容如下

yahoo   100 4500
google  150 7500
apple   180 8000
twitter 120 5000

我们要判断文件的第 3 列数据,也就是平均工资小于 5500 的公司,然后将其打印输出

awk '$3 < 5500 {print $0}' company.txt

上面的命令结果就是平均工资小于 5500 的公司名单,$3 < 5500 表示当第 3 列字段的内容小于 5500 的时候才会执行后面的 {print $0} 代码块

awk '$1 == "yahoo" {print $0}' company.txt

awk 还有一些其他的条件操作符如下

< 小于
<= 小于或等于
== 等于
!= 不等于
> 大于
>= 大于或等于
~ 匹配正则表达式
!~ 不匹配正则表达式

使用 if 指令判断来实现上面同样的效果

awk '{if ($3 < 5500) print $0}' company.txt

上面表示如果第 3 列字段小于 5500 的时候就会执行后面的 print $0

在 awk 中使用正则表达式

比如现在我们有这么一个文件 poetry.txt 内容如下:

This above all: to thine self be true
There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival

使用正则表达式匹配字符串 “There” ,将包含这个字符串的行打印并输出

awk '/There/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow

使用正则表达式配一个包含字母 t 和字母 e ,并且 t 和 e 中间只能有任意单个字符的行

awk '/t.e/{print $0}' poetry.txt

There is nothing either good or bad, but thinking makes it so
There’s a special providence in the fall of a sparrow
No matter how dark long, may eventually in the day arrival

如果只想匹配单纯的字符串 “t.e”, 那正则表达式就是这样的 /t.e/ ,用反斜杠来转义 . 符号 因为 . 在正则表达式里面表示任意单个字符。

使用正则表达式来匹配所有以 “The” 字符串开头的行

awk '/^The/{print $0}' poetry.txt

在正则表达式中 ^ 表示以某某字符或者字符串开头。

使用正则表达式来匹配所有以 “true” 字符串结尾的行

awk '/true$/{print $0}' poetry.txt

在正则表达式中 $ 表示以某某字符或者字符串结尾。

$ awk ‘/m[a]t/{print $0}’ poetry.txt

No matter how dark long, may eventually in the day arrival

上面这个正则表达式 /m[a]t/ 表示匹配包含字符 m ,然后接着后面还要包含中间方括号中表示的单个字符 a ,最后还要包含字符 t 的行,输出结果中只有单词 “matter” 符合这个正则表达式的匹配。因为正则表达式 [a] 方括号中表示匹配里面的任意单个字符。

继续上面的一个新例子如下

awk '/^Th[ie]/{print $0}' poetry.txt

这个例子中的正则表达式 /^Th[ie]/表示匹配以字符串 “Thi” 或者 “The” 开头的行,正则表达式方括号中表示匹配其中的任意单个字符。

再继续上面的新的用法

awk '/s[a-z]/{print $0}' poetry.txt

正则表达式 /s[a-z]/ 表示匹配包含字符 s 然后后面跟着任意 a 到 z 之间的单个字符的字符串,比如 “se”, “so”, “sp” 等等。

正则表达式 [] 方括号中还有一些其他用法比如下面这些

[a-zA-Z]  表示匹配小写的 a 到 z 之间的单个字符,或者大写的 A 到 Z 之间的单个字符
[^a-z]    符号 `^` 在方括号里面表示取反,也就是非的意思,表示匹配任何非 a 到 z 之间的单个字符

正则表达式中的星号 * 和加号 + 的使用方法,* 表示匹配星号前字符串 0 次或者多次,+ 和星号原理差不多,只是加号表示任意 1 个或者 1 个以上,也就是必须至少要出现一次。

正则表达式问号 ? 的使用方法,正则中的问号 ? 表示它前面的字符只能出现 0 次 或者 1 次。

正则表达式中的 {} 花括号用法,花括号 {} 表示规定它前面的字符必须出现的次数,像这个 /go{2}d/ 就表示只匹配字符串 “good”,也就是中间的字母 “o” 必须要出现 2 次。

正则表达式中的花括号还有一些其他的用法如下

/go{2,10}d/   表示字母 "o" 只能可以出现 2次,3次,4次,5次,6次 ... 一直到 10 次
/go{2,}d/     表示字母 "o" 必须至少出现 2 次或着 2 次以上

正则表达式中的圆括号表示将多个字符当成一个完整的对象来看待。比如 /th(in){1}king/ 就表示其中字符串 “in” 必须出现 1 次。而如果不加圆括号就变成了 /thin{1}king/ 这个就表示其中字符 “n” 必须出现 1 次。

一些组合使用

使用 awk 过滤 history 输出,找到最常用的命令

history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head

过滤文件中重复行

awk '!x[$0]++' <file>

将一行长度超过72字符的行打印

awk 'length>72' file

查看最近哪些用户使用系统

last | grep -v "^$" | awk '{ print $1 }' | sort -nr | uniq -c

2018-01-12 linux , command , awk , ed , editor

斐讯 T1 盒子去除广告

斐讯投资送硬件这路子是着魔了,所以在斐讯 K2P 之后又入手了这个 T1 的盒子,配置 2G RAM,16G Sdcard,配置还不错。接口有 USB,网口,HDMI 口,还有 一个 AV 输出口,日常使用是没有任何问题的,初尝试一下非常流畅,不过让我不爽的是,第一次进入竟然需要验证手机,说是 CIBN 盒子的验证,如果不注册还不让进入。

配置

CPU:Amlogic S912 八核 Cortex-A53 CPU up to 2.0GHz GPU:ARM Mali-T820MP3 GPU up to 750MHz

2G RAM+16G ROM,双频 WIFI,且支持 ac,但只是单天线

删除不需要的应用

首先,进入盒子的设置 – 高级 – 远程调试打开,然后下载 adb, 开发过 Android 的人,或者大概熟悉 Android 的人一定用过这个命令,我之前也写过文章,总结 adb 常用的命令

将盒子通电,将盒子联网(可以使用网线,也可以使用无线网络),只要能够在局域网中有一个 IP 地址,然后进路由器里记录,找到盒子的 IP(192.168.x.x)地址。

adb connect 192.168.x.x        (盒子 IP)
adb shell
pm list packages # 可以用来查看安装的应用列表
pm uninstall -k --user 0 com.feixun.dangbeimarket     #自带 cibn 当贝市场
pm uninstall -k --user 0 com.tianci.ad                #广告
pm uninstall -k --user 0 com.feixun.qiyiguo           #自带爱奇艺
pm uninstall -k --user 0 cn.cibn.health               #健康中国
pm uninstall -k --user 0 com.pptv.tvsports.preinstall #CIBN 体育
pm uninstall -k --user 0 com.phicomm.phiweather       #天气
pm uninstall -k --user 0 com.pplive.atv               #聚精彩
pm uninstall -k --user 0 cn.cibntv.ott                #CIBN 高清影视
pm uninstall -k --user 0 com.phicomm.update           #斐讯 OTA 升级
pm uninstall -k --user 0 com.phicomm.datatracker      #斐讯数据收集

如果想要恢复最原始的状态,在设置选项里,恢复出厂设置,还是最原来的状态。

添加过滤规则

如果路由器支持过滤广告,可以添加如下两条规则:

asimgs.cp61.ott.cibntv.net
hoisin.coocaatv.com

安装应用

简单的方法,只通过遥控器操作,先用系统自带的当贝市场,找到“视频加速器”这个应用,安装然后运行,就会在视频加速器运行界面的下方,看到有“当贝市场”,再安装这个当贝市场。

如果你有需要安装本地文件的 APK 安装包,有两种方式,推荐使用 adb 方式安装,先将想要安装的 apk,下载到本地文件,然后 adb 连接盒子

adb connect <ip>
adb install /path/to/app.apk   # 安装本地路径下的应用

直接将本地的应用安装到盒子。

如果安装不成功,记得禁用安装验证,在 adb 连接的情况下

settings put global package_verifier_enable 0

或者用“悟空遥控器”这个应用,当贝市场里可以搜索它来安装,但也要在手机上下载安装“悟空遥控器”APP,里面有个“本地 APK 推送”,用它就可以安装你想要装的应用了,只要有 APK 安装包。

最后要强烈推荐一个投屏 App,乐播投屏,因为斐讯 T1 并没有 DLNA 的功能,但幸而只要一个 App 就能够解决,这样就能够将手机爱奇艺上的内容快速的投屏到盒子上。

斐讯 t1_home_screen

su 密码

恩山最近的帖子更新了 T1 su 的密码,记录一下

31183118

最后这篇文章 是我使用过并且觉得不错的 Android TV 的应用,可以参考。

reference


2018-01-10 android , box , adb , apk , ads , phicomm , 斐讯 , tv , 电视盒子

Linux 下查看内存使用

在 Linux 下,命令行就是一切,GUI 不是任何时候都可用的,所以掌握一定的常用命令,能够方便日常使用,比如查看进程,查看内存占用,等等,这篇文章就总结了一下 Linux 下查看当前系统占用内存的命令。

free

free 命令是最常用的查看系统使用内存及剩余可用内存的命令。free 命令能够显示系统使用和可用的物理内存,同时能够显示 swap 分区内容使用量。free 命令也能够显示被 kernel 使用的 buffers 和 caches。free 命令通过解析 /proc/meminfo 来收集信息。

free -m
              total        used        free      shared  buff/cache   available
Mem:          15911        8803        1100         886        6007        3750
Swap:         16246        3100       13146

-m 选项表示使用 MB 来显示数据,上面表示总共 15911 MB 内存,使用了 8803 M 内存。

A buffer is something that has yet to be “written” to disk. A cache is something that has been “read” from the disk and stored for later use.

上面的解释非常清晰, buffer 用于存放要输出到disk的数据,cache是存放disk上读取的数据,这两者都为了提高IO性能由OS控制管理的。

还有一些其他比较有用的参数,比如 free -h 显示比较可读 h for human,还有 free -g 使用 GB 为单位显示信息。

/proc/meminfo

第二种方法就是直接读取 /proc/meminfo 文件, /proc 文件系统并不是真实的文件系统,不真正包含文件,他是虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件。

运行命令 cat /proc/meminfo 检查其中的 MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree 这几个字段,显示的内容和 free 命令是一样的。

vmstat

通过 vmstat-s 选项来显示内存使用

vmstat -s
 16293800 K total memory
  9125168 K used memory
 10143256 K active memory
  2252796 K inactive memory
  1001072 K free memory
   746520 K buffer memory
  5421040 K swap cache
 16636924 K total swap
  3174096 K used swap
 13462828 K free swap
 33736485 non-nice user cpu ticks
    57413 nice user cpu ticks
 10985936 system cpu ticks
465486969 idle cpu ticks
  6378633 IO-wait cpu ticks
        0 IRQ cpu ticks
   114100 softirq cpu ticks
        0 stolen cpu ticks
 41745039 pages paged in
125109404 pages paged out
  1098642 pages swapped in
  2128871 pages swapped out
2199460184 interrupts
3279263642 CPU context switches
1514898192 boot time
   3676272 forks

前面几行就能看到内存使用。

vmstat 命令能够显示进程,内存,paging,block IO,traps,disks,和 cpu 的活动及使用状态。

top

top 命令通常被用来检查每一个进程的内存和CPU使用,但其实也可以用他来查看系统整个的内存使用,最上方的数据就能看到。

同样,可视化程度更高的 htop 也能够快速的查看到使用的内存

dmidecode

通过一下命令也可以查看,这个命令只能够查看到物理内存条的大小,但是可以提供给我们一个信息就是系统的总内存,是由两个8G内存组成,而不是由8个2G内存条组成。

sudo dmidecode -t 17

选项 -t--type 缩写

sudo dmidecode -t 17
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 2.8 present.

Handle 0x001A, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM1
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 12303315
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

Handle 0x001B, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM2
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 1230330E
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

这个输出信息中可以看到电脑使用的两条 8G 的内存,类型 DDR4,速度是 2400 MHz.

下面这个命令也能够查看电脑上安装的内存条

sudo lshw -short -C memory
H/W path        Device     Class          Description
=====================================================
/0/0                       memory         64KiB BIOS
/0/18/15                   memory         128KiB L1 cache
/0/18/16                   memory         1MiB L2 cache
/0/18/17                   memory         8MiB L3 cache
/0/14                      memory         128KiB L1 cache
/0/19                      memory         16GiB System Memory
/0/19/0                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/1                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/2                    memory         [empty]
/0/19/3                    memory         [empty]
/0/100/1f.2                memory         Memory controller

2018-01-10 linux , memory , ram , free , command

Gson 使用

Gson 是 Google 发布的一个用于序列化和反序列化 json 的工具库,可以非常轻松的实现 json 到 java Object 的转变,也同样非常简单的可以将一个 Java 实例序列化为 json。

基本使用

Gson 最基本的使用方法,无非就是 toJson()fromJson() 两个函数,对于简单类,可以使用如下方式:

String json = gson.toJson(target); // serializes target to Json MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2

如果类中含有数组,会需要用到 toJson(Object, Type)fromJson(String, Type) 这两个方法:

Type listType = new TypeToken<List<String>>() {}.getType();
List<String> target = new LinkedList<String>();
target.add("blah");

Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);

属性重命名

Gson 默认情况下会使用 POJO 一致的属性名去解析和生成 json 字串,但是如果想要解析和生成的时候重命名字段,可以使用 @SerializedName 来重命名。

比如 json 中的字段叫做 email_address,而在 Java 类中,可以改为 emailAddress。这样,gson 在生成时会自动将 emailAddress 属性,改为 email_address

@SerializedName("email_address")
public String emailAddress;

如果 emailAddress 在 json 中还有其他的方式,也可以使用:

@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;

重新格式化序列化和反序列化的内容

在将 json 转化为 Java Object 的时候,可以自定义 Deserializer ,格式化其中的某一些字段,比如下面内容,将 dateOfBirth 修改了格式。

public class ActorGsonDeserializer implements JsonDeserializer<ActorGson> {
    private SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a");

    @Override
    public ActorGson deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonImdbId = jsonObject.get("imdbId");
        JsonElement jsonDateOfBirth = jsonObject.get("dateOfBirth");
        JsonArray jsonFilmography = jsonObject.getAsJsonArray("filmography");

        ArrayList<String> filmList = new ArrayList<>();
        if (jsonFilmography != null) {
            for (int i = 0; i < jsonFilmography.size(); i++) {
                filmList.add(jsonFilmography.get(i).getAsString());
            }
        }

        ActorGson actorGson = null;
        try {
            actorGson = new ActorGson(jsonImdbId.getAsString(), sdf.parse(jsonDateOfBirth.getAsString()), filmList);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return actorGson;
    }
}



Gson gson = new GsonBuilder()
        .registerTypeAdapter(ActorGson.class, new ActorGsonDeserializer())
        .create();

忽略某些字段

使用 transient 关键字

在 java 序列化是,一旦变量被 transient 修饰,变量将不再是持久化的一部分,变量内容在序列化后无法获得访问。同样如果在使用 Gson 序列化 json 的时候,添加关键字 transient 同样,Gson 也会忽略该字段:

private transient int id;

需要注意的是,如果一个 field 是 static 静态变量,gson 也会排除。Gson 在创建的时候可以使用 excludeFieldsWithModifiers 来指定排除的 field:

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();

使用 @Expose 注解保留关心的字段

最早不知道 transient 关键字的时候,看文档中只写了 @Expose 注解,但其实效果是一样的。使用 @Expose 注解来保留关心的字段,其他不需要的字段可以不注解,同样能够达到效果。

private int id; // 忽略id
@Expose private String name;    //保留name

如果使用 @Expose 注解,那么则需要使用 GsonBuilder()

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

自定义 ExclusionStrategy 规则

如果有更加复杂的排除规则,比如某一批Field,或者指定的 Class 不需要 serialize ,可以使用 ExclusionStrategy 来自定规则。

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.jutils.gson.entity.Car;
import com.jutils.gson.entity.PersonStrategy;

/**
 * ExclusionStrategy
 *
 * A strategy (or policy) definition that is used to decide whether or not a field or top-level class should be serialized or deserialized as part of the JSON output/input.
 */
public class ExcluStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        // ignore id field
        if (f.getDeclaringClass() == PersonStrategy.class && f.getName().equals("id")) {
            return true;
        }
        return false;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        if (aClass == Car.class) {
            return true;
        }
        return false;
    }
}

比如说上面这个,就忽略 PersonStrategy 类中的 Field “id”,还有 Car 类。如果使用这种方式,那么需要在构造 gson 时:

Gson gson = new GsonBuilder().setExclusionStrategies(new ExcluStrategy()).create();

实现自定义注解

如果熟悉了这个 ExclusionStrategy 就可以书写自己的注解。

注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

AnnotationExclusionStrategy 类

public class AnnotationExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes fieldAttributes) {
        return fieldAttributes.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        return false;
    }
}

测试方法

@Test
public void annotationTest() {
    PersonAnnotation annotation = new PersonAnnotation(1L, "name", 10);
    Gson gson = new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
    String s = gson.toJson(annotation);
    System.out.println(s);
}

reference


2018-01-09 java , gson , json , google

小米路由器 3G 开启SSH 安装 MT 工具箱

下面是小米路由器折腾记录,包括开启 SSH,然后安装 MT 工具箱,主要是为了其中的两个插件,一个是去广告,一个是SS代理,不过附带竟然发现了 frp 插件,开心啊。下面就是具体的记录。

小米路由器刷入开发版

下载 开发版,在后台点击上传安装开发版的bin,然后等待重启,完成开发版安装。

小米路由器开始 SSH

小米帐号绑定小米路由器,设置路由器可正常上网,并使用手机版小米wifi绑定路由器,然后在绑定小米账号的前提下,进入 https://d.miwifi.com/rom/ssh 这个网站,然后找到 SSH 登录的 root 密码,之后会用到。

工具包使用方法:小米路由器需升级到开发版0.5.28及以上,小米路由器mini需升级到开发版0.3.84及以上,小米路由器3即将支持。注意:稳定版不支持。 请将下载的工具包bin文件复制到U盘(FAT/FAT32格式)的根目录下,保证文件名为miwifi_ssh.bin; 断开小米路由器的电源,将U盘插入USB接口; 按住reset按钮之后重新接入电源,指示灯变为黄色闪烁状态即可松开reset键; 等待3-5秒后安装完成之后,小米路由器会自动重启,之后您就可以尽情折腾啦 :)

刷入 MT 工具箱

MT工具箱是目前第三方插件里面最为方便易用的插件集合

KMS 服务器,VSFTP 服务器,VPN 服务器,远程管理,ARIA2,Koolproxy广告过滤,阿呆喵广告过滤,Shadowsocks,webshell, frp 服务

Misstar Tools 2.0工具箱安装,经过上面的几个步骤,开启 SSH 之后,使用 ssh root@192.168.31.1 来连接路由器,使用之前获取的 SSH root 密码登录,进去之后 passwd 修改 root 密码,以方便下一次使用,然后直接执行如下代码,就能安装 MT 工具箱。

wget http://www.misstar.com/tools/appstore/install.sh -O /tmp/install.sh && chmod a+x /tmp/install.sh && /tmp/install.sh 

卸载

wget http://www.misstar.com/tools/uninstall.sh -O /tmp/uninstall.sh && chmod +x /tmp/uninstall.sh && /tmp/uninstall.sh

安装 Shadowsocks 科学上网插件的方法

在开启 SSH,并且安装 Misstar Tools 工具箱的前提下,有两种方法可以安装 Shadowsocks 插件,第一种就是使用备份的文件,传入路由器之后运行安装,第二种直接在安装页面修改页面内容,推荐使用第二种方法。

方法一:使用文件安装

  1. 将压缩包中两个文件传到路由器,放在同一目录,名字不要改。
  2. 执行:chmod +x ./install_ss & ./install_ss add
  3. 刷新路由器后台,科学上网插件已经出现了。
  4. 卸载的时候执行:chmod +x ./install_ss & ./install_ss del

from: http://bbs.xiaomi.cn/t-13765387

方法二:修改页面ID

使用 Chrome 打开 MT 插件管理页面,使用开发者工具,定位页面中任意一个 安装 按钮,然后找到代码中的 id="ftp" 字样,修改为 id="ss" ,然后点安装,成功后会回到 MT 工具箱首页,配置使用即可。

刷其他固件

按照上面官方的步骤操作完路由器就已经获取root权限了,再使用ssh工具连接路由即可,建议在进行下一步操作之前备份原版分区文件

root@XiaoQiang:~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 07f80000 00020000 "ALL"
mtd1: 00080000 00020000 "Bootloader"
mtd2: 00040000 00020000 "Config"
mtd3: 00040000 00020000 "Bdata"
mtd4: 00040000 00020000 "Factory"
mtd5: 00040000 00020000 "crash"
mtd6: 00040000 00020000 "crash_syslog"
mtd7: 00040000 00020000 "reserved0"
mtd8: 00400000 00020000 "kernel0"
mtd9: 00400000 00020000 "kernel1"
mtd10: 02000000 00020000 "rootfs0"
mtd11: 02000000 00020000 "rootfs1"
mtd12: 03580000 00020000 "overlay"
mtd13: 012a6000 0001f000 "ubi_rootfs"
mtd14: 030ec000 0001f000 "data"

查看U盘挂载的位置命令:

df -h

U盘一般是/extdisks/开头,后面的可能不一样,我的是:/extdisks/sda4/

备份小米路由器3G原版分区到文件,每行是一条命令,分别执行,最后一条可能会报错,可不用理会,最有用的是mtd0-mtd4

dd if=/dev/mtd0 of=/extdisks/sda4/rom/ALL.bin
dd if=/dev/mtd1 of=/extdisks/sda4/rom/Bootloader.bin
dd if=/dev/mtd2 of=/extdisks/sda4/rom/Config.bin
dd if=/dev/mtd3 of=/extdisks/sda4/rom/Bdata.bin
dd if=/dev/mtd4 of=/extdisks/sda4/rom/Factory.bin
dd if=/dev/mtd5 of=/extdisks/sda4/rom/crash.bin
dd if=/dev/mtd6 of=/extdisks/sda4/rom/crash_syslog.bin
dd if=/dev/mtd7 of=/extdisks/sda4/rom/reserved0.bin
dd if=/dev/mtd8 of=/extdisks/sda4/rom/kernel0.bin
dd if=/dev/mtd9 of=/extdisks/sda4/rom/kernel1.bin
dd if=/dev/mtd10 of=/extdisks/sda4/rom/rootfs0.bin
dd if=/dev/mtd11 of=/extdisks/sda4/rom/rootfs1.bin
dd if=/dev/mtd12 of=/extdisks/sda4/rom/overlay.bin
dd if=/dev/mtd13 of=/extdisks/sda4/rom/ubi_rootfs.bin
dd if=/dev/mtd14 of=/extdisks/sda4/rom/data.bin

首先,下载 Breed 刷入不死 breed,

下载地址:https://breed.hackpascal.net/ (搜索 breed-mt7621-xiaomi-r3g.bin)

用命令下载,在电脑终端中运行

wget https://breed.hackpascal.net/breed-mt7621-xiaomi-r3g.bin

将文件传入路由器

scp breed-mt7621-xiaomi-r3g.bin root@192.168.31.1:/tmp/

进入路由器

ssh root@192.168.31.1
cd /tmp
mtd -r write breed-mt7621-xiaomi-r3g.bin Bootloader

机器会重新启动,指示灯变蓝,确保电脑设置为自动获取 IP 地址,使用网线连接。刷入成功后,断掉电源,用东西顶住复位键不松开,然后再接上电源等待10秒左右放开复位键,浏览器输入 http://192.168.1.1 即可进行Breed Web恢复界面:

如果要刷入其他固件,打开Breed Web恢复控制台,点击左侧“固件更新”,钩选“固件”,选择固件,再点“上传”

更多内容可查看: http://www.right.com.cn/forum/thread-161906-1-1.html

reference


2018-01-07 router , xiaomi

斐讯 k2p 刷机

以前也整理过一篇 TP LINK MR12U 刷 Openwrt ,路由器刷机和手机刷机异曲同工。不过刷机需谨慎,稍有错误就有可能导致硬件损坏。前些时候入手了一个斐讯的 K2P,

大致分为几步,第一步开启 telnet,刷入 uboot,然后通过 uboot 刷入新固件。

因为新版本固件 V22.7.8.2版本及之后 需要使用 特殊工具 才能开启 telnet ,所以先要使用这个工具来开启 telnet。然后再使用 a大 的脚本安装 breed。

Opboot及Breed进入方法:

如果你当前是官改或其他第三方固件,请在opboot或breed输入:

  1. 计算机设置为自动获取IP,计算机网线连接K2P的任一LAN口
  2. K2P断电,按住K2P复位键,K2P开电,按住10秒后放开
  3. 访问http://192.168.1.1,可刷入K2P的任何版本
  4. 重新启动后建议K2P恢复一次出厂设置;

官改版本

地址: http://www.right.com.cn/forum/thread-221578-1-1.html

官改版本比较稳定,占用内存小,同时也带去广告,SS/SSR,内网穿透等等功能

潘多拉固件

地址: http://www.right.com.cn/forum/thread-216468-1-1.html

潘多拉版本,同时也支持 SS/SSR,去广告,定时重启等等功能

梅林固件

地址: http://www.right.com.cn/forum/thread-255053-1-1.html

  • 支持adbyby及koolproxy广告屏蔽
  • 支持最新版本ss及S-S R客户端
  • 支持webshell、kms、ngrok内网穿透、wol网络唤醒
  • 支持ssh、用户脚本、硬盘休眠、指示灯控制等

Openwrt 固件

地址: http://www.right.com.cn/forum/thread-240730-1-1.html

K2P基于openwrt最新稳定版chaos_calmer的固件

Padavan

荒野无灯 http://www.right.com.cn/forum/thread-218704-1-1.html 这个固件和原版固件的性能对比可以参考这一篇 http://www.acwifi.net/3173.html

下载地址:http://files.80x86.io/router/rom/K2P/

k2p 全套其他的固件,可以参考这个帖子 http://www.right.com.cn/forum/thread-254947-1-1.html

最后总结下,其实大部分的教程都能在恩山找到,在斐讯的论坛里面,找精华贴,能过滤很多杂乱的信息。并且通过 Google 关键词搜索也能够很快的找到答案。


2018-01-06 k2p , 路由器 , router , openwrt , linux , phicomm

CollectionUtils

Apache commons 包中 CollectionUtils 常用方法介绍。

org.apache.commons.collections.CollectionUtils;

将所有元素添加到一个集合

addAll(Collection<C> collection, C[] elements) 将后面的元素添加到集合中,或者是添加一个非空元素 addIgnoreNull(Collection<T> collection, T object)

Collate (整理)两个列表

将两个有序的列表合二为一,使用 collate() 方法

List<Customer> sortedList = CollectionUtils.collate(list1, list2);

将一个列表中一种类型的对象转变为另一组对象

使用 collect() 方法

Collection<Address> addressCol = CollectionUtils.collect(list1, 
  new Transformer<Customer, Address>() {
    public Address transform(Customer customer) {
        return customer.getAddress();
    }
});

判断是否包含

containsAll(Collection<?> coll1, Collection<?> coll2) 判断是否 coll2 中所有元素都在 coll1 中

containsAny(Collection<?> coll1, Collection<?> coll2) 判断 coll2 中有没有至少一个元素在 coll1 中

CollectionUtils.isSubCollection(list3, list1) 可以使用该函数来判断两个集合是否是包含关系。

过滤元素

使用 CollectionUtils.filter 可以用来过滤 Collection 中不满足条件的元素。这个函数接收两个参数,第一个参数为列表,第二个参数为 Predicate 用来设置条件。

boolean isModified = CollectionUtils.filter(linkedList1, 
  new Predicate<Customer>() {
    public boolean evaluate(Customer customer) {
        return Arrays.asList("Daniel","Kyle").contains(customer.getName());
    }
});

filter() 函数是当 Predicate 返回 false 时移除元素;filterInverse() 函数是当 Predicate 返回 true 时移除元素。

如果想要获取过滤的结果,可以使用selectselectRejected 这两个函数。

判断集合是否为空

CollectionUtils.isEmpty(null): true CollectionUtils.isEmpty(new ArrayList()): true CollectionUtils.isEmpty({a,b}): false ## 判断集合是否不为空 CollectionUtils.isNotEmpty(null): false CollectionUtils.isNotEmpty(new ArrayList()): false CollectionUtils.isNotEmpty({a,b}): true

2个集合间的操作

集合a: {1,2,3,3,4,5} 集合b: {3,4,4,5,6,7} CollectionUtils.union(a, b)(并集): {1,2,3,3,4,4,5,6,7} CollectionUtils.intersection(a, b)(交集): {3,4,5} CollectionUtils.disjunction(a, b)(交集的补集): {1,2,3,4,6,7} CollectionUtils.disjunction(b, a)(交集的补集): {1,2,3,4,6,7} CollectionUtils.subtract(a, b)(A与B的差): {1,2,3} CollectionUtils.subtract(b, a)(B与A的差): {4,6,7}

更多的内容可以 Google: https://www.google.com/search?q=CollectionUtils

示例代码可以到 https://github.com/einverne/jutils/tree/master/src/test 来查看。


2018-01-03

2017年读书笔记

又到一年的年末,2017 年对于我来说是变化最大的一年,这一年里完成了身份的转变,自此步入社会。这一年年初的时候定下目标年度100本书,看来又是无法完成了,现在看豆瓣的标注,也仅仅只有 30 本左右,距离目标甚是遥远。这里也不想再找借口,2017年中,很大一部分时间共享给了王者荣耀和荒野行动,这两个手游占据了原本打散的碎片化时间。今年也一度差点荒废了 Kindle,Kinde10000 的消失,曾一度让 Kindle 在床头吃灰。

2017 年的读书记录如果再按照以前的分类划分的话,也没太多的变化,唯一增加的分类可能是增加了金融相关的一些书籍。那下面就总结一下好了。

小说

排在首位的永远是小说,在去年读过了大部分的东野圭吾的小说之后,今年似乎找不到主题,除了零星捡漏又看了几部东野圭吾的小说之外,其他作者的很少涉猎。不过今年读过的东野圭吾的小说中也还是有让我记忆比较深刻的几本:

  • 首先是这本《恋爱的贡多拉》,起初看到这本书的名字的时候,甚至一度怀疑这个作者到底是不是东野圭吾,仔细的查看封面和前言之后确认,这确实是东野圭吾的作品。这本书的文风与之前相比截然不同,这么轻松诙谐的文字,也只有东野圭吾这样的鬼才才能写出来吧。看这本书似乎就是看一个剧,东野圭吾通过滑雪场的缆车把书中几对情侣的各种“恩怨情仇”很诙谐的讲述出来,这样的作品直接拿去改编成剧本都丝毫不会有任何违和。不过这也正是这本书的毛病所在,太狗血的爱情桥段,太烂俗的现代都市爱情故事。不过我更欣赏东野圭吾在书中表述出来的不同人物形象和性格,都知道东野圭吾的推理小说中都是一些阴冷黑暗的角色,而这本不是,甚至这一本可以当成恋爱小说看,里面的人物形象都跃然纸上,不同主人公处理事务各不相同,也最终导致他们的结局不同,总之非常有趣。
  • 《东野圭吾的最后致意》这是一本随笔录,我记得以前也看过一部《我晃荡的青春》随笔录,东野圭吾从工程师,毅然辞职当起职业作家一路并不是一帆风顺,在经历了这么多的曲折之后,才有了今天的东野圭吾,从这本随笔录中也能看到当时努力写作的东野圭吾,幽默的文风一如从前。
  • 再就是《风雪追击》这一本不是那么出彩,滑雪场相关
    • 《悖论13》 这算是科幻小说吗?我翻开豆瓣的评论,当时看完我写了,在这本小说中,我看到了好莱坞大片的元素,看到了《2012》,看到了《末日崩塌》,看到了《后天》这样的灾难大片的大场景,或许如果有一天有人把这个小说改编成电影,我也还是会去看一看的。其实这个故事很简答,出现了一个平行宇宙,里面的地球出现了各种灾难,一帮人自救互救的故事。
  • 《魔球》很早的作品了,平平
  • 《沉睡的人鱼之家》书中提出了一个值得思考的假设“如果一个人有着正常人的所有生命特征但长睡不醒那算是死亡吗?如果把一把刀插到植物人身上我算杀人吗?”
  • 《白马山庄杀人事件》或者叫 鹅妈妈旅舍杀人事件,关于这本书的出版,其实在《东野圭吾的最后致意》一书中也有提到,现在出版的名字是编辑给起的,原本的名字并不是白马山庄,看完其实也知道整本书和白马山庄并没有大多的关系。
  • 《疾风回旋曲》看完这本小说之后,也顺便把改编的电影也看了,东野圭吾是那么的喜欢滑雪嘛~最近太多关于滑雪场的故事了。
  • 《布谷鸟的蛋是谁的》 年中的时候看的,又是一本滑雪有关的,当然书中更多的讲得是关于孩子,血缘等等
  • 《拉普拉斯的魔女》说实话我不知道怎么归纳这一本,科幻?悬疑?就像当时在豆瓣写下的短评一样,我以为是讲述一个类似《达芬奇密码》这样保护秘密的故事,但其实故事更加复杂曲折。
  • 《平行世界·爱情故事》”带着科幻元素的爱情故事,及时记忆都不在了,爱情也依然不变。”现在看这段短评怎感觉和 《星际穿越》中及时时光穿梭,亲情不变这么像呢。
  • 《黎明之街》也是老早的作品了,tag 婚外情

专业书

今年看的专业书虽然数量有所增长,但是还是没达到目标。

  • 《REST API Design Rulebook》
  • 《Mastering Regular Expressions 3rd : Regular Expressions》
  • 《Mastering Nginx》
  • 《微服务设计》
  • 《Spring实战(第4版)》

还有好多都零零落落没看完。

其他

  • 支付战争 : 互联网金融创世纪,一个产品到达一定瓶颈的时候,需要借助很多的营销策略让用户增长跟上一个层级,这本书就是 PayPal 当时早期员工写的如何帮助 PayPal 获取用户的回忆录
  • 增长黑客 : 创业公司的用户与收入增长秘籍, tag 营销、获取用户
  • 《告白与告别》
  • 《冷浪漫》
  • 《细说汉字》
  • 《汉字构形学讲座》
  • 《基金投资入门与实战技巧》
  • 《一本书读懂黄金白银投资理财》
  • 《你今天真好看》
  • 《重新定义公司 : 谷歌是如何运营的》
  • 《孩子你慢慢来》

2017-12-31 book , reanding

Open Falcon 使用和介绍

OpenFalcon是一款企业级、高可用、可扩展的开源监控解决方案,提供实时报警、数据监控等功能。可以非常容易的监控整个服务器的状态,比如磁盘空间,端口存活,网络流量等等。

最近有些监控需求所以看了一下其中涉及到概念。

一些基础概念

Open-Falcon,采用和OpenTSDB相似的数据格式:metric、endpoint加多组key value tags,举两个例子:

{
    metric: load.1min,
    endpoint: open-falcon-host,
    tags: srv=falcon,idc=aws-sgp,group=az1,
    value: 1.5,
    timestamp: `date +%s`,
    counterType: GAUGE,
    step: 60
}
{
    metric: net.port.listen,
    endpoint: open-falcon-host,
    tags: port=3306,
    value: 1,
    timestamp: `date +%s`,
    counterType: GAUGE,
    step: 60
}

下面是一段Python上报数据的代码,其中涉及到的参数都是必须传的。

``` #!-- coding:utf8 --

import requests import time import json

ts = int(time.time()) payload = [ { “endpoint”: “test-endpoint”, “metric”: “test-metric”, “timestamp”: ts, “step”: 60, “value”: 1, “counterType”: “GAUGE”, “tags”: “idc=lg,loc=beijing”, },

{
    "endpoint": "test-endpoint",
    "metric": "test-metric2",
    "timestamp": ts,
    "step": 60,
    "value": 2,
    "counterType": "GAUGE",
    "tags": "idc=lg,loc=beijing",
}, ]

r = requests.post(“http://127.0.0.1:1988/v1/push”, data=json.dumps(payload))

print r.text ```

  • metric: 最核心的字段,监控指标名称,代表这个采集项具体度量的是什么, 比如是cpu_idle呢,还是memory_free, 还是qps
  • endpoint: 标明Metric的主体(属主),比如metric是cpu_idle,那么Endpoint就表示这是哪台机器的cpu_idle,一般使用机器的 hostname
  • timestamp: 表示上报该数据时的unix时间戳,注意是整数,代表的是秒
  • value: 代表该metric在当前时间点的值,float64
  • step: 表示该数据采集项的上报周期,这对于后续的配置监控策略很重要,必须明确指定。
  • counterType: 是Open Falcon定义的数据类型,取值只能是COUNTER或者GAUGE二选一,前者表示该数据采集项为计时器类型,后者表示其为原值 (注意大小写)

      - GAUGE:即用户上传什么样的值,就原封不动的存储
      - COUNTER:指标在存储和展现的时候,会被计算为speed,即(当前值 - 上次值)/ 时间间隔
    
  • tags: 监控数据的属性标签,一组逗号分割的键值对, 对metric进一步描述和细化, 可以是空字符串. 比如idc=lg,比如service=xbox等,多个tag之间用逗号分割

说明:这7个字段都是必须指定

如何在上报中断时报警

最近遇到的需求就是如果一段时间内,OpenFalcon 没有收集到数据,也就是 agent 没有采集到数据,程序挂了,或者没有执行,那么就报警。在最开始的时候查看了一下 OpenFalcon 报警函数

all(#3): 最新的3个点都满足阈值条件则报警
max(#3): 对于最新的3个点,其最大值满足阈值条件则报警
min(#3): 对于最新的3个点,其最小值满足阈值条件则报警
sum(#3): 对于最新的3个点,其和满足阈值条件则报警
avg(#3): 对于最新的3个点,其平均值满足阈值条件则报警
diff(#3): 拿最新push上来的点(被减数),与历史最新的3个点(3个减数)相减,得到3个差,只要有一个差满足阈值条件则报警
pdiff(#3): 拿最新push上来的点,与历史最新的3个点相减,得到3个差,再将3个差值分别除以减数,得到3个商值,只要有一个商值满足阈值则报警
lookup(#2,3): 最新的3个点中有2个满足条件则报警

这一下子就懵了,报警触发的条件都是根据最近上报的几个点的阈值来触发的,而我的需求可能是一段时间内根本没有上报数据。

然后仔细查看文档之后,发现 OpenFalcon 有一个 Nodata 配置,Nodata 的配置正好解决了上面的需求,当机器一段时间内中断上报时,Nodata 配置会上报一个指定的值,然后报警函数就能够根据 Nodata 上报的值来报警。

Nodata 的配置在 OpenFalcon 的后台,在 Nodata 页面添加 Nodata ,填写

  • name nodata 的名字,标示什么中断了
  • endpoint 选择 Endpoint ,机器列表,一行一个
  • metric 指定 metric
  • tags 指定 tags
  • type 暂时只支持 GAUGE
  • 周期 秒,与原始监控指标一致
  • 上报中断时补发值

当自定义上报中断的时候 Nodata 就会补发,通过补发的值,比如正常的取值是 >0 的正数值,那么补发的值可以写上 -1 ,然后通过最近连续的三个 -1 来触发报警。

项目的起源

最近看了一篇文章介绍了 Open Falcon 项目的起由,这里面提到了 Open Falcon 为了解决 zabbix 的一些问题而被提出

  • 性能瓶颈,数据量大,zabbix 使用的MySQL写入瓶颈
  • 多套zabbix管理成本高
  • 监控系统不统一

reference


2017-12-29 monitor , log , open-falcon , warning

argparse的使用介绍

argparse 模块可以轻松的写出友好的命令行交互,让使用者轻松定义命令行参数及使用参数。

简单使用

首先创建一个parser

首先通过 argparse 来创建一个 ArgumentParser 对象:

parser = argparse.ArgumentParser(description='Process some integers.')

ArgumentParser 对象会保存命令行基本的信息。

再添加参数

构造好了 ArgumentParser 对象之后使用 add_argument() 来往其中添加参数。

>>> parser.add_argument('integers', metavar='N', type=int, nargs='+',
...                     help='an integer for the accumulator')
>>> parser.add_argument('--sum', dest='accumulate', action='store_const',
...                     const=sum, default=max,
...                     help='sum the integers (default: find the max)')

这些信息都被保存在对象中,直到 parse_args() 函数被调用。 integers 参数保存了一个或者一个以上列表的int值, accumulate 参数则会根据传入的参数选择 sum() 或者 max() 方法。

解析并使用参数

ArgumentParser 通过 parse_args() 来解析和使用参数

>>> parser.parse_args(['--sum', '7', '-1', '42'])
Namespace(accumulate=<built-in function sum>, integers=[7, -1, 42])

关于 ArgumentParser 对象更多的使用方法可以参考官方文档

参数动作

argparse内置6种动作可以在解析到一个参数时进行触发:

  • store 保存参数值,可能会先将参数值转换成另一个数据类型。若没有显式指定动作,则默认为该动作
  • store_const 保存一个被定义为参数规格一部分的值,而不是一个来自参数解析而来的值。这通常用于实现非布尔值的命令行标记
  • store_ture/store_false 保存相应的布尔值。这两个动作被用于实现布尔开关
  • append 将值保存到一个列表中。若参数重复出现,则保存多个值
  • append_const 将一个定义在参数规格中的值保存到一个列表中
  • version 打印关于程序的版本信息,然后退出

参数群组

argparse能将参数定义组合成“群组”。默认情况下是使用两个群组,一个是选项的群组,另一个是必须的与位置相关的参数群组。

在基础解析器中使用add_argument_group()来创建一个“身份认证”群组,然后逐个添加身份认证相关的选项到该群组。

import argparse
parser = argparser.ArgumentParser(add_help=False)
group = parser.add_argument_group('authentication')
group.add_argument('--user', action="store")
group.add_argument('--password', action="store")

互斥选项

定义互斥的选项是选项分组特性的一个特例,使用add_mutually_exclusive_group()而不是add_argument_group()

import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-a', action='store_true')
group.add_argument('-b', action="store_true")
print parser.parse_args()

可变形参列表

你可以配置单个参数的定义使其能够匹配所解析的命令行的多个参数。根据需要或期望的参数个数,设置nargs为这些标识值之一:

含义
N 参数的绝对个数(例如:3)
? 0或1个参数
* 0或所有参数
+ 所有,并且至少一个参数

参数类型

argparse将所有参数值都看作是字符串,除非你告诉它将字符串转换成另一种数据类型。add_argument() 的type参数以一个转换函数作为值,被ArgumentParser用来将参数值从一个字符串转换成另一种数据类型。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', type=int)
parser.add_argument('-f', type=float)
parser.add_argument('--file', type=file)
try:
    print parser.parse_args()
except IOError, msg:
    parser.error(str(msg))

要想将一个输入参数限制为一个预定义集中的某个值,则使用choices参数。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--mode', choices=('read-only', 'read-write'))
print parser.parse_args()

values的类型取决于nargs的值。如果该参数允许多个值,则values会是一个列表,即使其仅包含一个列表项。

reference


2017-12-28 python , argparse , linux , command

电子书

Google+

最近文章

  • 使用 alembic 迁移数据库结构 Alembic 是一个处理数据库更改的工具,它利用 SQLAlchemy 来实现形成迁移。 因为 SQLAlchemy 只会在我们使用时根据 metadata create_all 方法来创建缺少的表 ,它不会根据我们对代码的修改而更新数据库表中的列。它也不会自动帮助我们删除表。 Alembic 提供了一种更新 / 删除表,更改列名和添加新约束的方法。因为 Alembic 使用 SQLAlchemy 执行迁移,它们可用于各种后端数据库。
  • 每天学习一个命令:iotop 查看 Linux 下每个进程 IO 占用 iotop 是一个用来监控磁盘 I/O 的类似 top 的工具,Linux 下 IO 统计工具,比如 iostat, nmon 等只能统计到每个设备的读写情况,如果想要知道哪一个进程占用比较高的 IO 就要使用 iotop。 iotop 使用 Python 语言编写,要求 Python >= 2.5,Linux Kernel >= 2.6.20.
  • 修正关于 HTTP Header 的错误认识 HTTP 请求的 Header 是不区分大小写的!,一直以为 HTTP 请求的请求头是有区分大小的,知道今天调试发现 Spring 将 header 全部处理成小写,然后有人提了 Bug 58464 然后看到 Stackoverflow 上面有人回答。
  • 解决 failed to create bus connection no such file or directory 错误 今天在修改 hostname 使用 sudo hostnamectl set-hostname ds 命令时遇到问题:
  • Vim 的颜色主题 Retro groove color scheme for Vim