最新发表

ganglia监控redis

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

redis

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

于是下载了源码包,搜索之后发现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)

本文来自leo108's blog

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

ganglia监控redis

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

[PYTHON] Can’t call the metric handler function for [connected_clients] in the python module [redis].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本文来自leo108's blog

查看上下文代码本文来自leo108's blog

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

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

ganglia

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

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

再重启gmond,过一会就可以在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

参考文章:

centos

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

http://www.hi-vps.com/wiki/doku.php?id=xen_vps_centos6_install_pptpd阿里云搭建VPN脚本(centos pptp)

php抓取百度阅读

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

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

PHP

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

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

本文来自leo108's blog

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

百度阅读

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

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

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

<?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可以正常使用。本文来自leo108's blog

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

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

本文来自leo108's blog

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

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

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

完美解决

微信JSSDK上传多张图片

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

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

由于uploadImage一次只能上传一张图片,因此当用户选择多张图片时,需要多次调用uploadImage接口来上传图片。推酷是个无耻的网站

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

推酷是个无耻的网站

查看了一下微信的文档,在常见问题中找到了相关的描述本文来自leo108's blog

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

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

代码如下:

微信

$('#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方法里设置自定义的异常处理函数:

在yaf中使用set_exception_handler

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

但是发现不生效,在这个controller中抛出的异常还是会被转发到ErrorController中。

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

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

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

phpstorm后期静态绑定代码提示

先上代码

class Ext_Data {
    protected static $_instance = array();

    public static function getInstance() {
        $className = get_called_class();
        if (!isset(self::$_instance[$className])) {
            self::$_instance[$className] = new $className();
        }
        return self::$_instance[$className];
    }

    public static function retErr($msg) {
        return array('result' => false, 'msg' => $msg);
    }

    public static function retOK($data = array(), $msg = '') {
        return array('result' => true, 'data' => $data, 'msg' => $msg);
    }
}

Ext_Data类是父类,子类需要用到该类的一些静态方法(retOk、retErr),并且要求子类是单例,例如:

本文来自leo108's blog

class Data_KV extends Ext_Data {
    protected function __construct() {
        //构造函数
    }

    public function get($key) {
        //一些操作
        return self::retOK($ret);
    }

    public function set($key, $value) {
        //一些操作
        if ($ret === false) {
            return self::retErr('操作失败');
        }
        return self::retOK($ret);
    }
}

因为子类的类名是动态的,所以我们没有办法在父类的getInstance方法上写注解来指明返回类型,这就导致使用Data_KV的时候没有代码提示,而且还会报方法未定义:

这个问题困扰了很久,今晚和室友吐槽的时候也提到这个问题,于是一拍脑袋想到别人应该也遇到过,于是在jetbrain官网搜索了一下,发现果真有解决方案

代码提示

直接添加一个@return static的注解即可

    /**
     * @return static
     */
    public static function getInstance() {
        $className = get_called_class();
        if (!isset(self::$_instance[$className])) {
            self::$_instance[$className] = new $className();
        }
        return self::$_instance[$className];
    }

phpstorm还支持@return $this   @return self这两种注解