博文

目前显示的是 九月, 2012的博文

用msmtp代替系统自身的sendmail

sendmail是一个漏洞奇多、配置超级麻烦的东西,所以很多系统管理员都把它禁用了。但是如此一来,如果crontab脚本执行出错,就只有天知地知了。sendmail有很多轻量级的替代,我之前一直在用ssmtp,但是这东西已经停止维护了,我在google 搜它的源代码都搜不到。于是我就只好找其它的替代,于是就找到了msmtp:http://msmtp.sourceforge.net/。安装:下载解压之后,./configure --prefix=/usr --libdir=/usr/lib64 --sysconfdir=/etcmakemake install即可。配置如果你像我这样,编译的时候加上了--sysconfdir=/etc,那么全局的配置文件就在/etc下面,名为msmtprc。这是我的配置:文件: /etc/msmtprcdefaults tls off logfile /var/log/msmtp.log account default host mail.tudou.com from xxxx@tudou.com domain tudou.com auth login user sunchangming password xxxx 很多SMTP服务器现在支持TLS加密。如果要启用TLS,那么把tls off改成tls on tls_trust_file /etc/ssl/certs/ca-bundle.crt 让mail命令使用msmtp而不是sendmail这个很简单,在/etc/mail.rc的末尾加一行set sendmail="/usr/bin/msmtp"让crontab用msmtp打开/etc/sysconfig/crond,设置一下CRONDARGS的值CRONDARGS="-m '/usr/bin/msmtp -t'"请注意这里一定要加-t。否则msmtp是从command line arg而不是stdin读取收件人列表。我之前就是因为没有加这个,而导致cron老是报告Sep 24 18:06:01 a02 crond[8023]: (app_admin) MAIL (mailed 52 bytes of output but got status 0x0040 )独家秘方哦…

一次数学式的抓虫经历

我们这有一个http service,每次应答的时候,它会生一个长度为8的随机字符串发给client。我们假设这个字符串是不会重复的,所以我们把它作为request id来唯一标识一个http request。但是后来做日志分析的时候,我们经常发现这个字符串重复了。 然后我去翻代码,发现有段代码有问题:staticchar[] alpha = ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXZY" +"0123456789abcdefghijklmnopqrstuvwxzy").toCharArray(); static String getRandStr(int length){ char[] ret = newchar[length]; for (int i = 0; i != length; ++i) { int pos = ThreadLocalRandom.current().nextInt(alpha.length); ret[i] = alpha[pos]; } returnnew String(ret); } 我第一眼就发现,0123456789被写了两遍。但是问题是,这究竟是BUG的真正原因吗? 我们这个页面每天要处理200万次请求,如果是从0-9、a-z、A-Z这62个字符随机,那么有效的取值范围是\( 62^8 \),约等于200万亿,根据我之前blog中介绍的公式 \(p = 1- e ^ {-\frac{m(m-1)}{2n}} \) 那么可计算出产生冲突的概率(p)小于百分之一。 但是上面的代码写错了,导致0-9的出现概率增大了一倍,为0.28。我不知道这种情况下该怎么算冲突概率,于是我就想用信息熵的方式模拟,len = 10 *.2777777778 + 52*(1-.2777777778)= 40.3。然后用40.3代替上面的62,计算出来冲突概率是25%。然后我写代码模拟了一下,publicstaticvoidmain(String[] args){ java.util.HashSet results = new java.util.HashSet(); int badcount = 0; for (…

2012-09-12

