那些证书相关的玩意儿(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12等)

SSL

SSL – Secure Sockets Layer,现在应该叫”TLS”,但由于习惯问题,我们还是叫”SSL”比较多.http协议默认情况下是不加密内容的,这样就很可能在内容传播的时候被别人监听到,对于安全性要求较高的场合,必须要加密,https就是带加密的http协议,而https的加密是基于SSL的,它执行的是一个比较下层的加密,也就是说,在加密前,你的服务器程序在干嘛,加密后也一样在干嘛,不用动,这个加密对用户和开发者来说都是透明的.More:[维基百科]

OpenSSL – 简单地说,OpenSSL是SSL的一个实现,SSL只是一种规范.理论上来说,SSL这种规范是安全的,目前的技术水平很难破解,但SSL的实现就可能有些漏洞,如著名的”心脏出血”.OpenSSL还提供了一大堆强大的工具软件,强大到90%我们都用不到.

证书标准
X.509 – 这是一种证书标准,主要定义了证书中应该包含哪些内容.其详情可以参考RFC5280,SSL使用的就是这种证书标准.

编码格式
同样的X.509证书,可能有不同的编码格式,目前有以下两种编码格式.

PEM – Privacy Enhanced Mail,打开看文本格式,以”—–BEGIN…”开头, “—–END…”结尾,内容是BASE64编码.
查看PEM格式证书的信息:openssl x509 -in certificate.pem -text -noout
Apache和*NIX服务器偏向于使用这种编码格式.

DER – Distinguished Encoding Rules,打开看是二进制格式,不可读.
查看DER格式证书的信息:openssl x509 -in certificate.der -inform der -text -noout
Java和Windows服务器偏向于使用这种编码格式.

相关的文件扩展名
这是比较误导人的地方,虽然我们已经知道有PEM和DER这两种编码格式,但文件扩展名并不一定就叫”PEM”或者”DER”,常见的扩展名除了PEM和DER还有以下这些,它们除了编码格式可能不同之外,内容也有差别,但大多数都能相互转换编码格式.

CRT – CRT应该是certificate的三个字母,其实还是证书的意思,常见于*NIX系统,有可能是PEM编码,也有可能是DER编码,大多数应该是PEM编码,相信你已经知道怎么辨别.

CER – 还是certificate,还是证书,常见于Windows系统,同样的,可能是PEM编码,也可能是DER编码,大多数应该是DER编码.

KEY – 通常用来存放一个公钥或者私钥,并非X.509证书,编码同样的,可能是PEM,也可能是DER.
查看KEY的办法:openssl rsa -in mykey.key -text -noout
如果是DER格式的话,同理应该这样了:openssl rsa -in mykey.key -text -noout -inform der

CSR – Certificate Signing Request,即证书签名请求,这个并不是证书,而是向权威证书颁发机构获得签名证书的申请,其核心内容是一个公钥(当然还附带了一些别的信息),在生成这个申请的时候,同时也会生成一个私钥,私钥要自己保管好.做过iOS APP的朋友都应该知道是怎么向苹果申请开发者证书的吧.
查看的办法:openssl req -noout -text -in my.csr (如果是DER格式的话照旧加上-inform der,这里不写了)

PFX/P12 – predecessor of PKCS#12,对*nix服务器来说,一般CRT和KEY是分开存放在不同文件中的,但Windows的IIS则将它们存在一个PFX文件中,(因此这个文件包含了证书及私钥)这样会不会不安全?应该不会,PFX通常会有一个”提取密码”,你想把里面的东西读取出来的话,它就要求你提供提取密码,PFX使用的时DER编码,如何把PFX转换为PEM编码?
openssl pkcs12 -in for-iis.pfx -out for-iis.pem -nodes
这个时候会提示你输入提取代码. for-iis.pem就是可读的文本.
生成pfx的命令类似这样:openssl pkcs12 -export -in certificate.crt -inkey privateKey.key -out certificate.pfx -certfile CACert.crt

其中CACert.crt是CA(权威证书颁发机构)的根证书,有的话也通过-certfile参数一起带进去.这么看来,PFX其实是个证书密钥库.

JKS – 即Java Key Storage,这是Java的专利,跟OpenSSL关系不大,利用Java的一个叫”keytool”的工具,可以将PFX转为JKS,当然了,keytool也能直接生成JKS,不过在此就不多表了.

证书编码的转换
PEM转为DER openssl x509 -in cert.crt -outform der -out cert.der

