艾莉亚的猫 Time is limited, To be a better man

进程分析工具lsof strace pstack pstree使用

Linux环境中下面的这几个命令都很重要,特别是在跟踪定位后台问题时候,需要熟练运用。

1

strace命令是一种强大的工具,能够显示任何由用户空间发出的系统调用。Strace显示这些调用的参数并返回符号形式的值。

下面记录几个常用的option:

-f选项告诉strace同时跟踪fork和vfork出来的进程。

-o**.txt输出到某个文档。

-e execve只记录execve这类系统调用。

-p pid trace一个现有的进程。

strace -fp $pid


pstack是一个脚本工具,其核心实现是使用了gdb以及threadapply all bt 命令,下面使用pstack查看进程函数堆栈。

pstack pid


pstree命令以树状图显示进程间的关系(displaya tree of processes)。ps命令可以显示当前正在运行的那些进程的信息,但是对于它们之间的关系显示的并不清晰。在linux系统中,系统调用fork可以创建子进程,通过shell可以创建子进程,Linux系统中进程间的关系天生是一棵树,树的根就是进程PID为1的init进程。

pstree -p 以树状图显示系统进程关系

pstree -pa pid 以树状图显示进程PID为<pid>的进程以及子进程


lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息,因此通过lsof工具能够查看这个列表对系统监测以及排错很有帮助。

  • losf常用指令
lsof <filename>    显示打开某个文件的进程
lsof +D <DIR>      显示所有打开该目录的进程,注意,如果不想显示其子目录的进程,可以使用+d
lsof -u <username>   显示某个用户启动进程所打开的文件
lsof -u^<username>   显示除了某个用户外的其他所以用户的启动进程打开的文件
lsof -i [4|6][@hostname|ip][:service|port]       显示打开某ip或者端口的进程 (可以和netstat 配合使用), 46 用来区分ip 协议
lsof -i    列出所有的网络连接(注意,网络连接也是文件)
lsof -i TCP:1-1024   列出所有使用TCP 协议,端口范围是1-1024 的网络连接
  • lsof输出列表的含义
FD  Represents the file descriptor. Some of the values of FDs are

cwd  Current Working Directory
txt  Text file
mem  Memory mapped file
mmap  Memory mapped device
NUMBER  Represent the actual file descriptor. The character after the number i.e 1u, represents the mode in which the file is opened. r for read, w for write, u for read and write.

TYPE  of files and its identification.

DIR  Directory
REG  Regular file
CHR  Character special file.
FIFO  First In First Out

参照链接

shawnwang_vip

5 simple ways to troubleshoot using Strace

strace介绍

使用RabbitMQ近一年时间的总结

遇到的一些问题

  • 交换器队列绑定有问题
  • 客户端代码编写问题
  • 没有选择合适的交换器类型
  • 客户端与MQ连接阻塞
  • 网络分区的发生与恢复方案
  • 非持久化镜像队列同步异常

性能优化

  • 优化网络配置(禁用Nagle算法,增加TCP缓存区大小)
  • 客户端代码优化(事务,普通confirm,批量confirm和异步confirm)
  • publisher confirm和comsumer ack,prefetchCount数值
  • 镜像队列的使用拉低整体性能
  • Erlang Hipe compile
  • 避免触发流控机制影响性能

消息传输保障

  • 生产者publisher confirm机制确保消息可靠传输到MQ中

  • 生产者配合使用mandatory或者备份交换器确保消息能够从交换器路由到队列中,进而能够保存下来而不会被丢弃

  • 交换器、消息、队列需要进行持久化处理

  • 消费者在消费消息的同时需要将autoAck设置为false,手动确认消息已经被正确消费

RabbitMQ没有消息去重机制,需要业务方根据自身的业务特性进行去重

运维监控

集群搭建

节点故障恢复

集群监控

  • 通过HTTP API接口提供监控数据
  • 通过客户端提供监控数据
  • 元数据管理与监控

跨级群和WAN部署

使用Federation和shovel可跨越集群(网络状态良好的LAN)界限,实现WAN部署