昨天下午突然被通知,周四搬到村里去上班。说实话,我真的很沮丧。一是某公司有强制加班(不是因为工作没完成)又不付加班费的习惯,二是上班大大的远了,且不说现在的房子何时到期,总之在中关村附近想找个交通方便又住的舒服的地方,很难。最近的工作重心依然放在kafka和hadoop上。为了让调试代码方便,我自己改了改kafka的SimpleConsumer的代码,让它支持socks代理。但是SimpleConsumer是针对单个broker的单个consumer的。如何从zookeeper中获取topic的partition信息,我还不甚清楚。我之前以为如果有两个broker,那么每个topic会自动分摊到这两个broker上,现在看来似乎不是的。(update: 这是kafka的bug https://issues.apache.org/jira/browse/KAFKA-278)elephant-bird 让我耗了很久时间。我本来是写了一个程序,利用elephant-bird的ProtobufBlockWriter写入hadoop,然后拿com.twitter.elephantbird.pig.load.ProtobufPigLoader 载入,然后再在pig中dump出来。但是执行dump的时候,每次都是在准备阶段就失败了。map/reduce的task count都是0,最终的输出也是空。而且,我看不到任何错误日志。愤怒!最后我看了看代码,ProtobufPigLoader是从LzoBaseLoadFunc继承的,于是我感觉似乎是要求这个file必须用lzo压缩。好吧,开始搞lzo的东西,可惜了,hadoop-gpl-compression 在windows下似乎是编译不过去的。liblzo2可以。由于压缩效果还是挺明显的,所以我觉得不压缩是不道德的,于是就乖乖的去折腾hadoop-gpl-compression 了。这东西在CentOS的官方的yum源里面就有,如果自己编译的话,hadoop-gpl-compression 要求liblzo2非得编译成动态库,不能是静态库。另外这个库还必须放在LIB_LIBRARY_PATH中,光用java.library.path指定还不行。于是我乖乖的去求运维人员了,我没root权限。随着把lzo搞定,在pig中dump终于成功…

2012-09-07

半个月前,我准备接手一件事情:现在某项目有一套类似于google analytics的东西,前端js就不说了,它会发起一个http请求给nginx,然后统计人员用python脚本去分析nginx的access log。整个系统用一台机器就搞定了。然后我打算把它做的更好一点,用自己写的一个http server代替nginx,然后它会主动吐日志给一个message queue,这个message queue是分布式的,另一端接hadoop,用map-reduce并行的从queue载入数据。现在这个http server刚刚写完部署上去了,各种问题。首先,我的程序没有root权限,所以在8080端口监听的。我让OP把80端口转过来,于是他就给装了一个nginx。我说iptables不行吗?他说这样不好。 我估计是因为这个nginx有什么监控和过滤功能。结果就是,我从socket拿不到client ip了。不仅如此,http header中也没找到x-forward-for之类的字段。各种扯皮啊……来回查。折腾了1天多了,下周继续去看。有些事情看着明明是一个非常简单的小事,但是实际做的时候,工期远远超过预期。唉…… 今天刚实现了对比“If-Modified-Since”和文件的最后修改时间而在适当的条件下返回304的功能。我现在只能处理GET请求,而且只能返回304或者200、404。剩下主要就是在hadoop中怎么存、怎么写统计脚本的问题。我准备学下pig,恰好发现twitter做了一些关于protocol buffers on hadoop的工作https://github.com/kevinweil/elephant-bird 我觉得对我会很有用。linkedin kafka的官方文档太少了,我自己写了一些,准备下个月做成pdf放出来。

通过代理访问hadoop

我有一套hadoop集群,部署在外网的服务器上。很遗憾的是,这些机器不能从办公区的网络直接访问。必须先ssh到一个跳板机,然后再ssh过去。之前的我做法是,部署两套,内网一套,公网一套。但是这样对我来说,维护成本无疑很大。因为这些hadoop就我一个人用啊!好在hadoop支持socks。只需要在core-site.xml中加上<property><name>hadoop.socks.server</name><value>127.0.0.1:5800</value></property><property><name>hadoop.rpc.socket.factory.class.default</name><value>org.apache.hadoop.net.SocksSocketFactory</value></property>就可以了。这是因为hadoop用的是java.io的Socket接口,而不是java.nio。而java.io是利用socket factory来创建socket,允许别人通过创建新的factory来支持socks代理、http代理等等。并且JDK内置了socks代理的支持。我觉得这种做法很值得参考。剩下的事情就交给ssh -D好了.

令人郁闷的maven

今天我用maven编译一个程序的时候,死活过不去。老说jms的jar包下载不下来。[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.407s
[INFO] Finished at: Wed Sep 05 17:38:58 CST 2012
[INFO] Final Memory: 3M/15M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project logconsumer: Could not resolve depende
ncies for project com.sunchangming:logconsumer:jar:0.0.1-SNAPSHOT: The following
artifacts could not be resolved: javax.jms:jms:jar:1.1, com.sun.jdmk:jmxtools:j
ar:1.2.1, com.sun.jmx:jmxri:jar:1.2.1: Could not transfer artifact javax.jms:jms
:jar:1.1 from/to java.net (https://maven-repository.dev.java.net/nonav/repositor
y): No connector available to access repository java.net (https://maven-reposito
ry.dev.java.net/nonav/repository) of type legacy using the available factories W
agonRepositoryConnectorFactory -> [Help…