DER转为PEM openssl x509 -in cert.crt -inform der -outform pem -out cert.pem

(提示:要转换KEY文件也类似,只不过把x509换成rsa,要转CSR的话,把x509换成req…)

获得证书
向权威证书颁发机构申请证书

用这命令生成一个csr: openssl req -newkey rsa:2048 -new -nodes -keyout my.key -out my.csr
把csr交给权威证书颁发机构,权威证书颁发机构对此进行签名,完成.保留好csr,当权威证书颁发机构颁发的证书过期的时候,你还可以用同样的csr来申请新的证书,key保持不变.

或者生成自签名的证书
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
在生成证书的过程中会要你填一堆的东西,其实真正要填的只有Common Name,通常填写你服务器的域名,如”yourcompany.com”,或者你服务器的IP地址,其它都可以留空的.
生产环境中还是不要使用自签的证书,否则浏览器会不认,或者如果你是企业应用的话能够强制让用户的浏览器接受你的自签证书也行.向权威机构要证书通常是要钱的,但现在也有免费的,仅仅需要一个简单的域名验证即可.有兴趣的话查查”沃通数字证书”.

How can I tell which config file Apache is using?

$ whereis httpd
/usr/sbin/httpd
$ /usr/sbin/httpd -V
Server version: Apache/2.2.11 (Unix)
Server built:   Jun 17 2009 14:55:13
Server's Module Magic Number: 20051115:21
Server loaded:  APR 1.2.7, APR-Util 1.2.7
Compiled using: APR 1.2.7, APR-Util 1.2.7
Architecture:   64-bit
Server MPM:     Prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/prefork"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_FLOCK_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT="/usr"
 -D SUEXEC_BIN="/usr/bin/suexec"
 -D DEFAULT_PIDLOG="/private/var/run/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_LOCKFILE="/private/var/run/accept.lock"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="/private/etc/apache2/mime.types"
 -D SERVER_CONFIG_FILE="/private/etc/apache2/httpd.conf"

使用redux过程中遇到Actions must be plain objects. Use custom middleware for async actions.异常的分析

原文
原因一:使用异步actions时,没有配置redux-thunk这个中间件
中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);

上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

applyMiddleware 是Redux 的原生方法,作用是将所有中间件组成一个数组,依次执行。

原因二:调用action方法时,方法体内部并没有调用dispatch。

/**
 * 加载路由或iframe
 *
 * @param menu      需要加载的菜单
 */
export function onLoadMain(menu) {
    let routeUrl = menu.routeurl;
    let url = menu.url;
    console.log("routeUrl : ",routeUrl);
    if (routeUrl) {
        weaHistory.push({pathname: routeUrl});

        document.getElementById('e9frameMain').style.visibility = 'hidden';
        document.getElementById('e9routeMain').style.display = 'block';
        // let mainframe = document.getElementById('mainFrame');
        // mainframe.src = 'about:blank';
        document.getElementById('mainFrame').src = 'about:blank';
    } else if (url && url != 'javascript:void(0);') {
        let target = menu.target || 'mainFrame';
        if ('mainFrame' != target) {
            window.open(url, target);
        } else {
            // let mainframe = document.getElementById('mainFrame');
            // mainframe.src = url;
            document.getElementById('mainFrame').src = url;
            document.getElementById('e9frameMain').style.visibility = 'visible';
            document.getElementById('e9routeMain').style.display = 'none';
        }
    }
}

以上是一个普通的function,放在action的js文件中,如果你使用了如下方式去调用这个方法,虽然能成功调用,但在配置了redux-thunk这个中间件的情况下,你发起的任何action方法,都会走thunk这个中间件,一旦方法体内没有dispatch这个方法,则会报Actions must be plain objects. Use custom middleware for async actions这个异常。

import * as themeActions from '../../../actions/theme';

handleClick(e) {

        const {actions}=this.props;
        let urlParams= {
            iframeUrl: e.key,
            routeUrl:e.item.props.routeurl
        }

        actions.updateMenusUrl(urlParams);*/
        let menuInfo={
            routeurl:e.item.props.routeurl,
            url: e.key,
            target:'mainFrame'
        }
        //通过派生action的形式来调用这个方法
        actions. onLoadMain(menuInfo);

function mapStateToProps(state) {
    const {middleTheme} = state;

    return {
        backEndMenuUrl: middleTheme.get('backEndMenuUrl'),
        columnMenuInfo:middleTheme.get('columnMenuInfo')
    }
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(themeActions, dispatch)
    };
}

