记一次PHP7中正则匹配失败,原因为PREG_JIT_STACKLIMIT_ERROR

最近弄了一台机器,安装php7,性能很棒!

但是在运行中发现之前的程序运行有些异常,有些正则匹配不到了,开始怀疑是正则表达式的问题,一番查找发现php7并没有对正则进行改动。接下来就是一番痛苦的查找bug,我甚至写了一个非正则的函数来替换原来的正则匹配,不过太消耗cpu而放弃了。

还是继续找bug,打印出正则的错误信息

echo array_flip(get_defined_constants(true)['pcre'])[preg_last_error()];

出现了 PREG_JIT_STACKLIMIT_ERROR

官方给出的提示是:

  • PREG_JIT_STACKLIMIT_ERROR (自 PHP 7.0.0 起)
PREG_JIT_STACKLIMIT_ERROR 当 PCRE 函数因 JIT 栈空间限制而失败, preg_last_error() 就会返回此常量。

果然是php7中引入的特性引起的!

在php7.1中,我们没办法改变jit的栈空间,当字符串太大的时候,栈空间满了,直接就出错了。

解决方案

打开php.ini,找到pcre.jit=1这行,改成pcre.jit=0,不使用jit

 

autossh穿透内网

之前树莓派使用ssh建立反向连接的时候不稳定,一会儿没用,就自动断开了,这个时候就得有一个帮手帮你维持这个连接,那就是autossh。

如何使用?

安装完autossh之后,可以用如下命令启动autossh

$ autossh -M 5678 -NR 1234:localhost:22 user@123.123.123.123 -p 222

上面几个参数的含义说明一下

-M 5678 这个表示是监控端口,检测到这个端口不通会重新连接

1234:localhost:22  1234表示需要在远程主机上开启的端口,22表示本地的ssh端口

user@123.123.123.123 -p222  user为vps上面的用户,222为vps ssh端口

避免输入密码

如果你不是用的sshkey,那么建立反向连接是需要输入密码的。为我们之后的加入开机启动带来了障碍。

生成sshkey

ssh-keygen -t rsa

由于我们不需要密码,所以一路回车,生成的id_rsa.pub文件在~/.ssh/文件夹里面。之后就可以将这个key推送到vps,使用如下命令进行推送

ssh-copy-id -i ~/.ssh/id_rsa.pub vps用户@vps主机地址 -p 端口/

(更多…)

穿透内网访问你的树莓派

前几天入手了一个树莓派3,然而不幸的是家中的网没有公网IP,直接访问肯定是没戏了,这时候突然想到了打洞这个词,我想ssh打洞肯定是可以的。

经过一番搜索,答案找到了,实现非常简单,步骤如下。

1.原料

实现内网穿透,你首先需要一台在公网的vps,哪个国家的都无所谓。

2.VPS配置

vps开启SSHD的GatewayPorts选项,开启方式如下:

vim /etc/ssh/sshd_config
# 找到GatewayPorts选项,将其变为yes

GatewayPorts yes
# 重启sshd服务
service sshd restart

3.树莓派配置

假设VPS的IP为1.1.1.1,远程转发的端口号为11111(端口号最好高于1024,否则需要使用root权限),远程服务器的ssh端口为22,登陆用户为username;
继续假设本地路由器需要转发的端口为22,路由器的在LAN中的IP地址为192.168.1.1。

当我们确认好了各个数字的意义后,就可以开始替换下面的命令了。 (更多…)

Redis主从模式下从库过期的key仍然能够被读到的解决方案

Redis主从模式下,当对一个key设定过期时间,到期之后从库依然能够读取到数据。这个问题困扰了我很久,相信很多人都遇到过这种问题了。(前提是你不去读主库,并且redis版本在3.2以下)。

经过一番搜寻,发现很多人遇到的问题和我一样。

主Redis

setex test 20 1
+OK
get test
$1
1
ttl test
:18

从Redis

get test
$1
1
ttl test
:7

以上都没问题,然而过几秒再看从Redis

ttl test
:-1
get test
$1
1

test这个key已经过期了,然而还是可以获取到test的值。

在使用Redis做锁的时候,如果直接取读从库的值,这就有大问题了。 (更多…)

js保存用户自定义的样式重新载入会闪烁的解决方案

