linux shell下按ctrl+箭头出现5D, 5C

今天在Linux下使用shell的时候,发现当我想使用Ctrl+箭头进行快速跳跃的时候,并没有出现预期的 光标 移动,而是出现了 5D, 5C 的字样。
这个很奇怪,查下相关资料,可以这样避免:
把以下两行添加到 ~/.inputrc文件中:
"\e[1;5C": forward-word # ctrl + right
"\e[1;5D": backward-word # ctrl + left

退出shell再重新登录,就可以解决了。

一次线上业务跨IDC高延迟问题解决的案例分享

最近在梳理某项目上各服务接口的性能情况,遇到两个问题。以下是定位和解决问题的一个思路,分享给大家。
业务之前并没有详细的性能日志记录,仅在电信机房(T机房)进行了性能测试,结果是各接口满足预期,服务上线。
在进一步对接口进行性能分析时,对各业务接口的关键路径添加了日志统计,通过日志进行分析,将接口的延迟进行统计,接入Grafana,观察数据后,发现两类问题。

  1. 连接MongoDB的服务,网通机房(C机房)延迟比电信机房(T机房)要高。
  2. 连接Mysql的服务,网通机房(C机房)延迟比电信机房(T机房)高。

    NOTE: 这些服务接口,都是只读,没有写操作。

    对两类问题分别进行排查:

MongoDB

简单的排查后发现,MongoDB实例有过一次迁移,并且迁移后只保留了电信机房(T机房)的实例,网通机房(C机房)没有从库,所以网通机房(C机房)延迟比电信机房(T机房)高。对网通机房(C机房)部署了从库实例后,却意外发现电信机房(T机房)的延迟比网通机房(C机房)高了。再次排查后发现,代码中配置的MongoDB的读策略是secondary(从库优先),所以网通机房(C机房)有从库后,电信机房(T机房)也去网通机房(C机房)读取,导致了电信机房(T机房)的延迟变高。更改读策略为nearest(就近优先),有所好转,但并没有预想的效果那么好。仔细看下官方文档

The driver reads from a random member of the set that has a ping time that is less than 15ms slower than the member with the lowest ping time. Reads in the MongoClient::RP_NEAREST mode do not consider the member’s type and may read from both primaries and secondaries.

就会发现,nearest是在客户端维护一个到各个实例延迟小于15ms的集合,而我们电信机房(T机房)到网通机房(C机房)是光纤直连,延迟在12ms左右,所以,每次客户端可能会连接到电信机房(T机房),也可能到网通机房(C机房)。
这点在以后的应用中,大家可以注意下。

Mysql

在所有的服务中,只有一个服务接口是读mysql实现的,而这个接口的表现更是奇怪,网通机房(C机房)的延迟比电信机房(T机房)多100 ms+。

开始时猜测有可能业务内做了某些写主库的操作,比如写mysql,或写redis之类的,跨机房写导致的延迟高。
实际分析后发现,业务内并没有写操作,多出的时间就是读mysql的时间。
mysql是有网通机房(C机房)的从库的,为什么读取从库的数据,延迟还会这么高呢。在我们服务端ping 网通机房(C机房)的mysql ip,发现延迟正常,只有零点几毫秒,不存在网络问题。
下一步就是通过抓包,分析下我们服务端跟mysql间到底有哪些交互,到底是哪个环节慢了。

根据抓包结果发现,正常的select查表请求很快能得到响应,但当从我们服务端发送一个 “Describe tableName”的请求到mysql 服务端时,服务端等待了较长时间(30ms+)才返回结果,而且一次接口服务请求中,有多次Describe的请求,这样,导致网通机房(C机房)最终延迟很高。
问题定位后,开始尝试解决。
解决问题前需要先理清思路:

  1. Describe TableName 这个命令是干什么用的,业务里并没有显式调用。这个请求能不能去掉。
  2. 如果不能去掉,那它的延迟为什么这么高,能不能优化?

第一个问题比较简单,Describe 命令是现在ORM中比较通用的做法,通过获取数据库的表结构,来动态的创建Model。如果不调用Describe命令,当然也可以做到,那样就需要自己业务端对每个model进行声明,这样开发成本会大大增加,这个方式不可取。所以需要保留Describe命令。

