分类目录归档:未分类

googleapis被墙,更换了一下博客主题

本来一直使用wordpress默认的主题,风格也挺好的,简洁、干净;但最近页面打开的总是很慢,在排除了服务器端的问题之后,通过浏览器定位发现每次都是因为某个css里要访问googleapis下的内容,而googleapis被墙掉了,导致资源加载非常慢。

要么修改默认主题里的css不要访问font.googleapis,要么换一个不会访问googleapis的主题。尝试了几个主题,现在用的这个还凑合,就是展示代码的效果不太好。先用着吧,有好的主题欢迎推荐给我。

李昌钰博士的演讲

下午在公司的报告厅,听了李昌钰博士的《我的鑑識生涯—世际名案与學思歷程》,以前从未了解过这个行业,李博士的演讲也蛮有趣的,还记得几点:

1) 他提到昨天到广州,进酒店时已经晚上11点,准备今天的ppt和演讲到晚上2点,然后又坚持写书(每天写一页),虽然他这一页写的少了些只有几行,但让自己心安。

2) 证人的信息有40%是不可靠的。

3) 关于陈水扁枪击案,子弹水平划过肚皮,很多人觉得像是刀伤,但是有灼烧痕迹的。这是通过一把玩具枪改造的手枪,做枪的人把里面的枪栓换掉,这种枪杀伤力不大,但在短距离内还是有一些威胁的。使用自制子弹。打中吕秀莲腿部的是铜子弹,打中陈水扁的是铅子弹。嫌疑犯后来落水死亡,这个案子仍未结案。

4) 他的父亲是1949年乘坐太平轮从上海去往台湾时遇难的

家境原本还算富裕,父亲遇难后变得比较困难,所以他后来读书时选择读免费的警官学校。可能也是这个原因使他一直很尊敬和听从他妈妈的话,他演讲过程中提到过好几次别人邀请他做某个位置的职务时,他起先并不愿意,后来对方通过他的妈妈说服他。

2013年的环台湾(6): 在高雄遇到的两位前辈

两位前辈前辈都已55岁,聊起来他们曾在1974年和1980年的时候骑单车环过两次岛。

这张照片是后来email的,是他们在1980年环岛时于枋寮车站照的,照片从左第一位和第三位是这次路上遇到的。

从台南到高雄的这端途中,跟随两位前辈同行,其中一位偏外向,一位偏内向。路上大多时间在和这位外向的前辈聊天,聊了很多有关宗教、政治、旅行、互联网等方面的话题。在途中,他刻意选择一个要经过的教堂去参观了一下。

在台湾的一路骑行中,发现即使再小的村庄,也会有菩萨、妈祖的庙,异或是基督、天主教堂,甚至清真寺,信仰的印记在这里随处可见。

到达高雄的时候,天色已黑,前辈请我去了一家,不在闹市区,普通人还有点难找的“酸菜火锅”饭馆。我非常奇怪,台湾南部怎么会有这种北方家常菜,后来也是从龙应台的《大江大海1949》这本书里了解到当年有相当多的山东籍国军从青岛乘船到高雄。这家饭馆所在的地方,他们称为“眷村”,也就是军队家属们所住的地方。

我们要了一份火锅,饺子,以及几个菜。酸菜和饺子口味很地道,并没有”本地化”,不知道饺子用的面是不是从大陆运过来的。

两位前辈虽然年级较大,但对新事物的接受能力很强,我们也聊了彼此的工作,对于互联网,他们是facebook的重度用户,也了解大陆大多人在用微信、微博等产品。看到我用的小米手机,他们说小米手机在台湾也非常受欢迎。我跟他们提到内地一些互联网产品,以及几家大公司的情况。也从他们那儿了解了一点台湾高铁的建造过程。

那天晚上到10点左右,他们送我到市区中心旅馆较多的地方。我们交换了email,后来我回来后把我们在教堂的合影发给了他,他也发给我他们年轻时的这张照片。

tomcat进程意外退出的问题分析

节前某个部门的测试环境反馈tomcat会意外退出,我们到实际环境排查后发现不是jvm crash,日志里有进程销毁的记录,从pause到destory的整个过程:

org.apache.coyote.AbstractProtocol pause
Pausing ProtocolHandler
org.apache.catalina.core.StandardService stopInternal
Stopping service Catalina
org.apache.coyote.AbstractProtocol stop
Stopping ProtocolHandler
org.apache.coyote.AbstractProtocol destroy
Destroying ProtocolHandler

从上面日志来可以判断:

1) tomcat不是通过脚本正常关闭(viaport: 即通过8005端口发送shutdown指令)