在制作一个页面的时候,有需要前台js保存用户自定义的样式的需求,但是保存之后,重新刷新页面,会显示原来的样式,然后再变更为现在的自定义的样式。

这是一个有闪烁的例子,点击打开

保存样式之后,再重新强制刷新几次,可以看到页面在载入的时候会出现闪烁的情况。强迫症患者表示这不能接受,页面这么小的情况都闪烁的这么厉害,这页面大了,加载速度慢了,自定义样式还得等着加载完成之后才能显示,就失去了自定义的意义了。

上面的闪烁是可以理解的,css渲染完成之后,js给body增加了一个class,浏览器又会重新渲染,因此会出现短暂的闪烁

那么在渲染到head的时候,此时再用js给head标签里面增加css呢?这样不就在body出现之前就完成了css的渲染吗?果然,这样的方法是可行的

这是一个没有闪烁的例子,点击打开

至此,问题解决。也许更多的原因我还需要去看看浏览器渲染页面相关的文章。

awk分析nginx日志中的网页响应时间

nginx日志可以十分方便的看到每一个请求的响应速度,通常我会用awk去分析这些请求耗时。通常nginx的log配置是这样的

log_format access_comment '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" $http_x_forwarded_for '
 '$upstream_response_time $request_time';

我们记录的日志类似于这样

127.0.0.1 - - [15/Feb/2017:10:30:19 +0800] "POST /get" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36" 111.111.111.111, 10.0.0.0 0.007 0.007

响应时间是最后一个。

awk的工作原理是读出一行数据,然后根据指定的分隔符对行进行分割,放到序号变量里面,默认是按照空格分割,比如$1是127.0.0.1,$2是-…

然而按照空格分割的话,这里有一个不确定的因素,就是http_user_agent,这个里面的空格是不确定的,因此没有一个固定的序号变量来保存响应时间。

不急,awk提供了一个非常好的内置变量NF,NF保存了当前分割出来的字段总数,那么最后一个则是$(NF),这样我们就能得到日志里面的响应时间。

命令如下

$ tail -f /data/logs/nginx/access.log | awk '{print $(NF)}'

实际应用中路径需要你记录nginx日志的路径。

同样我们还可以分析最近请求的平均耗时,使用NR,NR是已经读取的行数

tail -f /data/logs/nginx/access.log | awk 'BEGIN{FS=" ";count=0} {count+=$(NF); print (count/NR),"\t",$(NF)}'

awk的详细用法可以查看:http://imhuchao.com/606.html

bash学习笔记(一):变量,函数,控制流程

这是我学习bash的一个笔记。记录一下,或许能够帮助那些踩到坑的同学

变量部分

变量定义

和其他类C语言一样,bash拥有变量,定义:

a="hello" #注意,等号两边不能有空格 比如 a = "hello"这是错误的,这是我踩到的第一个坑

变量使用

变量使用需要在定义的变量名之前加上$

echo $a #将会在屏幕上打印 hello

特殊变量后面再说,现在仅仅是最基础的,能够让自己写出一个完整的脚本就行。

(更多…)

websocket协议详解

近来项目中使用websocket,于是来研究一番。websocket传输协议有两个部分,握手和数据传输

握手

GET / HTTP/1.1
HOST: <IP>:<PORT> 
Sec-Websocket-Version: 13
Sec-Websocket-Key: <KEY>
Connection: keep-alive, Upgrade
Upgrade: websocket

之后服务端会返回类似下面的数据

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <ACCEPT_KEY>
Sec-WebSocket-Version: 13
Server: swoole-websocket-server

校检连接

这里可以对accept key进行校检,确定服务器确实是websocket服务器,校检算法为

sha1(KEY+‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11’),将得到的一串字符串用16进制表示,之后再进行base64编码。 (更多…)

记录一次编译安装php7的过程,以后当做参考

之前安装过一次php7,不过配置参数听过,这次再安装,就记录一下,后面就当做参考了,主要是配置里面东西有点多。

STEP 1

去php官网下载最新版本的php,目前是7.0.10,地址:http://php.net/downloads.php#v7.0.10,可以使用下面的命令,直接下载解压

$ wget http://am1.php.net/get/php-7.0.10.tar.gz/from/this/mirror
tar -xvf mirror

STEP 2

进入刚才解压的文件,应该是 php-7.0.10。安装之前需要解决一下依赖问题。 (更多…)

