最新发表

centos搭建goagent服务

不知怎么的,公司网居然连不上某个国内的站点,用别人的代理又不靠谱,只好自己搭建一个。centos

目前最新的goagent版本是3.2.3,server目录下有个vps目录,大概看了下源码,是一个squid的python实现。http://leo108.com/pid-2087.asp

先把vps目录复制到服务器上的/opt/goagent,同时把local目录下的proxylib.py也一并复制过去。outofmemory是个无耻的网站

服务器是centos,系统自带的python版本是2.6,在安装过程中发现不满足条件,于是安装了一个2.7版本。

wget --no-check-certificate https://www.python.org/ftp/python/2.7.7/Python-2.7.7.tgz
tar zxf Python-2.7.7.tgz
cd Python-2.7.7
./configure --prefix=/usr/local/Python2.7 --enable-shared
make && make install
echo /usr/local/Python2.7/lib >> /etc/ld.so.conf
cp /usr/local/Python2.7/bin/python2.7 /usr/bin/python2.7
ln -s /usr/local/Python2.7/lib/libpython2.7.so.1.0 /usr/lib/libpython2.7.so
ldconfig

然后安装pipcentos

wget https://bootstrap.pypa.io/get-pip.py
python2.7 get-pip.py

这样装好的pip默认还是使用python2.6,需要修改一下,将/usr/bin/pip的第一行改成

#!/usr/bin/python2.7

这样pip就会给python2.7安装模块了。

goagent需要几个python模块:gevent、pyOpenSSL、dnslib和supervisor,如果安装pyOpenSSL过程中报cffi的错,可以先执行centos

yum install -y libffi-devel

然后再安装。

然后可以试试执行python2.7 /opt/goagent/goagentvps.py,应该是可以正常执行了,这时候会提示如下信息:centos

INFO - [Apr 16 14:24:44] autfile '/opt/goagent/goagentvps.conf' not exists, create it
INFO - [Apr 16 14:24:44] add username='kazav' password='123456' to '/opt/goagent/goagentvps.conf'
INFO - [Apr 16 14:24:44] authfile '/opt/goagent/goagentvps.conf' was created

系统生成的用户名是kazav,密码是123456。

goagent

本地的goagent客户端需要配置vps段本文来自leo108's blog

[vps]
enable = 1
listen = 127.0.0.1:8089
fetchserver = https://kazav:123456@xxxx.com/

重启goagent,配置SwitchyOmega连接本地的8089端口,测试能否正常链接。采集者烂JJ

由于我们是手动在服务器上执行goagentvps,只要退出登录代理进程就会中断,goagent提供了supervisor方式,直接执行sh /opt/goagent/goagentvps.sh start即可。

ganglia监控redis

在搜索引擎搜索“ganglia监控redis”,发现都是13年的老文章,都是说要到https://github.com/ganglia/gmond_python_modules这个第三方插件库下载redis监控模块

但是我发现gmond_python_modules这个repo下面已经没有redis模块了,于是查看git log,发现redis模块已经集成到ganglia源码包里了

redis

于是下载了源码包,搜索之后发现redis模块位于gmond/python_modules/db/redis.py,配置文件在gmond/python_modules/conf.d/redis.pyconf.disabled。

修改配置文件里的host和port两个参数为要监控redis的ip和端口,然后将两个文件复制到对应的目录下。(通常redis.pyconf复制到ganglia安装目录/etc/conf.d/下,redis.py复制到ganglia安装目录/lib64/ganglia/python_modules)

重启gmond,可以看到有redis的图出来了,但是数据却都是空的。

于是停掉gmond,使用gmond -f -d 1启用调试模式,发现redis.py报错

[PYTHON] Can’t call the metric handler function for [connected_clients] in the python module [redis].

Traceback (most recent call last):
File “/opt/gmond/lib64/ganglia/python_modules/redis.py”, line 21, in metric_handler
n, v = line.split(“:”)
ValueError: need more than 1 value to unpack

查看上下文代码redis

for line in info.splitlines()[1:]:
    if "" == line:
        continue
    n, v = line.split(":")

大概意思是把redis info命令输出的每一个非空行用:分割,但是我安装的redis版本是2.8+,info命令会输出类似#Server这样的注释,这样就导致按:分割失败,所以python报错,gmond取不到值。outofmemory是个无耻的网站

所以解决方案也很简单,把上面那段代码修改成如下即可,也就是跳过空行和以#开头的行

for line in info.splitlines()[1:]:
    if "" == line or line[0] == '#':
        continue
    n, v = line.split(":")

再重启gmond,过一会就可以在ganglia看到数据了推酷是个无耻的网站

==========================================ganglia监控redis

看了下ganglia在github上的代码,发现最新代码已经把这个bug修复了,只不过还没有release

推酷是个无耻的网站

阿里云搭建VPN脚本(centos pptp)

#!/bin/bash