项目中待改善的细节

  • 使用rabbitmq达到一定规模后,可根据业务功能和场景进行归类拆分,为之分配不同vhost
  • 用户是访问控制的基本单元,单用户可跨多个vhost进行授权,需合理配置用户权限
  • 使用aliveness-test程序配合AMQPPing一起全面监控RabbitMQ服务
  • 加强元数据管理与监控,提供给业务方使用的用户只有可读可写权限,不能够变更元数据信息
  • 目前MQ程序异常是单纯通过守护脚本拉起,这其中逻辑需要完善,确保在启动时最后关闭的节点是第一个启动的

  • 没有对重要业务加以区分,制作备份交换器
  • 没有监控队列中可能存在的消息积压的情况,运维部分薄弱
  • 可考虑预先分配创建元数据资源,避免人为因素,代码缺陷导致的MQ使用问题
  • 格式化使用reset操作而非删除menisa数据

消息追踪和问题定位

  • 使用firehose和tracing插件追踪消息的处理过程
  • 在开启web管理插件的前提下,登陆操作界面查看问题
  • 合理利用amq.rabbitmq.log交换器,过滤不同级别日志信息
  • 查看日志文件分析定位问题
  • 使用操作管理命令(rabbitmqctl)和HTTP API接口

2018年阅读书单

高可用架构(第1卷)

程序员代码面试指南:IT名企算法与数据结构题目最优解

Docker生产环境实践指南

RabbitMQ实战指南

从零起步:素描基础教程

素描的诀窍(15周年畅销版)

Tensorflow:实战Google深度学习框架

软件工程的事实与谬误

Robot Framework 自动化测试修炼宝典

代码之外的功夫-程序员精进之路

十亿消费者

大规模分布式

如何看懂书法

如何写书法

诸遂良阴符经倪宽赞

诸遂良雁塔圣教序

隐藏在爱的外衣下的自私

我的孩子已经八个月大了。有一天,我和他在床上玩耍,发现他对婴儿用的抽纸特别感兴趣,喜欢不停地把纸抽出来。看到这样的场景,我下意识地将抽纸从他的身边拿开。过了一会,我突然意识到,我刚才的那“不经意”的行为犯了一个错误。我赶紧将抽纸重新拿回他的跟前,看着他乐此不疲的一张张把纸抽出来玩。

事后,我重新思考了几个问题。

我为什么会下意识地将抽纸从他的身边拿开。是因为我觉得玩抽纸对他的成长没有好处吗?是因为担心他养成“破坏”的习惯吗?不是的。他把原本整洁的床弄的乱七八糟,我事后就需要为他的行为买单 – 把床重新收拾好。同时,原本可以被合理利用的东西被“浪费”了,这也足以让我心烦意乱。此时,我思考的问题角度始终放在我自身。

他能从抽纸的游戏中收获到什么。我重新将思考的角度放在孩子身上。在他的世界里,抽纸是一个有趣的事,纸可以不停地从一个小盒子里抽出来,就像变魔术一样。在这个过程中,他可以体会抽纸是一种什么样的体验,同时还能体会到探索新鲜事物所带来原始的快乐。对于孩子而言,这个游戏既有趣而有收获。

为什么我和孩子之间的意识产生了如此之大的落差呢?归根结底,是我们潜意识里总是优先从自身的角度出发处理问题,这是来自人天性中的自私。

我发现,这样的例子在生活中比比皆是。孩子在自己的小天地里爬行,我会时不时担心他会不小心把脑袋磕到坚硬的护栏,我的担心来源于,他弄疼了自己后我需要花时间抚慰他,多于心疼他所要忍受的痛苦(有时孩子在冒险过程中的磕碰对他的成长其实有利)。我们会对孩子令行禁止或者过度呵护,是因为我们害怕承担孩子在冒险过程中所产生的各种不好的后果,而忽略了冒险给孩子带来的成长。我们常常冠以爱的借口,来掩饰我们潜意识里的自私。

最近,我和一位在世界一流名校就读的女孩有过短暂的交流。在同龄人当中,算是相当优秀出众的。让我影响深刻的是她的这样一段经历。她有段时间喜欢上心理学,希望将来能就读心理学专业。周围的亲戚都认为心理学前途堪忧,劝她放弃。而她的父亲知道后,通过朋友介绍她去一家与心理学相关的机构实习,待她体验了之后,再决定是否坚持自己的选择。