murmur hash,一个更快的hash算法

在打算搭建memcache集群的时候,使用了crc3算法来对key进行hash,然而发现该算法性能比较低,于是寻找一个高性能,低碰撞的hash算,很高兴有前人已经为我们发明了这种算法——murmur。

MurmurHash算法:高运算性能,低碰撞率,由Austin Appleby创建于2008年,现已应用到Hadoop、libstdc++、nginx、libmemcached等开源系统。2011年 Appleby被Google雇佣,随后Google推出其变种的CityHash算法。

MurmurHash算法,自称超级快的hash算法,是FNV的4-5倍。官方数据如下:

OneAtATime – 354.163715 mb/sec
FNV – 443.668038 mb/sec
SuperFastHash – 985.335173 mb/sec
lookup3 – 988.080652 mb/sec
MurmurHash 1.0 – 1363.293480 mb/sec
MurmurHash 2.0 – 2056.885653 mb/sec

    //-----------------------------------------------------------------------------  
    // MurmurHash2, 64-bit versions, by Austin Appleby  
      
    // The same caveats as 32-bit MurmurHash2 apply here - beware of alignment   
    // and endian-ness issues if used across multiple platforms.  
      
    typedef unsigned long int uint64_t;  
      
    // 64-bit hash for 64-bit platforms  
    uint64_t MurmurHash64A ( const void * key, int len, unsigned int seed )  
    {  
            const uint64_t m = 0xc6a4a7935bd1e995;  
            const int r = 47;  
      
            uint64_t h = seed ^ (len * m);  
      
            const uint64_t * data = (const uint64_t *)key;  
            const uint64_t * end = data + (len/8);  
      
            while(data != end)  
            {  
                    uint64_t k = *data++;  
      
                    k *= m;   
                    k ^= k >> r;   
                    k *= m;   
      
                    h ^= k;  
                    h *= m;   
            }  
      
            const unsigned char * data2 = (const unsigned char*)data;  
      
            switch(len & 7)  
            {  
            case 7: h ^= uint64_t(data2[6]) << 48;  
            case 6: h ^= uint64_t(data2[5]) << 40;  
            case 5: h ^= uint64_t(data2[4]) << 32;  
            case 4: h ^= uint64_t(data2[3]) << 24;  
            case 3: h ^= uint64_t(data2[2]) << 16;  
            case 2: h ^= uint64_t(data2[1]) << 8;  
            case 1: h ^= uint64_t(data2[0]);  
                    h *= m;  
            };  
       
            h ^= h >> r;  
            h *= m;  
            h ^= h >> r;  
      
            return h;  
    }   
      
      
    // 64-bit hash for 32-bit platforms  
    uint64_t MurmurHash64B ( const void * key, int len, unsigned int seed )  
    {  
            const unsigned int m = 0x5bd1e995;  
            const int r = 24;  
      
            unsigned int h1 = seed ^ len;  
            unsigned int h2 = 0;  
      
            const unsigned int * data = (const unsigned int *)key;  
      
            while(len >= 8)  
            {  
                    unsigned int k1 = *data++;  
                    k1 *= m; k1 ^= k1 >> r; k1 *= m;  
                    h1 *= m; h1 ^= k1;  
                    len -= 4;  
      
                    unsigned int k2 = *data++;  
                    k2 *= m; k2 ^= k2 >> r; k2 *= m;  
                    h2 *= m; h2 ^= k2;  
                    len -= 4;  
            }  
      
            if(len >= 4)  
            {  
                    unsigned int k1 = *data++;  
                    k1 *= m; k1 ^= k1 >> r; k1 *= m;  
                    h1 *= m; h1 ^= k1;  
                    len -= 4;  
            }  
      
            switch(len)  
            {  
            case 3: h2 ^= ((unsigned char*)data)[2] << 16;  
            case 2: h2 ^= ((unsigned char*)data)[1] << 8;  
            case 1: h2 ^= ((unsigned char*)data)[0];  
                            h2 *= m;  
            };  
      
            h1 ^= h2 >> 18; h1 *= m;  
            h2 ^= h1 >> 22; h2 *= m;  
            h1 ^= h2 >> 17; h1 *= m;  
            h2 ^= h1 >> 19; h2 *= m;  
      
            uint64_t h = h1;  
      
            h = (h << 32) | h2;  
      
            return h;  
    }