月度归档:2013年11月

论后台平滑升级

业务发展,更新升级必不可少,升级的影响可大可小,不同行业影响的评判也不甚相同,金融行业做某些升级会直接让服务不可用,因为在某些环节他不允许失败,要么提供服务,要么成功。互联网行业随意得多,升级过程中页面刷不开没关系,只要再刷几下成功就行了。这个话题有点大,不过google了下发现相关信息太少,权当抛砖引玉,梳理下偶工作中亲身经历的或感同身受的。

【升级通常有两个目标】
1. 在升级开始到升级结束的这段时间内,老版本依然正常使用。
a) 首先这个目标应该有选择的看
假设整个系统是个黑盒,在输入输出均不变这种情况下的升级,该目标实际上没必要考虑。这种常见于相对独立的小版本或小改动,例如前台改个页面,后台改个逻辑。
b) 其次这个过程很容易被无线缩小
给人的感觉是,整个模块发布完成时间跨度很短。比如两个独立的模块同时发布,准备好之后几十秒就可以完成发布。当然这是理想情况,假如发布不成功咋办,这几十秒就被无限延长了。所以实际上影响的时间是不可控的。
2. 升级结束后,老版本中的数据依然有效。
这也是容易忽略的一个问题,经常会发现有时候各个模块都已经切换过来了,但是新版本却没有老版本中的数据。

【处理方法】
1. 通道分离技术
核心方法就是将新老通道分离,俗称做版本,但表现形式就多种多样了。
a) 内部区分实现通道分离
这种方法适合于接口有变动但逻辑变动不大的情况,具体实现例如在请求中预埋版本标识等等,后台只要能够识别当前版本就能屏蔽和兼容版本了。
好处:调用方不用变更后端位置,后台自身仅仅维护一个独立的实体。
坏处:后台自身必须融合兼容逻辑,当变更较大时,代码将变得繁琐冗余。
b) 外部重建实现通道分离
顾名思义就是新建一个后端给新版本服务,新版本请求到新的后端,老版本还是请求到老后端。
好处:版本间独立性非常好,实现起来思路清晰。
坏处:通常实现成本较大,且调用方需要变更后端地址,当共存版本较多时,后端将会存在很多套。

2. 入口切换配合存量切换
在一个无状态的服务中,不存在这种情况,因为通常只提供通道和方法,不提供数据。虽然大家都喜欢无状态的服务,但是,对于有数据存取的业务,通常在整个服务链的最底层都是有状态的。

【遇到的例子】
1. 产品排序逻辑变更导致需要后台调整搜索排序
直接迅速改完,发布,有问题就回滚,没问题就全量,这应该是最朴实升级场景。
2. agent数据结构变更,导致需要修改共享内存的使用逻辑
这个东东,曾经让我爽了一阵。因为共享内存有两个常见的限制:
a) 有其他进程挂上去时,无法真实的删除。
b) 可以将原共享内存减小,但不能增大。
然后我升级恰恰必须要突破这两个限制。因为其他业务已经在使用我agent维护的共享内存,且由于业务发展我需要调大共享内存。最后,我新建了一片共享内存,重新发布部署agent,(此时老的业务仍在使用老的共享内存没问题),直到他们切换到新的agent地址上,然后统一iprm老的共享内存。
3. 产品业务调整,修正管理|审核后台的逻辑
管理后台通常跟数据打交道,多多少少都有点状态。由于老的管理后台没有处理经纬度信息,新的需要处理这个数据,ok,遵照封入口导存量的原则,先发布新的管理后台(有了这个逻辑),再将存量数据处理一遍,done。
4. nginx的升级
平滑升级在软件行业非常常见。不仅仅局限于我自己所处的业务后台,开源软件本身也一样,nginx是个好软件,所以也应该是个好例子。除了nginx是靠信号来做状态切换,本质上也是全新起一套通道,来运行新的版本,升级完成后再销毁老版本的一个过程。

glog的C语言风格日志

c++的流输入输出用久了毛病就出来了,参数多的时候打印代码写起来就忒别扭,不能一气呵成的编排好格式。

1. 然后就对c++日志接口做了一层简单的封装

2. 再然后我发现自己搞错了, glog原生就有自己的C风格日志输出,只不过放在raw_logging.h里头。

core与不core你决定

常在河边走哪能不湿鞋,进程crash是常有的事儿,不过工作中与coredump的交集非常固定,我大致将其分为两种:一种是core得太普通,另一种是core得没证据。前者出现的场景通常是“你咋整的,程序怎么又core了”,泛指core得莫名其妙;后者出现的场景大致是“你咋搞的,居然不知道core在哪哈儿“,泛指core得没凭没据。下面来看看如何core得高大上。