孩子的选择做父母的若是有能力预见,多半会苦口婆心的劝说。因为劝说的成本很低,我们总是希望借助自己的权威,只需要费点口舌就达到我们自己的目的。克服自己天性的自私,从孩子的角度出发,努力去做一件能够帮助孩子理解这个世界的事情实属不易,这样的父亲我觉得是伟大的。

最后,都说父母是孩子的起点,如果我们能在不断翻越自己的思维壁垒的过程中成长,那么孩子们就能站在我们的肩膀上,看到更广阔的世界。


文章来自Joshua Nie

Shell脚本

一个脚本骨架

自己写了个脚本框架,实现了简单日志定位和异常捕获,以下几点可增强脚本健壮性

1) set -o nounset

当你使用未初始化的变量时让bash自动退出

2) set -o errtrace

告诉bash一旦有任何语句出错则退出

3) command ; if [ “S?” -ne 0 ]; then … fi

使用$?判断上一句执行结果

4) command || echo “command failed”

使用 ||

5) trap func ERR

使用trap捕获异常


脚本实现如下,供参考:


#!/bin/sh -
# Description: 脚本框架示例,注意外部调用时return值
# Author: jiangzhe

IFS='
    '
UMASK=002
umask $UMASK

#set -o nounset
set -o errtrace
set -o errexit

START_DIR=`pwd`
trap "traperror $LINENO ${FUNCNAME} $BASH_LINENO" ERR
trap "cd $START_DIR" EXIT

SUCCESS='[  \033[1;32mOK\033[0;39m  ]'
FAILURE='[\033[1;31mFAILED\033[0;39m]'

# 日志文件,推荐使用绝对路径
LOG_FILE_PATH=$HOME
LOG_FILE=$LOG_FILE_PATH/shell.log

# 是否打印屏幕
SHOW_CONSOLE=Y

function usage()
{
    cat <<EOF
Usage:
    脚本使用说明:
        -d | --debug    脚本调试
        -v | --version  获取版本信息
        -h | -- help    获取帮助
    执行:
        sh skeleton.sh start
    
EOF
}

function version()
{
    echo "Skeleton 1.0.0"
    echo ""
    echo "Copyright (C) 2017, Jiangzhe."
    echo "All rights Reserved."
    echo ""
}

function log_info()
{
    if [ $# -lt 1 ]; then
        return
    fi
    echo "$(date +"%F %T") [INFO] $*" >> $LOG_FILE
    [ $SHOW_CONSOLE == Y ] && echo "$(date +"%F %T") [INFO] $*"
}

function traperror()
{
    local err=$?
    local line=$1
    [ "$2" != "" ] && local funcstack=$2
    [ "$3" != "" ] && local linecallfunc=$3
    log_info "<-----"
    log_info "Error: line $line - command exit with status: $err"
    if [ "funcstack" != "" ];then
        log_info "   ... Error at function ${funcstack[0]}() "
        if [ "$linecallfunc" != "" ];then
            log_info "called at line $3"
        fi
        echo ""
    fi
    log_info "----->"
}

function do_kill()
{
    ps -ef| grep $@ |awk '{print $2}'| xargs kill -KILL
}

function do_start()
{
    #ulimit -c unlimited -n 81920 -s 1024
    #./test &
    # TODO
    log_info "trying to starting test ..."
    echo -e "starting test:        $SUCCESS"
}

function do_stop()
{
    #do_kill test
    # TODO
    log_info "trying to stopping test ..."
    echo -e "stopping test:        $SUCCESS"
}

function parseArgs()
{
    while [ $# -gt 0 ]
    do
        case "$1" in
        -d | -debug | --d | --debug )
            set -x
            ;;
        -h | -help | --h | --help )
            usage
            ;;
        -v | -version | --v | --version )
            version
            ;;
        start)
            do_start
            ;;
        stop)
            do_stop
            ;;
        restart)
            do_stop
            sleep 2
            do_start
            ;;
        status)
            ;;
        esac
        
        shift
    done
}

#### MAIN ####

echo `date +"[%Y-%m-%d %H:%M]"`: $0 $@

if [ $# -eq 0 ]; then
    usage
    exit 255
fi

parseArgs $*

exit 0