module.exports = connect(mapStateToProps, mapDispatchToProps)(E9LeftMenusTree);

所以常规function最好通过import导入的形式去调用就OK了,如下:

import {onLoadMain} from '../../../actions/theme';

handleClick(e) {

        const {actions}=this.props;
        let urlParams= {
            iframeUrl: e.key,
            routeUrl:e.item.props.routeurl
        }

        actions.updateMenusUrl(urlParams);*/
        let menuInfo={
            routeurl:e.item.props.routeurl,
            url: e.key,
            target:'mainFrame'
        }
        //常规import组件形式调用function
        onLoadMain(menuInfo);
    }

最后分享下我在解决这个问题中参考的阮大大的关于redux中间件的介绍:Redux中间件和异步操作

memcached 启动参数 和 stat 参数详解

/usr/local/bin/memcached -u root -c 50000 -t 8 -m 256 -l 192.168.1.101 -p 11211 -d
-p <num>      监听的TCP端口(默认: 11211)
-U <num>      监听的UDP端口(默认: 11211, 0表示不监听)
-s <file>     用于监听的UNIX套接字路径(禁用网络支持)
-a <mask>     UNIX套接字访问掩码,八进制数字(默认:0700)
-l <ip_addr>  监听的IP地址。(默认:INADDR_ANY,所有地址)
-d            作为守护进程来运行
-r            最大核心文件限制
-u <username> 设定进程所属用户。(只有root用户可以使用这个参数)
-m <num>      所有slab class可用内存的上限,以MB为单位。(默认:64MB)
              (译者注:也就是分配给该memcached实例的内存大小。)
-M            内存用光时报错。(不会删除数据)
-c <num>      最大并发连接数。(默认:1024)
-k            锁定所有内存页。注意你可以锁定的内存上限。
              试图分配更多内存会失败的,所以留意启动守护进程时所用的用户可分配的内存上限。
              (不是前面的 -u <username> 参数;在sh下,使用命令"ulimit -S -l NUM_KB"来设置。)
-v            提示信息(在事件循环中打印错误/警告信息。)
-vv           详细信息(还打印客户端命令/响应)
-vvv          超详细信息(还打印内部状态的变化)
-h            打印这个帮助信息并退出
-i            打印memcached和libevent的许可
-P <file>     保存进程ID到指定文件,只有在使用 -d 选项的时候才有意义
-f <factor>   不同slab class里面的chunk大小的增长倍率。(默认:1.25)
              (译者注:每个slab class里面有相同数量个slab page,每个slab page里面有chunk,且在当前slab class内的chunk大小固定。
              而不同slab class里的chunk大小不一致,具体差异就是根据这个参数的倍率在增长,直到分配的内存用尽。)
-n <bytes>    chunk的最小空间(默认:48)
              (译者注:chunk数据结构本身需要消耗48个字节,所以一个chunk实际消耗的内存是n+48。)
-L            尝试使用大内存页(如果可用的话)。提高内存页尺寸可以减少"页表缓冲(TLB)"丢失次数,提高运行效率。
              为了从操作系统获得大内存页,memcached会把全部数据项分配到一个大区块。
-D <char>     使用 <char> 作为前缀和ID的分隔符
              这个用于按前缀获得状态报告。默认是":"(冒号)
              如果指定了这个参数,则状态收集会自动开启;如果没指定,则需要用命令"stats detail on"来开启。