因为正常关闭(viaport)的话会在 pause 之前有这样的一句warn日志:

    org.apache.catalina.core.StandardServer await
    A valid shutdown command was received via the shutdown port. Stopping the Server instance.
    然后才是 pause -> stop -> destroy 
2) tomcat的shutdownhook被触发,执行了销毁逻辑

而这又有两种情况,一是应用代码里有地方用System.exit来退出jvm,二是系统发的信号(kill -9除外,SIGKILL信号JVM不会有机会执行shutdownhook)

先通过排查代码,应用方和中间件团队都排查了System.exit在这个应用中使用的可能。那就只剩下Signal的情况了;经过一番排查后,发现每次tomcat意外退出的时间与ssh会话结束的时间正好吻合。

有了这个线索之后,银时同学立刻看了一下对方测试环境的脚本,简化后如下:

$ cat test.sh
#!/bin/bash
cd /data/server/tomcat/bin/
./catalina.sh start
tail -f /data/server/tomcat/logs/catalina.out

tomcat启动为后,当前shell进程并没有退出,而是挂住在tail进程,往终端输出日志内容。这种情况下,如果用户直接关闭ssh终端的窗口(用鼠标或快捷键),则java进程也会退出。而如果先ctrl-c终止test.sh进程,然后再关闭ssh终端的话,则java进程不会退出。

这是一个有趣的现象,catalina.sh start方式启动的tomcat会把java进程挂到init(进程id为1)的父进程下,已经与当前test.sh进程脱离了父子关系,也与ssh进程没有关系,为什么关闭ssh终端窗口会导致java进程退出?

我们的推测是ssh窗口在关闭时,对当前交互的shell以及正在运行的test.sh等子进程发送某个退出的Signal,找了一台装有systemtap的机器来验证,所用的stap脚本是从涧泉同学那里copy的:

function time_str: string () {
    return ctime(gettimeofday_s() + 8 * 60 * 60);
}

probe begin {
    printdln(" ", time_str(), "BEGIN");
}

probe end {
    printdln(" ", time_str(), "END");
}

probe signal.send {
    if (sig_name == "SIGHUP" || sig_name == "SIGQUIT" || 
        sig_name=="SIGINT" || sig_name=="SIGKILL" || sig_name=="SIGABRT") {
        printd(" ", time_str(), sig_name, "[", uid(), pid(), cmdline_str(), 
                "] -> [", task_uid(task), sig_pid, pid_name, "], ");
        task = pid2task(pid());
        while (task_pid(task) > 0) {
            printd(" ", "[", task_uid(task), task_pid(task), task_execname(task), "]");
            task = task_parent(task);
        }
        println("");
    }
}

模拟时的进程层级(pstree)大致如下,tomcat启动后java进程已经脱离test.sh,挂在init下:

|-sshd(1622)-+-sshd(11681)---sshd(11699)---bash(11700)---test.sh(13285)---tail(13299)

经过内核组伯俞的协助,我们发现

a) 用 ctrl-c 终止当前test.sh进程时,系统events进程向 java 和 tail 两个进程发送了SIGINT 信号
SIGINT [ 0 11  ] -> [ 0 20629 tail ] 
SIGINT [ 0 11  ] -> [ 0 20628 java ] 
SIGINT [ 0 11  ] -> [ 0 20615 test.sh ] 

注pid 11是events进程
b) 关闭ssh终端窗口时,sshd向下游进程发送SIGHUP, 为何java进程也会收到?
SIGHUP [ 0 11681 sshd: hongjiang.wanghj [priv] ] -> [ 57316 11700 bash ] 
SIGHUP [ 57316 11700 -bash ] -> [ 57316 11700 bash ]
SIGHUP [ 57316 11700 ] -> [ 0 13299 tail ] 
SIGHUP [ 57316 11700 ] -> [ 0 13298 java ] 
SIGHUP [ 57316 11700 ] -> [ 0 13285 test.sh ] 

不过伯俞很忙没有继续协助分析这个问题(他给出了一些猜测,但后来证明并不是那样)。

确定了是由signal引起的之后,我的疑惑变成了:

1) 为什么SIGINT (kill -2) 不会让tomcat进程退出?
2) 为什么SIGHUP (kill -1) 会让tomcat进程退出?

我第一反应可能是jvm在某些参数下(或因为某些jni)对os的信号处理会不同,看了一下应用的jvm参数,没有看出问题,也排除了tomcat使用apr/tcnative的情况。