第二个问题,延迟为什么高,这个命令是很简单的一个命令,没有任何复杂的操作,而且主库上都没有这个问题。结合DBA同学在Mysql上使用了Atlas中间件,可以大胆猜想下,应该是这个中间件搞的鬼,把select请求分配到从库执行,但是把Describe分配到了主库执行,有可能是因为Atlas中间件只考虑了一些读的SQL,把这类请求分配到从库,而其它各种请求,可能由于过于复杂,就默认分配到主库去执行。当然这只是猜测,没有查看过Atlas的源码,所以不能妄下结论。结合已经整理到的线索,跟DBA同学进行了确认,确定 Describe命令确实被Atlas中间件发送到主库去执行了,至于这么做的原因,是为了避免主从结构不一致时,从库拿到的表结构错误。这种情况下,我们也不能评价说中间件做的到底合理不合理,所以我们需要从自己的角度再思考下能不能优化。如果说希望避免每次请求都执行Describe命令,除了说刚才提到的自己声明,另外一个方式就是cache了,因为表结构变化的频次太低了,我们完全可以设置一个较长时间的cache,来避免频繁的这种请求。业务使用的是Phalcon框架,这个框架中已经提供了这种meta-Data cache的方案,Yii中也有类似的实现: schemaCaching
当启用这种cache后,效果就很明显,可以看到:
网通机房(C机房)延迟从原来的120ms降到7ms, 电信机房(T机房)延迟从原来的10ms降低到5ms.

后续需要考虑的就是,如果表结构发生变化,如何在不影响业务的情况下进行更新。这个也可以有多种实现的方案,大家可以自己想下。

总结

解决问题的思路就在于,遵循最小化原则,先对可能产生这种问题原因进行大胆猜测,然后快速验证,逐步缩小范围,将问题定位到一个最小可复现的范围内,再深入分析具体原因。当然这一切都要有数据说话,如果平时开发中,能提供足够丰富的日志数据,就可以很快的定位问题,甚至提前发现问题。

安装chrome 出现 0x80004002错误的解决办法

家里电脑重装了,想安装chrome,网络不好啊,用online的方式安装,代理都不好设置。
所以最好下载个offline的安装包。
http://www.google.com/chrome/eula.html?system=true&standalone=1
然后开始安装。
安装中遇到了0x80004002的错误提示,使用管理员权限打开又提示 已经有另一个chrome安装中。

解决办法就是 重启电脑,按F8进入安全模式,进行安装,即可安装成功。

chrome-0x80004002

centos 下安装 perf

查了一些资料,如果要安装perf的话,都说执行

apt-get perf
apt-get linux-tools
yum install google-perftools google-perftools-devel

之类的。
不过在centos下确实没有这些东西可用。
yum源找不到,所以还是自己找源装吧。

在这里找到一个rpm包的库: https://www.rpmfind.net/linux/rpm2html/search.php?query=perf

可以找到perf的rpm包。

centos-perf-rpm-install

Read more…

如何在Fiddler script中捕获请求结果并针对返回内容发起一个新请求

有朋友报名东方时尚,理论课选的网上教学,需要看完所有的视频才能算科目一培训完成,在很早之前就有朋友因为这个找过我。当时东方时尚的授课系统还很简单。。每次看完一个视频都会发起一个固定的请求,表示当前视频看完了。所以之前的做法很简单,把那个url在新标签页打开,每开一个视频,刷一下页面,就算看完一段视频了。。所以不用什么复杂操作,很容易就能完成。
不过现在,东方时尚的系统明显升级了,已经不是那么简单的逻辑了,每次请求一个新视频,都会有一个/start的请求,把视频id返回,后边每次心跳和看完视频的请求,都是以该id为参数。不能再像以前那么简单的玩耍了。。本来是想写个chrome的扩展来拦截请求做处理的,突然想到Fiddler的script就可以做这些,而且语法是.NET的,会比较熟:)。
做法也很简单,就是在OnBeforeResponse阶段,获取到body,正则匹配到需要的id,然后再发起一个请求:),多说无益,直接show code.