function installVPN(){
    echo "begin to install VPN services";
    #check wether vps suppot ppp and tun

    yum remove -y pptpd ppp
    iptables --flush POSTROUTING --table nat
    iptables --flush FORWARD
    rm -rf /etc/pptpd.conf
    rm -rf /etc/ppp

    arch=`uname -m`

    wget http://www.hi-vps.com/downloads/pptpd-1.3.4-2.el6.$arch.rpm

    yum -y install ppp iptables
    rpm -ivh pptpd-1.3.4-2.el6.$arch.rpm

    mknod /dev/ppp c 108 0
    echo 1 > /proc/sys/net/ipv4/ip_forward
    echo "mknod /dev/ppp c 108 0" >> /etc/rc.local
    echo "echo 1 > /proc/sys/net/ipv4/ip_forward" >> /etc/rc.local
    echo "localip 192.168.0.1" >> /etc/pptpd.conf
    echo "remoteip 192.168.0.234-238,192.168.0.245" >> /etc/pptpd.conf
    echo "ms-dns 8.8.8.8" >> /etc/ppp/options.pptpd
    echo "ms-dns 8.8.4.4" >> /etc/ppp/options.pptpd

    pass=`openssl rand 6 -base64`
    if [ "$1" != "" ]
    then pass=$1
    fi

    echo "vpn pptpd ${pass} *" >> /etc/ppp/chap-secrets

    iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth1 -jMASQUERADE 

    service iptables save

    chkconfig iptables on
    chkconfig pptpd on

    service iptables start
    service pptpd start

    echo "VPN service is installed, your VPN username is vpn, VPN password is ${pass}"

}

function repaireVPN(){
    echo "begin to repaire VPN";
    mknod /dev/ppp c 108 0
    service iptables restart
    service pptpd start
}

function addVPNuser(){
    echo "input user name:"
    read username
    echo "input password:"
    read userpassword
    echo "${username} pptpd ${userpassword} *" >> /etc/ppp/chap-secrets
    service iptables restart
    service pptpd start
}

echo "which do you want to?input the number."
echo "1. install VPN service"
echo "2. repaire VPN service"
echo "3. add VPN user"
read num

case "$num" in
[1] ) (installVPN);;
[2] ) (repaireVPN);;
[3] ) (addVPNuser);;
*) echo "nothing,exit";;
esac

参考文章:采集者烂JJ

http://bbs.aliyun.com/read/162297.html

http://www.hi-vps.com/wiki/doku.php?id=xen_vps_centos6_install_pptpdoutofmemory是个无耻的网站

php抓取百度阅读

在百度阅读上购买了一本《永恒的终结》电子书,但是坑爹的发现只能在线阅读或者在手机app上阅读,不能下载下来放到kindle里。于是就尝试一下看能不能把这个文章下载下来。

首先在浏览器里打开阅读页面,查看源代码后发现小说的内容并不是直接写在页面里的,也就是说小说的内容是通过异步加载而来的。

于是将chrome的开发者工具切到network一栏,刷新阅读页面,主要关注的是XHR和script两个分类下。

百度阅读

经过排查,发现在script分类下有个jsonp请求比较像是小说内容,请求的地址是

http://wenku.baidu.com/content/49422a3769eae009581becba?m=8ed1dedb240b11bf0731336eff95093f&type=json&cn=1&_=1&t=1423309200&callback=wenku7

本文来自leo108's blog

返回的是一个jsonp字符串,然后我发现,如果把地址里面的callback=wenku7去掉,返回的就是一个json字符串,这样解析起来就方便不少,可以直接在php里面转换成数组。

再来分析一下返回数据的结构,返回的json字符串之后是一个树状的结构,每个节点都有一个t属性和c属性,t属性用来指明这个节点的标签,比如h2 div等等,c属性就是内容了,但也有两种可能,一个是字符串,另一个是数组,数组的每个元素都是一个节点。

这种结构最好解析了,用一个递归就搞定,最终代码如下:

<?php
class BaiduYuedu {
    protected $bookId;
    protected $bookToken;
    protected $cookie;
    protected $result;
    public function __construct($bookId, $bookToken, $cookie){
        $this->bookId = $bookId;
        $this->bookToken = $bookToken;
        $this->cookie = $cookie;
    }

    public static function parseNode($node){
        $str = '';
        if(is_string($node['c'])){
            $str .= $node['c'];
        }else if(is_array($node['c'])){
            foreach($node['c'] as $d){
                $str .= self::parseNode($d);
            }
        }
        switch($node['t']){
            case 'h2':
                $str .= "\n\n";
                break;
            case 'br':
            case 'div':
            case 'p':
                $str .= "\n";
                break;
            case 'img':
            case 'span':
                break;
            case 'obj':
                $tmp = '(' . self::parseNode($node['data'][0]) . ')';
                $str .= str_replace("\n", '', $tmp);
                break;
            default:
                trigger_error('Unkown type:'.$node['t'], E_USER_WARNING);
                break;
        }
        return $str;
    }