-t <num>      使用的线程数(默认:4)
-R            每个连接可处理的最大请求数
-C            禁用CAS
-b            设置后台日志队列的长度(默认:1024)
-B            绑定协议 - 可能值:ascii,binary,auto(默认)
-I            重写每个数据页尺寸。调整数据项最大尺寸
echo -e "stats\nquit\n" | /usr/bin/nc 192.168.1.101 11211
STAT pid 20487               ## memcache 进程PID
STAT uptime 1977637         ## 自memcache启动以来,服务器运行秒数
STAT time 1461202739       ## 服务器当前unix时间戳
STAT version 1.4.21      ## memcache 服务器版本
STAT libevent 1.4.13-stable      ## libevent 版本
STAT pointer_size 64      ##  架构(32 或 64 位)
STAT rusage_user 150.835069      ## 进程累计用户时间
STAT rusage_system 249.086133      ## 进程累计系统时间
STAT curr_connections 10      ## 当前打开连接数
STAT total_connections 5509      ## 自memcache启动以来,打开的连接总数
STAT connection_structures 11      ## 服务器分配的连接结构数
STAT reserved_fds 40      ## 
STAT cmd_get 8913248      ## 自memcache启动以来,执行get命令总数
STAT cmd_set 123382      ## 自memcache启动以来,执行set命令总数
STAT cmd_flush 0      ## 自memcache启动以来,执行flush命令总数
STAT cmd_touch 0      ## 自memcache启动以来,执行touch_all命令总数
STAT get_hits 8913074      ## 自memcache启动以来,get命中次数
STAT get_misses 174      ## 自memcache启动以来,get未命中次数
STAT delete_misses 0      ## 自memcache启动以来,delete未命中次数
STAT delete_hits 0      ## 自memcache启动以来,delete命中次数
STAT incr_misses 0      ## 自memcache启动以来,incr未命中次数
STAT incr_hits 0      ## 自memcache启动以来,incr命中次数
STAT decr_misses 0      ## 自memcache启动以来,decr未命中次数
STAT decr_hits 0      ## 自memcache启动以来,decr命中次数
STAT cas_misses 0      ## 自memcache启动以来,cas未命中次数
STAT cas_hits 0      ## 自memcache启动以来,cas命中次数
STAT cas_badval 0      ## 使用擦拭次数
STAT touch_hits 0      ## 自memcache启动以来,touch命中次数
STAT touch_misses 0      ## 自memcache启动以来,touch未命中次数
STAT auth_cmds 0      ##
STAT auth_errors 0      ##
STAT bytes_read 111225505      ## memcached服务器从网络读取的总的字节数
STAT bytes_written 3621054898      ## memcached服务器发送到网络的总的字节数
STAT limit_maxbytes 33554432      ## memcached服务缓存允许使用的最大字节数(分配的内存数)
STAT accepting_conns 1      ## 目前接受的链接数
STAT listen_disabled_num 0      ##
STAT threads 8      ## 被请求的工作线程的总数量
STAT conn_yields 0      ## 连接操作主动放弃数目
STAT hash_power_level 16      ##
STAT hash_bytes 524288      ##
STAT hash_is_expanding 0      ##
STAT malloc_fails 0      ## 
STAT bytes 384154      ## 存储item字节数(当前存储占用的字节数)
STAT curr_items 856      ## item个数(当前存储的数据总数)
STAT total_items 123382      ## item总数(启动以来存储的数据总数)
STAT expired_unfetched 0      ##
STAT evicted_unfetched 0      ##
STAT evictions 0      ## LRU释放的对象数目。为了给新的数据项目释放空间,从缓存移除的缓存对象的数目。比如超过缓存大小时根据LRU算法移除的对象,以及过期的对象
STAT reclaimed 0      ## 已过期的数据条目来存储新数据的数目
STAT crawler_reclaimed 0      ##
STAT lrutail_reflocked 0      ##
END

判断一个点是否在一个多边形里

“判断一个点是否在一个多边形里”,一开始以为是个挺难的问题,但Google了一下之后发现其实蛮简单,所用到的算法叫做“Ray-casting Algorithm”,中文应该叫“光线投射算法”,这是维基百科的描述:[维基百科]

简单地说可以这么判断:从这个点引出一根“射线”,与多边形的任意若干条边相交,累计相交的边的数目,如果是奇数,那么点就在多边形内,否则点就在多边形外。

阅读详细

from collections import namedtuple
from pprint import pprint as pp
import sys
 
Pt = namedtuple('Pt', 'x, y')               # Point
Edge = namedtuple('Edge', 'a, b')           # Polygon edge from a to b
Poly = namedtuple('Poly', 'name, edges')    # Polygon
 
_eps = 0.00001
_huge = sys.float_info.max
_tiny = sys.float_info.min
 
def rayintersectseg(p, edge):
    ''' takes a point p=Pt() and an edge of two endpoints a,b=Pt() of a line segment returns boolean
    '''
    a,b = edge
    if a.y > b.y:
        a,b = b,a
    if p.y == a.y or p.y == b.y:
        p = Pt(p.x, p.y + _eps)
 
    intersect = False
 
    if (p.y > b.y or p.y < a.y) or ( p.x > max(a.x, b.x)):
        return False
 
    if p.x < min(a.x, b.x): intersect = True else: if abs(a.x - b.x) > _tiny:
            m_red = (b.y - a.y) / float(b.x - a.x)
        else:
            m_red = _huge
        if abs(a.x - p.x) > _tiny:
            m_blue = (p.y - a.y) / float(p.x - a.x)
        else:
            m_blue = _huge
        intersect = m_blue >= m_red
    return intersect
 
