最新发表

(R)?ex输出多行

一直在用(R)?ex,感觉在小规模的服务器管理上十分方便,不需要client端,只要配好ssh信任即可。

(R)?ex输出多行

但是一直有一个问题在困扰我,那就是一条输出多行的命令,经过rex的say run ‘xxxx’之后会变成一行,换行符没掉了,看起来就很不舒服 ,比如ll -h原本应该输出

-rw-r--r-- 1 www www 6.7M Nov  1 03:08 access.log-20141101
-rw-r--r-- 1 www www 6.6M Nov  2 03:33 access.log-20141102
-rw-r--r-- 1 www www 6.5M Nov  3 03:10 access.log-20141103

经过rex的say run ‘ls -lh’输出就变成了

total 664M-rw-r--r-- 1 www www 6.7M Nov  1 03:08 access.log-20141101-rw-r--r-- 1 www www 6.6M Nov  2 03:33 access.log-20141102-rw-r--r-- 1 www www 6.5M Nov  3 03:10 access.log-20141103-rw-r--r-- 1 www www 643M Oct 31 00:32 access.log.20141031

今天实在受不了了,新想肯定有人也烦恼过这个问题,于是跑到github上搜索了一下,结果发现果真有人提过issue,只要在say run中间加一个scalar就行本文来自http://leo108.com

rex -e "say scalar run 'ls -lh /mnt/logs/access_log'"

记一次git误操作恢复全过程

我们团队使用git flow方式,和此次误操作有关的有3个分支:develop、feature/discuss和feature/search,两个feature分支都是从develop分支上拉出来的,feature/discuss先从develop分支拉出来,然后develop分支上有了新的commit(其他分支feature/other开发完毕合入develop),之后再从develop分支拉出feature/search分支。

本次事故实际上是由两次误操作导致的,第一次误操作是在feature/discuss分支上执行了采集者烂JJ

git pull origin feature/search

这就导致处于开发状态的分支feature/search被合入到feature/discuss,我在发现这个误操作之后立刻让同事撤销此次merge,我以为同事会使用记一次git误操作恢复全过程

git reset --hard cab39f53

来撤销提交,也就没有去详细看提交日志,于是就继续开发。

等到这个分支功能开发完毕,将这个分支合入develop

git flow feature finish discuss

这个时候git会将发生合并的文件列出来,这个时候我发现有大量文件在这次合并过程中被删除,这些文件都是分支feature/other合并到develop时新增的,于是我看了一下git log,主要的提交如下图:

git

原来是同事使用了revert而不是reset来回滚,git revert实际上是把当前的要revert到的hash和head做diff,然后对head应用这些diff作为一个新的commit,这样在feature/discuss这个分支上来看,代码是回到了cab39f53这个版本。http://leo108.com/pid-2026.asp

但是当把feature/discuss合入develop分支时就有问题了,revert产生的提交也会应用到develop分支,这就把feature/other分支产生的所有变更都“撤销”了。

如果只有第一个误操作,那恢复起来就是一个reset命令的事情,现在两个误操作在一块就会麻烦一点。

本文来自leo108's blog

  1. 把develop分支通过reset命令恢复到merge feature/discuss之前
  2. 从develop分支上拉出一个新的feature/tmp分支
  3. 找到拉feature/discuss分支之前的一个commit A
  4. 找到feature/search合入feature/discuss分支之前的一个commit B
  5. 通过git diff A B获取discuss分支到误操作前的所有提交,保存到patch1
  6. 找到最后一个revert的commit C
  7. 切换到feature/discuss分支,通过git diff C来获取误操作之后的所有提交,保存到patch2
  8. 切到develop分支,依次执行git apply patch1和git apply patch1

这样就把错误的提交给剔除了,不过遗憾的是feature/discuss的提交历史全都合并成了1个提交。

本文来自http://leo108.com

===========================================================

未经允许严禁转载

在写这篇博客的时候我突然想到,第一次的patch应该是没必要的,生成patch2之后,直接checkout B,拉出一个tmp分支,然后apply patch2,提交后,再把tmp合入develop,这样会丢失误操作之后的提交历史,但误操作之前的提交历史都还在。