static function OnBeforeResponse(oSession: Session) {
        
        if (m_Hide304s && oSession.responseCode == 304) {
            oSession["ui-hide"] = "true";
        }
        oSession.utilDecodeResponse();

        var oBody = oSession.GetResponseBodyAsString (); 
        var regex = /{"data":\{"streamId":(\d+)},"/i ;
        if (oBody.match(regex)) {
            var matches = oBody.match(regex);
            //MessageBox.Show(matches );
            var s = "POST http://api.dfsstv.cn/api/v1/Stream/End?streamId=" + matches[1] + " HTTP/1.1\r\nHost: api.dfsstv.cn\r\nConnection: keep-alive\r\nContent-Length: 0\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nDeviceId: 1456634190000\r\nOrigin: http://www.dfsstv.cn\r\nApiKey: 59d71859d3dd491a8ad6accbe7262d94\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36\r\nAuthToken: a54a874bddfb449cbbf4ed698b4e0fc4\r\nAccept: */*\r\nReferer: http://www.dfsstv.cn/?u=11107668&p=06260\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.8\r\n\r\n";
            try{ 
                FiddlerObject.utilIssueRequest(s); 
            } 
            catch(e){ 
                MessageBox.Show("send failed" + e.ToString()); 
            } 
        }
}

regex正则匹配内容。使用 utilIssueRequest 发起请求。
当然这里的做法简单了些,因为东方时尚的请求header里还使用了authToken认证,每次登录的token都不一样,这里直接写死了。

到这里就简单多了,每次打开视频就相当于播放完了,直接刷新就可以看下一集了。
下边图片就是效果,每次视频一start,就产生一个end的请求:)

fiddler-BeforeOnResponse-send-new-request

fiddler-BeforeOnResponse-send-new-request

当然也没这么简单。。它会把token放在localSotrage里,每次开始播放的时候会清掉,应该是等快结束的时候才放进去。。所以导致每次刷新页面都会需要重新登录,所以又要麻烦点,在浏览器的开发者工具里,每次刷新页面前,都set一下localStorage,就可以拉。。

视频是可以快速跳过了,但是每一章视频看完后都会有一个测验,要把三十道题目都回答正确才行。一看请求,每次请求问题的时候,答案也放在返回结果里了。。所以,很容易想到,答题完成其实也就是一个固定的请求而已。。硬着头皮答完了一次题,抓到了完成题目的请求,是一个 Practice/Finish?lessonId=1 这样的请求,每次只要改lessionId的值就好了, 很简单吧。。:)

openresty/nginx 中如何替换正文字符串

最近搞了个海外的阿里云主机后,就把自己的个人站迁移了。然后就想办法做优化,用上了openresty + php7 + http2。
因为历史原因,自己搞了几个不同的域名,一个是fukun.org,还有一个之前一直用的digdeeply.org,现在想统一起来,可是却发现wordpress里有很多上传附件的时候,链接已经是写死的,使用的当时的域名digdeeply.org,如果去一个一个的把他们替换过来,就不太现实了。
因为自己用过openresty中的lua,所以首先想到的就是使用一个content_by_lua来实现替换,在这个阶段把内容中字符串替换掉,不过有个细节问题就是这里lua拿到的都是nginx的一个一个的chunk,如果想要替换的字符串正好在两个chunk的分界处,那么这个就会被漏掉而不能替换。
然后在查一些资料的时候,突然发现,其实,nginx自己就有支持字符串替换的模块的: sub_filter , 这样就不用自己在lua写了,会更方便一些,那么先来看看sub_filter怎么用吧。

location / {
    sub_filter 'https://fukun.org'  'https://fukun.org';
    sub_filter 'https://fukun.org'  'https://fukun.org';
    sub_filter_once off;
}

用法很简单,只要使用 sub_filter ‘search’ ‘replacement’ ,把被替换和替换内容给出来就可以了,可以写多条规则,替换多次。 其它的几个参数稍微看下文档也能明白了:

语法: sub_filter_once on | off;
默认值: sub_filter_once on;
配置段: http, server, location 字符串替换一次还是多次替换,默认替换一次。如果全文中有多个查找到的字符串,希望全部替换的话,就开启这个选项。