1. coredump的出现必须满足3个条件
a) 产生信号
linux下部分信号默认是即终止程序又产生core(也有只终止程序不产生coredump的),SIGSEGV,SIGILL,SIGFPE,SIGABRT,SIGBUS,SIGTERM这几个是常使用的(还有几个也会产生core,硬件信号等等,没见出现过忽略掉),kill -l可查看。
b) 进程环境允许core
这个最容易遗忘,进程执行环境需要保证corefile的设置值有足够大小,该值默认为0,通常设为unlimited。
c) 拥有写core文件的权限且磁盘有空间
这个通常都满足,不赘述。

2. coredump分为主动或被动
a) 被动core掉 – 程序自己执行到非法操作时,会产生信号,触发信号默认行为。
b) 主动core掉 – 自己发送信号触发这个逻辑或者利用gcore强制生成core。特殊场景会需要(如果仅仅只是查看当前堆栈信息,pstack和gdb的attach + bt可以满足),例如做测试时发送SIGQUIT给进程,gdb的generate-core-file指令(gdb –batch –pid=$1 -ex generate-core-file),gcore [pid] -o filename这种方式不需要重启server。

3.生产环境中如何强制保留现场
其实有木有core文件倒还其次,关键是有现场信息,知道程序哪个位置出错了。
a)  如果你跟我一样用glog,那这个就好办了。
google::InstallFailureSignalHandler() 接管会产生core文件的信号函数,然后用InstallFailureWriter(&func)自定义core时的堆栈信息处理,默认这些信息是打印到终端,而通常服务端程序都是以deamon形式运行,已经关闭了终端输出。所以同城将func定义将堆栈信息打印到日志文件。形如

b) 如果你还是一个不满足于glog堆栈信息的人,你或许还需要这个
为什么这么说,因为glog给出的堆栈信息只有地址信息木有行号,所以需要做些改变。
backtracebacktrace_symbols可以获取到堆栈信息,然后需要依靠libbfd或者libdwarf去转换进程的地址信息到函数和行号,后者真心复杂,遂放弃。反正程序都要core了也不在乎退出时的性能问题了,于是配合glog的能力用sscanf匹配出栈帧中的地址信息,然后用addr2line去转换。

4. 如果你无论如何都要生成core文件
当然生成core文件这种机制也是有缺点的,因为占空间且不实时。但是人们熟悉这玩意儿,并且也简单。无奈只有祭出神器 – google-coredumper。这么给你说吧,你想啥时候写core文件,他就啥时候写,不是必须要设置ulimit选项,不是必须要信号触发,当然你还是要有磁盘空间的(需要注意googlecode上的最新版本有部分编译错误需要自己修改下)。常见的场景,例如我要在程序自发core信号产生时强制生成core文件,只需绑定以下信号函数即可:

win7 x64下的GAE(python)安装问题

欣喜的下了GAE 1.8.8和python 2.7.6,win7 x86下跑得飞起,win7 x64下一直报各种编码错误,找了半天终于发现原来是python的bug,按照url提示的补丁文件(windows下没有diff工具)手动修正后成功运行demo.

http://www.cnblogs.com/cloud2rain/p/3427194.html
http://www.crifan.com/python_syntax_error_indentationerror/

linux终端字符画工具

曾经在某运维的电脑上看到用可打印字符组成的终端字符画,觉得很潮。试用了几个比较实用的,吐血推荐!!!

1. banner (https://github.com/uffejakobsen/sysvbanner)
这个是最简单的,就一种样式,用#字符来画出你输入的字符组合。
banner [text]
banner

 

 

 

2.figlet (http://www.figlet.org/)
figlet也提供了n中样式,不过是靠字体文件来区分样式的,默认是标准字体
figlet -f [font name] [text]
figlet

 

 

 

 

3. boxes (http://boxes.thomasjensen.com/)
提供了n种样式,例如各种动物等,然后你输入的字符就放在这些图案的内部空白处
boxes -l 列出所有的样式
echo [text] | boxes -d [style name] 用dog这个样式来显示
boxes

[参考链接]
http://blog.csdn.net/eastcoke/article/details/6427158
http://mewbies.com/acute_terminal_fun_01_get_ascii-fied_on_the_terminal.htm
http://my.oschina.net/leejun2005/blog/81869

 

把C代码当shell脚本来执行

C这种编译型语言要运行必然是需要编译的,但是做点手脚亦可从表明上改变这种认知。下面给出两种把C代码当shell脚本来执行的案例。

1. tcc的脚本模式
tcc是C的编译器,脚本模式会直接编译该代码并运行。

2. shell与C代码共存
脚本语言是解释执行的,充分利用#if条件语句来隔离shell和c编译器,达到可以直接运行的效果。