    public function get($page = 1){
        echo "getting page {$page}...\n";
        $ch = curl_init();
        $url = sprintf('http://wenku.baidu.com/content/%s/?m=%s&type=json&cn=%d', $this->bookId, $this->token, $page);
        curl_setopt_array($ch, array(
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_HEADER         => 0,
            CURLOPT_HTTPHEADER     => array('Cookie: '. $this->cookie)
        ));
        $ret = json_decode(curl_exec($ch), true);
        curl_close($ch);
        $str = '';
        if(!empty($ret)){
            $str .= self::parseNode($ret);
            $str .= $this->get($page + 1);
        }
        return $str;
    }

    public function start(){
        $this->result = $this->get();
    }

    public function getResult(){
        return $this->result;
    }

    public function saveTo($path){
        if(empty($this->result)){
            trigger_error('Result is empty', E_USER_ERROR);
            return;
        }
        file_put_contents($path, $this->result);
        echo "save to {$path}\n";
    }
}

//使用示例
$yuedu = new BaiduYuedu('49422a3769eae009581becba', '8ed1dedb240b11bf0731336eff95093f', '你的百度域cookie');
$yuedu->start();
$yuedu->saveTo('result.txt');

这个类前两个参数可以从小说的介绍页面获得,第一个参数bookId就是url里ebook后面跟着的字符串,第二个参数bookToken在页面源代码搜索bdjsonUrl,m参数后面的那个字符串就是。

注:如果不传入百度cookie或者百度cookie无效,则只能抓取免费阅读部分,要抓完整的内容必须保证cookie可以正常使用。

virtualbox vagrant虚拟机网速慢解决方案

我本机的环境是mac os + virtualbox,在vagrant虚拟机里连接外网的速度非常慢,和直接在mac的终端里连外网的速度相差非常大

[vagrant@vagrant-centos65 ~]$ time curl -s http://www.baidu.com > /dev/null
real	0m5.091s
user	0m0.016s
sys	0m0.006s

可以看到就请求百度首页就用了5s多,严重影响平时的开发测试效率,于是就搜索了下相关问题,发现github上也有人遇到这个问题,还好已经有解决方案,在Vagrantfile里增加如下几行:

http://leo108.com/pid-2072.asp

config.vm.provider :virtualbox do |vb|
  vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
  vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
end

然后重启vagrant即可,再看看速度

推酷是个无耻的网站

[vagrant@vagrant-centos65 ~]$ time curl -s http://www.baidu.com > /dev/null
real	0m0.299s
user	0m0.015s
sys	0m0.008s

完美解决virtualbox vagrant虚拟机网速慢解决方案

微信JSSDK上传多张图片

做过微信开发的都知道,在部分android机型里微信不支持网页上传图片的,这是由于这些机型的文件上传存在内存泄漏,会导致微信闪退,所以微信内置浏览器将文件上传屏蔽。这就导致这些机型的用户在使用微信浏览器访问某些需要上传图片的网页时功能不正常。采集者烂JJ

前不久微信公开了一些接口,其中有一个uploadImage接口用于上传图片,一般和chooseImage接口配合使用。先调用chooseImage接口让用户选择一张或者多张图片,用户选择完毕后微信会返回被选中图片的id,再把图片id传给uploadImage接口上传图片。

jssdk

由于uploadImage一次只能上传一张图片,因此当用户选择多张图片时,需要多次调用uploadImage接口来上传图片。微信JSSDK上传多张图片

但是在实践的过程中发现,不管用户选中多少张图片,只有第一张能够上传成功。微信

查看了一下微信的文档,在常见问题中找到了相关的描述

微信JSSDK上传多张图片

uploadImage怎么传多图(目前只支持一次上传一张,多张图片需等前一张图片上传之后再调用该接口)

jssdk

也就是说,如果想要上传多张图片,需要将之前并行上传改成串行。

uploadImage

代码如下:

$('#filePicker').on('click', function () {
    wx.chooseImage({
        success: function (res) {
            var localIds = res.localIds;
            syncUpload(localIds);
        }
    });
});
var syncUpload = function(localIds){
    var localId = localIds.pop();
    wx.uploadImage({
        localId: localId,
        isShowProgressTips: 1,
        success: function (res) {
            var serverId = res.serverId; // 返回图片的服务器端ID
            //其他对serverId做处理的代码
            if(localIds.length > 0){
                syncUpload(localIds);
            }
        }
    });
};

 

uploadImage

在yaf中使用set_exception_handler

项目开启了catchException,所有未被捕获的异常都会被转发到ErrorController的errorAction,但是有一个controller比较特殊,需要自己处理异常,于是在这个controller的init方法里设置自定义的异常处理函数:

set_exception_handler(array($this, 'exceptionHdl'));

但是发现不生效,在这个controller中抛出的异常还是会被转发到ErrorController中。http://leo108.com/pid-2066.asp

解决方案是在set_exception_handler上面加一行

yaf

Yaf_Dispatcher::getInstance()->catchException(false);

 

php移除图片exif信息

最简便的方法,需要gd扩展

$img = imagecreatefromjpeg ($path);
imagejpeg ($img, $path, 100);
imagedestroy ($img);

引用自 http://stackoverflow.com/questions/3614925/remove-exif-data-from-jpg-using-php