def _odd(x): return x%2 == 1
 
def ispointinside(p, poly):
    ln = len(poly)
    return _odd(sum(rayintersectseg(p, edge)
                    for edge in poly.edges ))
 
def polypp(poly):
    print "\n  Polygon(name='%s', edges=(" % poly.name
    print '   ', ',\n    '.join(str(e) for e in poly.edges) + '\n    ))'
 
if __name__ == '__main__':
    polys = [
      Poly(name='square', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=0, y=0))
        )),
      Poly(name='square_hole', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=0, y=0)),
        Edge(a=Pt(x=2.5, y=2.5), b=Pt(x=7.5, y=2.5)),
        Edge(a=Pt(x=7.5, y=2.5), b=Pt(x=7.5, y=7.5)),
        Edge(a=Pt(x=7.5, y=7.5), b=Pt(x=2.5, y=7.5)),
        Edge(a=Pt(x=2.5, y=7.5), b=Pt(x=2.5, y=2.5))
        )),
      Poly(name='strange', edges=(
        Edge(a=Pt(x=0, y=0), b=Pt(x=2.5, y=2.5)),
        Edge(a=Pt(x=2.5, y=2.5), b=Pt(x=0, y=10)),
        Edge(a=Pt(x=0, y=10), b=Pt(x=2.5, y=7.5)),
        Edge(a=Pt(x=2.5, y=7.5), b=Pt(x=7.5, y=7.5)),
        Edge(a=Pt(x=7.5, y=7.5), b=Pt(x=10, y=10)),
        Edge(a=Pt(x=10, y=10), b=Pt(x=10, y=0)),
        Edge(a=Pt(x=10, y=0), b=Pt(x=2.5, y=2.5))
        )),
      Poly(name='exagon', edges=(
        Edge(a=Pt(x=3, y=0), b=Pt(x=7, y=0)),
        Edge(a=Pt(x=7, y=0), b=Pt(x=10, y=5)),
        Edge(a=Pt(x=10, y=5), b=Pt(x=7, y=10)),
        Edge(a=Pt(x=7, y=10), b=Pt(x=3, y=10)),
        Edge(a=Pt(x=3, y=10), b=Pt(x=0, y=5)),
        Edge(a=Pt(x=0, y=5), b=Pt(x=3, y=0))
        )),
      ]
    testpoints = (Pt(x=5, y=5), Pt(x=5, y=8),
                  Pt(x=-10, y=5), Pt(x=0, y=5),
                  Pt(x=10, y=5), Pt(x=8, y=5),
                  Pt(x=10, y=10))
 
    print "\n TESTING WHETHER POINTS ARE WITHIN POLYGONS"
    for poly in polys:
        polypp(poly)
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[:3])
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[3:6])
        print '   ', '\t'.join("%s: %s" % (p, ispointinside(p, poly))
                               for p in testpoints[6:])
public static class RayCastingAlgorithm {
        public static bool IsWithin(Point pt, IList<Point> polygon, bool noneZeroMode) {
            int ptNum = polygon.Count();
            if (ptNum < 3) {
                return false;
            }
            int j = ptNum - 1;
            bool oddNodes = false;
            int zeroState = 0;
            for (int k = 0; k < ptNum; k++) { Point ptK = polygon[k]; Point ptJ = polygon[j]; if (((ptK.Y > pt.Y) != (ptJ.Y > pt.Y)) && (pt.X < (ptJ.X - ptK.X) * (pt.Y - ptK.Y) / (ptJ.Y - ptK.Y) + ptK.X)) { oddNodes = !oddNodes; if (ptK.Y > ptJ.Y) {
                        zeroState++;
                    }
                    else {
                        zeroState--;
                    }
                }
                j = k;
            }
            return noneZeroMode?zeroState!=0:oddNodes;
        }
    }

mysql主从配置

开启mysql只读模式:

FLUSH TABLES WITH READ LOCK;
SET GLOBAL read_only = 1;

查看主数据库状态:
show master status; //可以查看主mysql状态

备份主数据库:

mysqldump -h -ujbbwbu -p jbbwb > jbbwb.sql