我们看一下默认情况下,jvm进程对SIGINTSIGHUP是怎么处理的,用scala的repl模拟一下:

scala> Runtime.getRuntime().addShutdownHook(
            new Thread() { override def run() { println("ok") } })

对这个java进程分别用kill -2kill -1发现都会导致jvm进程退出,并且也触发shutdownhook。这也符合oracle对hotspot虚拟机处理Signal的说明,参考这里SIGTERM,SIGINT,SIGHUP三种信号都会触发shutdownhook

看来并不是jvm的事,继续猜测是否与进程的状态有关?catalina.sh脚本里并没有使用start-stop-daemon之类的方式启动java进程,start参数的执行方式简化后脚本相当于:

eval '"/pathofjdk/bin/java"' 'params' org.apache.catalina.startup.Bootstrap start '&'

就是简单的把java放到后台执行。当catalina.sh自身进程退出后,java进程的ppid变成了1

花了很多的时间猜测可能是OS层面的原因,后来发现并没有关系。春节后回来让少明和涧泉也一起分析这个问题,因为他们有c的背景,对系统底层知道的多一些,用了大半天时间,不断猜测和验证,最后确认了是Shell的原因。

SIGINT (kill -2) 不会让后台java进程退出的原因

为了简便,我们用sleep来模拟进程,当我们在交互模式下:

$ sleep 1000 & 

$ ps -opid,pgid,ppid,stat,cmd -C sleep
  PID  PGID  PPID STAT CMD
 9897  9897  9813 S    sleep 1000   

注意,进程sleep 1000的pid与pgid(进程组)是相同的,这时我们用kill -2是可以杀掉sleep 1000进程的。

现在我们把sleep进程放到一个脚本里后台执行:

$ cat a.sh
#!/bin/sh
sleep 4400 &
echo "shell exit"

运行a.sh脚本之后,sleep 4400进程的pid与pgid是不同的,pgid是其父进程的id,即已经退出了的a.sh进程

$ ps -opid,pgid,ppid,comm -p 63376
  PID  PGID  PPID COMM
63376 63375     1 sleep

这时我们用kill -2是杀不掉sleep 4400进程的。

到了这一步,已经非常接近原因了,一定是shell对后台进程signal_handler做了什么手脚。少明实现了一个自定handler的命令看看是否对kill -2有效:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void my_handler(int sig) {
    printf("handler aaa\n");
    exit(0);
}

int main() {
    signal(SIGINT, my_handler);
    for(;;) { }
    return 0;
}

我们把编译后的a.out命令在脚本里以后台方式运行:

$ cat a.sh
#!/bin/sh
/tmp/a.out &

这次再尝试用kill -2去杀a.out进程,是可以的。这说明shell对signal_handler做手脚是在执行用户逻辑之前,也就是脚本在fork出子进程的时候就设置了。按照这个线索我们google后了解到: shell在非交互模式下对后台进程处理SIGINT信号时设置的是IGNORE

交互模式与非交互模式对作业控制(job control)默认方式不同

为什么在交互模式下shell不会对后台进程处理SIGINT信号设置为忽略,而非交互模式下会设置为忽略呢?还是比较好理解的,举例来说,我们先某个前台进程运行时间太长,可以ctrl-z中止一下,然后通过bg %n把这个进程放入后台,同样也可以把一个cmd &方式启动的后台进程,通过fg %n放回前台,然后在ctrl-c停止它,当然不能忽略SIGINT

为何交互模式下的后台进程会设置一个自己的进程组ID呢?因为默认如果采用父进程的进程组ID,父进程会把收到的键盘事件比如ctrl-c之类的SIGINT传播给进程组中的每个成员,假设后台进程也是父进程组的成员,因为作业控制的需要不能忽略SIGINT,你在终端随意ctrl-c就可能导致所有的后台进程退出,显然这样是不合理的;所以为了避免这种干扰后台进程设置为自己的pgid。

而非交互模式下,通常是不需要作业控制的,所以作业控制在非交互模式下默认也是关闭的(当然也可以在脚本里通过选项set -m打开作业控制选项)。不开启作业控制的话,脚本里的后台进程可以通过设置忽略SIGINT信号来避免父进程对组中成员的传播,因为对它来说这个信号已经没有意义。

回到tomcat的例子,catalina.sh脚本通过start参数启动的时候,就是以非交互方式后台启动,java进程也被shell设置了忽略SIGINT信号,因此在ctrl-c结束test.sh进程时,系统发送的SIGINT对java没有影响。

SIGHUP (kill -1) 让tomcat进程退出的原因