===========================================================

revert

11月3日更新:git-flow

实际上只需要使用git rebase命令就可以直接剔除那3个错误的revert,以下内容引用自git help rebae

A range of commits could also be removed with rebase. If we have the following situation:

E—F—G—H—I—J topicA
then the command

git rebase –onto topicA~5 topicA~3 topicA

本文来自leo108's blog

would result in the removal of commits F and G:

revert

E—H’—I’—J’ topicA

采集者烂JJ

git太强大!

本文来自http://leo108.com

mac升级到Yosemite导致goagent不能开机启动解决方案

今天把mac升级到Yosemite,结果发现goagent的进程不在了。

goagent

goagent的开机启动是使用mac的LaunchDaemons来实现的,在goagent的目录下有个addto-startup.py文件,执行这个脚本会在/Library/LaunchDaemons/目录下生成一个org.goagent.macos.plist文件,然后执行

sudo launchctl load /Library/LaunchDaemons/org.goagent.macos.plist

就可以实现开机启动(实际上在执行完这条命令之后就会启动goagent进程)。但是现在进程却不存在了,通过命令http://leo108.com/pid-2021.asp

sudo launchctl unload /Library/LaunchDaemons/org.goagent.macos.plist
sudo launchctl load /Library/LaunchDaemons/org.goagent.macos.plist

重新加载仍然不行。未经允许严禁转载

于是查看mac官网对于LauchDaemons的文档,参照example试着自己写了个LauchDaemons,发现可以正常加载,于是判断是goagent的LauchDaemons有错误。

本文来自http://leo108.com

接下来就比较简单了,只要把org.goagent.macos.plist里面的配置逐个干掉,然后重新加载,看看干掉哪个配置的时候可以正常启动。

LauchDaemons

最终发现是WorkingDirectory这个配置导致,在我的配置里WorkingDirectory这个配置项是个空字符串,不知道是更新之后不支持了还是啥情况,总之干掉本文来自leo108's blog

<key>WorkingDirectory</key>
<string></string>

之后再加载goagent进程就出来了。

本文来自leo108's blog

php原生模板引擎性能优化

好久没写博客了,今天来一发。

背景:

基于YAF开发的一个网站,模板引擎使用原生php,同时为了满足需求,自己开发了一个widget的功能,每次调用widget都会引发一次模板渲染。在网站首页会调用同一个widget数十次,在查看xhprof的数据时,发现widget渲染模板耗时较多,主要消耗在加载模板文件上,由于同一个widget使用的模板是同一个,所以希望只加载一次模板来提高执行效率。本文来自http://leo108.com

ab -n1000 -c50的结果是11.31qps(虚拟机性能差)

采集者烂JJ

思路:

首先想到的是在include文件之前把文件读入内存,放到一个静态变量中,之后再调用时就直接从静态变量中取即可。但问题是把模板文件放到变量中之后,怎么渲染呢?最简单粗暴的办法是使用eval,但是太粗暴了,不想使用。