语法: sub_filter_types mime-type …;
默认值: sub_filter_types text/html;
配置段: http, server, location 指定需要被替换的MIME类型,默认为“text/html”,如果制定为*,那么所有类型的内容都会被替换,我们其实一般来说,只希望替换text/html中的就可以了,因为如果是图片啥的,你想替换啥呢?

如何安装sub_filter功能模块http_sub_module

以上是sub_filter的用法,但是如果你已经安装好nginx服务了,而且之前没有安装sub_filter模块,那怎么办呢,总不能把nginx全部重新编译重装吧,那样会覆盖之前的nginx配置,还得再来配置一遍。
可以用另外一种方法来更新nginx,那就是只从新编译,而不安装。就是只执行 ./configure 和 make 方法,而不执行make install,这样就不会有问题了。然后把make编译好的nginx二进制文件拷贝到已安装的nginx sbin目录下覆盖之前的,覆盖之前请做好备份,并停止当前运行的nginx。
我是用的openresty,所以很多configure的参数不用自己加,第一次我编译的时候,因为需要http2和status模块, 只执行了:

./configure --prefix=/home/s/apps/openresty-1.9.7.1 --with-http_v2_module --with-http_ssl_module --with-luajit --with-http_stub_status_module

现在需要sub_filter功能,所以只要添加–with-http_sub_module就可以了。那么就是这么编译:

./configure --prefix=/home/s/apps/openresty-1.9.7.1 --with-http_v2_module --with-http_ssl_module --with-luajit --with-http_stub_status_module --with-http_sub_module

然后把build/nginx-xxx/objs目录下的nginx二进制文件复制走去用就可以了。

shell/bash if elif then fi 的用法

#!/bin/sh

a=10
b=20

if [ $a == $b ]
then
   echo "a is equal to b"
elif [ $a -gt $b ]
then
   echo "a is greater than b"
elif [ $a -lt $b ]
then
   echo "a is less than b"
else
   echo "None of the condition met"
fi

深入理解计算机系统P36练习题2.15

深入理解计算机系统P36
练习题2.15
只使用位级和逻辑运算,编写一个C表达式,他等价于x==y。换句话说,当x和y相等时他将返回1,否则就返回0.

题目考察的是位级运算符和逻辑运算符的用法,以下是两种实现方式,供参考:

#include 

void main(){
int x = 0x1101, y = 0x1100;
printf("%dn", 0x01 ^ x ^ y || 0);
printf("%dn", !(x ^ y));
x = 0x1101;
y = 0x1101;
printf("%dn", 0x01 ^ x ^ y || 0);
printf("%dn", !(x ^ y));
}

PHP安装时libiconv错误问题解决

/var/tools/php-5.6.17/ext/iconv/iconv.c:2512: undefined reference to `libiconv_open’
ext/xmlrpc/libxmlrpc/encodings.o: In function `convert’:
/var/tools/php-5.6.17/ext/xmlrpc/libxmlrpc/encodings.c:73: undefined reference to `libiconv_open’
/var/tools/php-5.6.17/ext/xmlrpc/libxmlrpc/encodings.c:81: undefined reference to `libiconv’
/var/tools/php-5.6.17/ext/xmlrpc/libxmlrpc/encodings.c:101: undefined reference to `libiconv_close’
collect2: ld returned 1 exit status
make: *** [sapi/cli/php] Error 1

在阿里云安装php时,make的时候,发生了libiconv错误,通过安装libiconv,指定with-iconv-dir也没解决。最终是在make时加了一个参数,然后顺利编译通过的。
make ZEND_EXTRA_LIBS='-liconv'

oh-my-zsh自动更改screen标题的问题解决

使用oh-my-zsh时,同时使用了screen插件,oh-my-zsh对screen有个优化,会自动的变更当前window的title,这个功能虽然看起来很好用,但对我个人来说,很是不习惯,因为我自己有对每个window命名的习惯,每个window干什么自己都记得很清楚,他一来了就给我重命名,搞的我很不习惯,所以,我需要把这个oh-my-zsh自动更改screen的title的情况关闭掉。
查找了相关资料后,找到了一个方法,其实很简单。在 .zshrc 的配置文件里,添加一行:

DISABLE_AUTO_TITLE="true"

就可以了。