导入备份文件到从数据库:

source jbbwb.sql

设置同步点:
change master to
-> master_host=’192.168.78.128′,
-> master_user=’test’,
-> master_password=’MyPass1!’,
-> master_log_file=’mysql-bin.000001′,
-> master_log_pos=1244;

start slave;

show slave status \G;

关闭mysql只读模式:
SET GLOBAL read_only = 0;
UNLOCK TABLES;

ubuntu清理磁盘空间

  1. df -h:显示磁盘占用

  2. apt-get autoclean:清理旧版本缓存

  3. apt-get clean:清理所有缓存

  4. apt-get autoremove:移除孤立软件(可以清理出500M+的空间)

  5. rm -rf /var/log/*:删除所有log(删了后last命令提示找不到一个/var/log下的文件。。。)

  6. find / -size +100M:列出所有大于100M的文件运行一遍后,把快照大小从3G+降到了2.5G,但相比我刚装好所有软件时的2.1G的快照还是大了蛮多

How to Read the PHP Slow Request Log

The PHP slow request log is where PHP records information about any request that takes more than five seconds to execute.

Having slow PHP code not only creates a bad experience for your site’s visitors, but it is also expensive for you. An app with fast PHP code only needs a single PHP process to handle many requests each second. However, since each PHP process can only execute one request at a time, when a PHP script is slow, the app needs additional PHP processes to handle the requests. Each additional process uses more memory, so having slow code means your server needs a lot more memory.

If your server has high memory usage or requests that are timing out, you may have slow requests that you can debug by reading the PHP slow request log.

Location of the PHP Slow Request Log

On the ServerPilot Business plan, you can view the PHP slow request log from each app’s Logs tab in ServerPilot.

If you’re not on the Business plan, you can SSH or SFTP into your server to view the PHP slow request log.

Each app has its own PHP slow request log. The log file is located under the app’s system user’s home directory at:

log/APPNAME/APPNAME_phpX.Y.slow.log

where phpX.Y is the PHP version the app is using, for example php7.0.

Understanding the PHP Slow Request Log

The PHP slow request log records stack traces (also known as tracebacks) of each slowly executing script at the moment the request passed five seconds in execution.

For example, an entry in the slow request log might look like this:

[06-Oct-2015 12:20:34]  [pool example] pid 1852
script_filename = /srv/users/serverpilot/apps/example/public/index.php
[0x00007f7e475ba138] curl_exec() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:44
[0x00007f7e475b9f10] curl() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:20
[0x00007f7e475b9d48] request() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request.php:63
[0x00007f7e475b9be0] perform() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:941
[0x00007f7e475b99f8] request() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:1098
[0x00007f7e475b98b8] updateTrackingCode() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/TrackingCode.php:16
[0x00007f7e475b9720] __construct() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik.php:288
[0x00007fffa8cdcac0] addJavascriptCode() unknown:0
[0x00007f7e475b9540] call_user_func_array() /srv/users/serverpilot/apps/example/public/wp-includes/plugin.php:503
[0x00007f7e475b9428] do_action() /srv/users/serverpilot/apps/example/public/wp-includes/general-template.php:2284
[0x00007f7e475b9358] wp_footer() /srv/users/serverpilot/apps/example/public/wp-content/themes/k2/footer.php:17
[0x00007f7e475b9358] +++ dump failed

The first two lines tell you the time of the slow request and which script was initially requested. In the above example, these first two lines are:

[06-Oct-2015 12:20:34]  [pool example] pid 1852
script_filename = /srv/users/serverpilot/apps/example/public/index.php

The third line (the line immediately below the first two lines) is the top of the stack trace. This tells you the currently executing function at the time the script passed five seconds in execution. In the example above, the top of the stack trace is:

[0x00007f7e475ba138] curl_exec() /srv/users/serverpilot/apps/example/public/wp-content/plugins/wp-piwik/classes/WP_Piwik/Request/Rest.php:44

The three parts of this line are a unique identifier for the request ([0x00007f7e475ba138]), the name of the executing function (curl_exec()), and the file name and line number of that function (/srv/users/.../Request/Rest.php:44).

The top of the stack trace is generally the cause of your script’s slowness. The rest of the lines below that tell you each function call that led up to that point.

Note that you will sometimes see +++ dump failed at the bottom of a stack trace. That indicates the end of the stack trace and does not indicate a problem.

If you see a WordPress plugin name in the slow request log, you may be able to resolve your app’s slow request problems by disabling the plugin.

Common Causes of Slow Requests

There are a few common causes of slow requests.

Slow Requests Due to curl_exec()

If you see the curl_exec() function in your PHP slow request log, the problem is not a bug in cURL but rather that the request is taking a long time to complete.

When your scripts rely on making requests to external services and other websites, your own site can become slow and unreliable due to problems with the other website. For example, if their website is slow or is rate-limiting your requests, your own app will become very slow. These issues can be resolved by disabling plugins that make external requests and not writing your own PHP code to perform HTTP requests to external websites or services.

Slow Requests Due to mysqli_query()

If you see the mysqli_query() function in your PHP slow request log, the problem is not a bug in MySQL or any MySQL settings but rather that your PHP code is executing slow SQL queries.

You can have slow SQL queries due to, for example, lack of proper indexes in your database tables or PHP code that is inefficiently using the databases. The solution in this case is to track down the particular queries that are slow and fix them.

Acceptable Cases of Slow Requests

There is one acceptable case of slow requests where you can safely ignore entries in the PHP slow request log: slow requests that are not being performed when answering a website visitor’s request but rather are scheduled requests being performed in the background. Common example of such requests are requests for wp-cron.php or any requests you may have scheduled using cron.

pureComponent vs Component vs Stateless Functional component的区别

1,createClass,没有使用es6语法的时候,定义组件,就使用React.createClass来定义组件

var React = require("react");
var Greeting = React.createClass({
  
  propTypes: {
    name: React.PropTypes.string //属性校验
  },

  getDefaultProps: function() {
    return {
      name: 'Mary' //默认属性值
    };
  },
  
  getInitialState: function() {
    return {count: this.props.initialCount}; //初始化state
  },
  
  handleClick: function() {
    //用户点击事件的处理函数
  },

  render: function() {
    return <h1>Hello, {this.props.name}</h1>;
  }
});
module.exports = Greeting;

在createClass中,React对属性中的所有函数都进行了this绑定,也就是上面的handleClick其实相当于handleClick.bind(this);

2, component,因为es6对类和继承有语法级别的支持,所以用es6创建组件的方式更为优雅

import React from 'react';
class Greeting extends React.Component {

  constructor(props) {
    super(props); //调用父类的构造函数
    this.state = {count: props.initialCount};
    this.handleClick = this.handleClick.bind(this);
  }
  
  //static defaultProps = {
  //  name: 'Mary'  //定义defaultprops的另一种方式
  //}
  
  //static propTypes = {
    //name: React.PropTypes.string
  //}
  
  handleClick() {
    //点击事件的处理函数
  }
  
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Greeting.propTypes = {
  name: React.PropTypes.string
};

Greeting.defaultProps = {
  name: 'Mary'
};
export default Greating;

3,pureComponent
我们知道,当组件的props或者state发生变化时,React就会对当前的props和state分别与nextProps和nextState进行比较,当发生变化时,就会对当前组件或子组件进行重新渲染,否则就不渲染,有时候为了避免组件进行不必要的重新渲染,我们通过shouldComponentUpdate来优化性能

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

shouldComponentUpdate通过判断props.color和state.count是否发生变化来重新渲染组件,React提供了PureComponent来自动帮我们做这件事,

class CounterButton extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

4,Stateless Functional Component
上面提到的是用来创建包含状态和用户交互的复杂组件,当组件本身只是用来展示,所有数据都是通过props传入的时候,我们便可以使用Stateless Functional Component来快速创建组件

import React from 'react';
const Button = ({
  day,
  increment
}) => {
  return (
    <div>
      <button onClick={increment}>Today is {day}</button>
    </div>
  )
}

Button.propTypes = {
  day: PropTypes.string.isRequired,
  increment: PropTypes.func.isRequired,
}

选哪个
createClass, 除非你确实对ES6的语法一窍不通,不然的话就不要再使用这种方式定义组件。

Stateless Functional Component, 对于不需要内部状态,且用不到生命周期函数的组件,我们可以使用这种方式定义组件,比如展示性的列表组件,可以将列表项定义为Stateless Functional Component。

PureComponent/Component,对于拥有内部state,使用生命周期的函数的组件,我们可以使用二者之一,但是大部分情况下,我更推荐使用PureComponent,因为它提供了更好的性能,同时强制你使用不可变的对象,保持良好的编程习惯。