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

使用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

面向对象设计

面向对象的三个基本元素

  1. 封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。

  2. 继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

  3. 多态: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。

C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。

面向对象的5个基本设计原则

  • 单一职责原则(Single-Resposibility Principle)

    其核心思想为:一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。职责过多,可能引起它变化的原因就越多,这将导致职责依赖,相互之间就产生影响,从而大大损伤其内聚性和耦合度。通常意义下的单一职责,就是指只有一种单一功能,不要为类实现过多的功能点,以保证实体只有一个引起它变化的原因。

    专注,是一个人优良的品质;同样的,单一也是一个类的优良设计。交杂不清的职责将使得代码看起来特别别扭牵一发而动全身,有失美感和必然导致丑陋的系统错误风险。

  • 开放封闭原则(Open-Closed principle)

    其核心思想是:软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。开放封闭原则主要体现在两个方面1、对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。2、对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对其进行任何尝试的修改。

    实现开开放封闭原则的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以修改就是封闭的;而通过面向对象的继承和多态机制,又可以实现对抽象类的继承,通过覆写其方法来改变固有行为,实现新的拓展方法,所以就是开放的。

    “需求总是变化”没有不变的软件,所以就需要用封闭开放原则来封闭变化满足需求,同时还能保持软件内部的封装体系稳定,不被需求的变化影响。