在非交互模式下,shell对java进程设置了SIGINTSIGQUIT信号设置了忽略,但并没有对SIGHUP信号设为忽略。再看一下当时的进程层级:

|-sshd(1622)-+-sshd(11681)---sshd(11699)---bash(11700)---test.sh(13285)---tail(13299)

sshd把SIGHUP传递给bash进程后,bash会把SIGHUP传递给它的子进程,并且对于其子进程test.sh,bash还会对test.sh的进程组里的成员都传播一遍SIGHUP。因为java后台进程从父进程catalina.sh(又是从其父进程test.sh)继承的pgid,所以java进程仍属于test.sh进程组里的成员,收到SIGHUP后退出。

如果我们在test.sh里设置开启作业控制的话,就不会让java进程退出了

#!/bin/bash
set -m  
cd /home/admin/tt/tomcat/bin/
./catalina.sh start
tail -f /home/admin/tt/tomcat/logs/catalina.out

此时java后台进程继承父进程catalina.sh的pgid,而catalina.sh不再使用test.sh的进程组,而是自己的pid作为pgid,catalina.sh进程在执行完退出后,java进程挂到了init下,java与test.sh进程就完全脱离关系了,bash也不会再向它发送信号。

firefox27下vimperator遇到的问题

几天前Firefox升级到27之后,vimperator插件在搜索的时候会报错误:“Type Error: config.browser.getFindBar is not a function”。google之后看到已经有人在google code 给 vimperator-labs提了这个问题,有人已经提交了补丁,当时想着等官方插件升级后再更新。

但这几天vimperator的版本一直没有更新,今天再去看,主干里已经修复,只是官网还没有发布二进制更新。于是按照步骤把源码自己编译一下:

// 如果没有hg的话先通过homebrew安装一下
$ hg clone https://vimperator-labs.googlecode.com/hg/ vimperator-labs

$ cd vimperator-labs/vimperator

// 注意在mac下 sed 和 cp需要设定为gnu的gsed和通过homebrew安装的coreutils下的gcp
$ make -e SED=gsed CP=gcp  xpi

生成的 xpi 位于 vimperator-labs/downloads 下,用firefox重新打开一下即可。

注意恶意攻击

博客最近的访问记录里总能看到一些攻击者,想要访问wp的登录页;因为我的登陆页设置了不太好猜的参数,参数错误的话会被跳转到google,所以起了一道保护。这个经验也是我之前因为被攻击而学会的,曾遇到过一次ip显示来自东欧地区的攻击,大量请求在login页面,视图猜测我的密码,导致cpu被占满,博客无法访问。

最近又发现一些恶意攻击,视图猜测我设置的参数,类似下面的:

   13:45:20 ->/wp-login.php?action=register
   13:45:22 ->/wp-login.php?registration=disabled 

或许是年底了,cracker们也忙碌起来了?可这个技术博客有啥好图的呢。

wordpress的 wp-login.php 页面被频繁攻击

某晚上收到阿里云vps短信报警,博客http服务不正常,第一反应以为还是mysql的问题,上次已经解决过,难道没效果?

结果上去看,mysql,nginx,php等进程全都存在,页面也能访问,不过特别慢。以为是哪里发生了阻塞,先尝试重启了mysql,没用还是慢。然后重启nginx,一样,再重启php5-fpm 还是慢。这就怪了,用top看主要是几个php-fpm进程把cpu都占满了。

去看日志,一下子明白了,有大量的请求过来,都是在访问 wp-login.php,原来是黑客又在猜我的密码,这些ip大多是来自东欧的。

网上看了一下解决办法,有2种

1)修改 wp-login.php 在请求后边设置自定义参数,参数不正确重定向到其他页面或网站。
2)限制访问 wp-login.php 的 ip地址。

我采用了第一种方案。

flickr is back

flickr的改版后,让这个有些暮气的产品有体现了一些活力,起码1T的空间是个非常有力的竞争筹码。我在来往的时候也很想把相册做成一个有竞争力的产品,推出类似无上限的相册产品。

大概是06,07年的时候,还在方正做传统软件方面的开发,那个时候web2.0正兴起,以flickr, last.fm, 以及豆瓣等为代表的网站引起了我的强烈兴趣,原来互联网不只是门户,可以专注、有趣。是它们让我后来转入了互联网领域。

贴一下我的flickr地址:http://www.flickr.com/photos/whj/ 很久没有在上面更新过了,我用过的几个相册产品里flickr是最老的一个,也是最有感情的一个产品。

相关阅读:抓取flickr上有趣图片的脚本,注意改版后的内容有可能需要修改,我没有验证。