然后想起来php有一个wrapper的功能,可以注册一个wrapper,比如mem,同样是把模板文件读入内存,然后就可以通过include(‘mem://模板引擎路径’)的方式来加载,大致代码如下:

本文来自leo108's blog

class Ext_Wrapper {
    //存放模板文件内容的静态成员变量
    protected static $_fileArr = array();
    protected $_pos;
    protected $_curFile;

    public function stream_open($path, $mode, $options, &$opened_path) {
        $path = substr($path, 5, strlen($path) - 5);
        //判断模板文件是否已经在变量中,不存在就读取
        if (!isset(self::$_fileArr[$path])) {
            self::$_fileArr[$path] = file_get_contents($path);
        }
        $this->_curFile = $path;
        $this->_pos     = 0;
        return true;
    }
    public function stream_read($count) {
        //直接从静态变量中读数据
        $content = self::$_fileArr[$this->_curFile];
        $ret     = substr($content, $this->_pos, $count);
        $this->_pos += strlen($ret);
        return $ret;
    }
    //其他方法略
}
//注册wrapper
stream_register_wrapper('mem', 'Ext_Wrapper');

ab -n1000 -c50的结果是12.68qps

PHP

最后试了一下eval的性能,大致代码如下:性能

class Ext_View extends Yaf_View_Simple {
    private $tmpPath;
    private $tmpData = array();
    private $include;

    //用于保存模板内容的静态变量
    protected static $_fileArr = array();

    public function display($tplFile, $data = array()) {
        $this->tmpPath = $this->getScriptPath() . '/' . $tplFile;
        if (is_array($data)) {
            $this->tmpData = array_merge($this->tmpData, $data);
        }
        unset($tplFile);
        unset($data);
        extract($this->tmpData, EXTR_OVERWRITE);
        //判断模板文件是否已经在变量中,不存在就读取
        if (!isset(self::$_fileArr[$this->tmpPath])) {
            self::$_fileArr[$this->tmpPath] = file_get_contents($this->tmpPath);
        }
        eval('?>'.self::$_fileArr[$this->tmpPath]);
    }
    //其他代码略
}

ab -n1000 -c50的结果是15.07qps,吓尿了,eval果真是简单粗暴有效

本文来自http://leo108.com

sonar中文乱码解决方案

OSC搞了一个代码质量管理系统,用的是sonar,看上去很不错,于是就自己搭建了一个,但是发现代码里的中文全部变了‘?’号。http://leo108.com/pid-2011.asp

首先检查代码,确认是UTF8格式无误。

未经允许严禁转载

再检查代码目录下的sonar-project.properties文件,编码配置sonar.sourceEncoding=UTF-8,也没有错。未经允许严禁转载

然后检查数据库,发现数据库的字符集是latin,于是把所有的表字符集都改成utf8。

乱码

重新运行了一下sonar-run,以为能解决了,结果发现还是‘?’号。本文来自leo108's blog

检查数据库中的数据,已经是乱码的了,说明在写入的时候就有问题了。

本文来自http://leo108.com

再检查了一下sonar-run的配置,里面的jdbc配置是:

sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&amp;characterEncoding=utf8

看上去是utf8没错啊……但是我发现里面有个&amp;,这个是&的转义,于是想会不会是这里的问题,把&amp;改成&,再运行sonar-run,显示中文正常!

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

piwik页面卡住问题追查记录

今天无聊就折腾了下piwik,但是发现有概率页面会504,查看nginx日志是php执行超时,而且第一次504之后,后面的所有页面访问全部都是504,重启php-fpm之后又可以正常访问了。piwik

于是打开php-fpm的slowlog,在页面504的时候slowlog里也打出了对应的调用栈:未经允许严禁转载

script_filename = /home/www/online/website/piwik/index.php
[0x00007fbec1568f48] session_start() /home/www/online/website/piwik/libs/Zend/Session.php:469
[0x00007fbec1568370] start() /home/www/online/website/piwik/core/Session.php:113
[0x00007fbec1567e68] start() /home/www/online/website/piwik/core/FrontController.php:416
[0x00007fbec1567a08] prepareDispatch() /home/www/online/website/piwik/core/FrontController.php:502
[0x00007fbec15675f8] doDispatch() /home/www/online/website/piwik/core/FrontController.php:84
[0x00007fbec15671d0] dispatch() /home/www/online/website/piwik/core/dispatch.php:34
[0x00007fbec15669a0] +++ dump failed

看来是执行session_start的时候卡住了,php的session是有锁机制的,如果某个页面开启了session,并且该请求尚未结束,则后续该session的请求就会全部锁住,等待之前的请求结束。

本文来自leo108's blog

也就是说piwki页面卡住的原因是之前的某次请求没有结束,但是是哪个请求没有结束呢?因为没有看piwik的代码,而且504是有概率出现的,并不是访问了某个特定的页面后才504,问题追到这里就遇到了瓶颈。

鼓捣了半天之后突然想到,那个没有结束的请求肯定会打在php的slowlog里面,于是重新检查了一下,果真在一堆session的slowlog中发现了不一样的日志:

piwik页面卡住问题追查记录

script_filename = /home/www/online/website/piwik/index.php
[0x00007fbec1568c40] curl_exec() /home/www/online/website/piwik/core/Http.php:483
[0x00007fbec1568798] sendHttpRequestBy() /home/www/online/website/piwik/core/Http.php:94
[0x00007fbec15685e0] sendHttpRequest() /home/www/online/website/piwik/core/Http.php:720
[0x00007fbec1567e00] fetchRemoteFile() /home/www/online/website/piwik/plugins/ExampleRssWidget/RssRenderer.php:46
[0x00007fbec1567be8] get() /home/www/online/website/piwik/plugins/ExampleRssWidget/Controller.php:25
[0x00007fffe0d3cf00] rssPiwik() unknown:0
[0x00007fbec1567788] call_user_func_array() /home/www/online/website/piwik/core/FrontController.php:531
[0x00007fbec1567378] doDispatch() /home/www/online/website/piwik/core/FrontController.php:84
[0x00007fbec1566f50] dispatch() /home/www/online/website/piwik/core/dispatch.php:34
[0x00007fbec1566720] +++ dump failed

看上去是个curl调用http请求,追了下里面的代码,发现是个公共的类,并不是固定请求某个url,这也简单,在curl_exec()之前加个打日志的代码,把curl请求的url记录下来。

未经允许严禁转载

于是又随便点了记下piwik的页面,发现有记录了一个url:http://feeds.feedburner.com/Piwik

本文来自leo108's blog

看上去是rss的url,直接在piwik的grep这个url,结果如下:

./plugins/ExampleRssWidget/Controller.php:            $rss = new RssRenderer('http://feeds.feedburner.com/Piwik');
./plugins/ExampleRssWidget/Controller.php:            $rss = new RssRenderer('http://feeds.feedburner.com/PiwikReleases');

是个插件的代码。piwik页面卡住问题追查记录

进到piwik的后台,直接把ExampleRssWidget这个插件禁用。问题解决!

本文来自http://leo108.com

PS:中国的国情啊……………………

json格式化、高亮库jsonFormater

JsonFormater

基于jQuery的json格式化、高亮库

核心代码参考天马行空工作室,本人只做了模块化和一些代码优化。

demo

http://leo108.github.io/jsonFormater/

github地址

https://github.com/leo108/jsonFormater

使用方式

引入jQuery

<script type="text/javascript" src="jquery-1.7.2.min.js"></script>

注意:jQuery版本要求>=1.7.2

json格式化、高亮库jsonFormater

引入jsonFormater.js和jsonFormater.css

<script type="text/javascript" src="jsonFormater.js"></script>
<link href="jsonFormater.css" type="text/css" rel="stylesheet"/>

在html中新增一个空的元素作为显示的容器

例如<div id='container'></div>jQuery

调用JsonFormater

$(document).ready(function(){
    var options = {
        dom : '#container' //对应容器的css选择器
    };
    var jf = new JsonFormater(options); //创建对象
    jf.doFormat('{"string":"leo108"}'); //格式化json
});

效果图

效果图

采集者烂JJ

详细使用方式

配置

支持的配置以及默认配置如下:本文来自leo108's blog

{
    dom: '',          //用于放置的dom的选择器
    singleTab: "  ",  //单个tab
    tabSize: 2,       //缩进数量
    quoteKeys: true,  //key是否用双引号包含
    imgCollapsed: "images/Collapsed.gif", //收起的图片路径
    imgExpanded: "images/Expanded.gif",  //展开的图片路径
    isCollapsible: true //是否支持展开收起
}

方法

var obj = new JsonFormater({dom: '#container'});
obj.doFormat(json) //格式化一个json字符串或者js对象
obj.expandAll()    //全部展开
obj.collapseAll()  //全部收起
obj.collapseLevel(level)  //展开到level层

 

本文来自http://leo108.com

WordPress语法高亮增强插件更新至2.5.0版本

WordPress语法高亮增强插件(SyntaxHighlighter++)今日更新至2.5.0版本,此版本是一个bug修复版本,主要是修复在高版本(>=3.7.0)的后台“多媒体”→“媒体库”中,点击编辑任意一个多媒体,多媒体页面会被本插件的输入框遮挡的问题。

WordPress官方下载地址:http://wordpress.org/extend/plugins/syntax-highlighter-with-add-button-in-editor/

采集者烂JJ