centos 下安装 Let’s Encrypt 永久免费 SSL 证书

lets encrypt github地址为:https://github.com/certbot/certbot

1.

git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
chmod +x letsencrypt-auto

 

./letsencrypt-auto certonly  --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com

-d参数后面对应的是域名,在执行的过程中,我遭遇了下面的报错:

Total size: 44 M
Downloading Packages:
Running rpm_check_debug
ERROR with rpm_check_debug vs depsolve:
libgdbm.so.2()(64bit) is needed by python-libs-2.6.6-66.el6_8.x86_64
** Found 7 pre-existing rpmdb problem(s), 'yum check' output follows:
4:perl-5.10.1-141.el6_7.1.x86_64 has missing requires of libgdbm.so.2()(64bit)
4:perl-devel-5.10.1-141.el6_7.1.x86_64 has missing requires of gdbm-devel
polkit-0.96-5.el6_4.x86_64 has missing requires of libeggdbus-1.so.0()(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16()(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of libmysqlclient.so.16(libmysqlclient_16)(64bit)
2:postfix-2.6.6-6.el6_5.x86_64 has missing requires of mysql-libs
python-libs-2.6.6-52.el6.x86_64 has missing requires of libgdbm.so.2()(64bit)
Your transaction was saved, rerun it with: yum load-transaction /tmp/yum_save_tx-2017-04-07-22-1798AqLE.yumtx
Could not install OS dependencies. Aborting bootstrap!

centos 6 需要安装 libgdbm.so.2

wget http://mirror.centos.org/centos/6/os/x86_64/Packages/gdbm-1.8.0-39.el6.x86_64.rpm
yum localinstall gdbm-1.8.0-39.el6.x86_64.rpm

完成后,重新执行上面的命令行:

./letsencrypt-auto certonly --standalone  --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com -d img.appfront.fancyecommerce.com -d img.apphtml5.fancyecommerce.com -d img.fancyecommerce.com

 

下面的安装就比较顺利了,下面是我的log

[root@iZ942k2d5ezZ certbot-master]# ./letsencrypt-auto certonly   --email 2358269014@qq.com -d fecshop.appfront.fancyecommerce.com -d fecshop.appfront.es.fancyecommerce.com
/root/.local/share/letsencrypt/lib/python2.6/site-packages/cryptography/__init__.py:26: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6
  DeprecationWarning
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Failed to find executable apachectl in PATH: /usr/local/mysql/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

How would you like to authenticate with the ACME CA?
-------------------------------------------------------------------------------
1: Place files in webroot directory (webroot)
2: Spin up a temporary webserver (standalone)
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for fecshop.appfront.fancyecommerce.com
tls-sni-01 challenge for fecshop.appfront.es.fancyecommerce.com

-------------------------------------------------------------------------------
Could not bind TCP port 443 because it is already in use by another process on
this system (such as a web server). Please stop the program in question and then
try again.
-------------------------------------------------------------------------------
(R)etry/(C)ancel: R

-------------------------------------------------------------------------------
Could not bind TCP port 443 because it is already in use by another process on
this system (such as a web server). Please stop the program in question and then
try again.
-------------------------------------------------------------------------------
(R)etry/(C)ancel: R
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem.
   Your cert will expire on 2017-07-06. To obtain a new or tweaked
   version of this certificate in the future, simply run
   letsencrypt-auto again. To non-interactively renew *all* of your
   certificates, run "letsencrypt-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

[root@iZ942k2d5ezZ certbot-master]# ls

在nginx中添加代码:

listen 443 ssl;
        ssl on;
        ssl_certificate /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

注意上面的文件路径 ,将 fecshop.appfront.fancyecommerce.com 替换成您的文件路径。因为不同的域名生成的文件名字不同,

nginx填写完的代码如下:

server {
        listen     80  ;
        listen 443 ssl;
        ssl on;
        ssl_certificate /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/privkey.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server_name fecshop.appfront.fancyecommerce.com fecshop.appfront.es.fancyecommerce.com;
    root  /www/web/develop/fecshop/appfront/web;
        server_tokens off;
    include none.conf;
    index index.php index.html index.htm;
    access_log /www/web_logs/access.log wwwlogs;
    error_log  /www/web_logs/error.log  notice;
    location ~ \.php$ {
                        fastcgi_pass   127.0.0.1:9000;
                        fastcgi_index  index.php;
                        include fcgi.conf;
        }

        location ~ /sitemap.xml
        {
                if ($host  ~ .*appfront.es.fancyecommerce.com) {
                        rewrite ^/sitemap\.xml /sitemap_es.xml last;
                }
        }

         location /fr/ {
                index index.php;
                if (!-e $request_filename){
                        rewrite . /fr/index.php last;
                  }
        }
         location /es/ {
                index index.php;
                if (!-e $request_filename){
                        rewrite . /es/index.php last;
                }
        }

         location /cn/ {
                index index.php;
                if (!-e $request_filename){
                        rewrite . /cn/index.php last;
                }
        }

         location /de/ {
                index index.php;
                if (!-e $request_filename){
                        rewrite . /de/index.php last;
                }
        }

        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
                expires      30d;
        }

        location ~ .*\.(js|css)?$ {
                expires      12h;
        }
        location /api {
                rewrite /api/([a-z][0-9a-z_]+)/?$ /api.php?type=$1;
        }


}

 

重启nginx,就可以访问https了

2自动续签

lets encrypt证书有三个月的到期时间。可以自动renew,步骤如下:

官方资料:https://certbot.eff.org/docs/using.html?highlight=renew#renewing-certificates

改写到期更新时间

vim /etc/letsencrypt/renewal/fecshop.appfront.fancyecommerce.com.conf

fecshop.appfront.fancyecommerce.com.conf是您生成的名字,这个根据您自己域名,不是我的这个名字,打开这个文件,
将第一行的注释去掉,修改为89days

renew_before_expiry = 89 days
version = 0.13.0

89days代表,多少天之前算过期,证书过期时间为90天,如果我设置了89,那么第二天就需要renew了,
如果您设置了30天,那么就是2个月后需要renew,如果时间没到,renew会提示失败。

改好文件后,进入letsencrypt 文件夹,执行

./certbot-auto renew   --force-renewal   --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start"

`–force-renewal`: 代表强制renew
`–pre-hook`: 是nginx关闭的命令,因为renew需要关闭nginx
`–post-hook`: 是nginx开启的命令,搞完证书后,开启nginx

自动续签可以使用crontab来完成,在linux中执行  crontab -e
添加代码:(www/web_logs/letsencry.log 为日志文件,您需要新建这个文件并设置可写)

06 06 * * * /www/web/test/lets/certbot-master/certbot-auto renew   --force-renewal   --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start" >> /www/web_logs/letsencry.log 2>&1

下面是我的日志:

[root@iZ942k2d5ezZ certbot-master]# ./certbot-auto renew   --force-renewal   --pre-hook "/etc/init.d/nginx stop" --post-hook "/etc/init.d/nginx start"


/root/.local/share/letsencrypt/lib/python2.6/site-packages/cryptography/__init__.py:26: DeprecationWarning: Python 2.6 is no longer supported by the Python core team, please upgrade your Python. A future version of cryptography will drop support for Python 2.6
  DeprecationWarning
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/fecshop.appfront.fancyecommerce.com.conf
-------------------------------------------------------------------------------
Running pre-hook command: /etc/init.d/nginx stop
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for fecshop.appfront.fancyecommerce.com
tls-sni-01 challenge for fecshop.appfront.es.fancyecommerce.com
tls-sni-01 challenge for img.appfront.fancyecommerce.com
tls-sni-01 challenge for img.apphtml5.fancyecommerce.com
tls-sni-01 challenge for img.fancyecommerce.com
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0003_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0003_csr-certbot.pem

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem
-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/fecshop.appfront.fancyecommerce.com/fullchain.pem (success)
Running post-hook command: /etc/init.d/nginx start
[root@iZ942k2d5ezZ certbot-master]#

 

3.开启http2

可以更快的加载https,关于http2的资料为:http://www.jianshu.com/p/47d02f10757f

nginx安装的时候需要添加:

./configure   --with-http_v2_module

然后在nginx的ssl配置将listen 443 ssl ;改为:

listen 443 ssl http2;

这样就开启http2了,http2可以更快的加载。

http2的资料:

http://www.infoq.com/cn/news/2015/02/https-spdy-http2-comparison/

4.强制使用https,http访问进行跳转

server {
           listen       80;
           server_name  fecshop.appfront.fancyecommerce.com fecshop.appfront.es.fancyecommerce.com;
           rewrite ^(.*)$ https://$host$1 permanent;

}

 

其他参考地址:https://www.v2ex.com/t/310130

 

Fecshop 环境部署 以及 安装步骤

一:安装Fecshop的环境部署:

本文只讲解在Linux下面部署安装Fecshop的步骤,在windows下面不做阐述(需要安装的东西太多,windows下面部署我没有玩过,遇到的问题也不能提供好的解决方式,故而不做阐述),对于Linux开发环境,可以在window下虚拟一个linux,文档参看: 关于vagrant的安装详情参看

本文是在centos 6下面安装环境的详细步骤。

1.更新linux

yum update

2.yum安装基础

yum  install ntp vim-enhanced gcc gcc-c++ gcc-g77 flex bison autoconf automake glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel libtool* zlib-devel libxml2-devel libjpeg-devel libpng-devel libtiff-devel fontconfig-devel freetype-devel libXpm-devel gettext-devel curl curl-devel pam-devel e2fsprogs e2fsprogs-devel krb5 krb5-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel nss_ldap openldap-clients openldap-servers gzip  make bzip2bzip2-devel pcre-devel wget ncurses-devel cmake make perl
yum -y install gcc automake autoconf libtool make   gcc-c++ glibc  libmcrypt-devel mhash-devel libxslt-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel lib
yum -y install gcc gcc-c++
yum install  libevent*  libtool* autoconf* libstd* ncurse* bison* openssl*  

1.2 如果在安装 libevent*的时候报错:

libevent2-devel conflicts with libevent-devel-1.4.13-4.el6.x86_64

则使用下面的安装方式

yum install  libevent2  libtool* autoconf* libstd* ncurse* bison* openssl*

查看文件是否存在

ls /usr/lib/libncurses.so

如果找不到,则

find / -name ‘libncurses.so’

我找到的文件如下:

/usr/lib64/libncurses.so

增加快捷方式

ln -s /usr/lib64/libncurses.so /usr/lib/libncurses.so

在开始安装之前,需要下载下面的这些软件,你可以通过我的打包一次下载下面,解压到您的/tools文件夹下面使用,所有的软件的下载地址为(百度云盘):

http://pan.baidu.com/s/1kVwRD2Z,进入下载:linux环境安装所需软件.zip

当然您可以自己去相应官网下载相应的软件。

2.开始安装mysql

安装教程为:

linux 安装mysql5.6

在上述安装中,遇到yum安装的地方可以直接忽略掉,因为上面已经安装了。

3.安装php 以及扩展

php有php5和php7,两种版本,下面都给予了安装文档,建议安装php7,性能比较快

3.1 php7的安装

安装php7以及扩展教程为:

php 7 环境安装

3.2 php5的安装

3.2.1 安装php5.4教程为:

Linux 安装php 5.4 – yii2 环境

在上述安装中,遇到yum安装的地方可以直接忽略掉,因为上面已经安装了。

3.2.2 安装php5.4的扩展

安装教程为:

Linux php 扩展安装 mongo ,redis ,soap,imap,pdo_mysql,oauth

注意:上面的安装php5.4扩展的教程中,有一个跳转到mongodb的教程,只需要执行里面的步骤1, 2, 3, 4.2 这几个步骤即可。步骤1,2,3是安装mongodb的步骤,步骤4.2是安装mongodb扩展的步骤,不需要安装php-mongo扩展

安装完成后重启php

/etc/init.d/php-fpm restart

 

5.安装redis

安装教程为:

yii2 – redis 配置

注意:只需要执行步骤1,2就可以了。

redis关闭命令:/etc/init.d/redis_6379 stop

redis开启命令:/etc/init.d/redis_6379 start

6.安装nginx

安装教程为:

Linux – 安装nginx – 搭配YII2环境

关于nginx的配置,您可以在上面,在百度云盘下载将环境配置里面

https://pan.baidu.com/s/1kVwRD2Z

下载 “环境配置文件.rar”,解压后,将里面的文件覆盖到linux的根目录。这里面也有php的配置,覆盖即可。

然后在 vim /usr/local/nginx/conf/conf.d/default.conf 修改里面的配置即可。

7.php设置快捷方式:

ln -s  /usr/local/php/bin/php  /usr/bin/php

 

8.linux一般安装在内部或者远程服务器,当然也可以通过vagrant安装虚拟机的方式,我们开发的时候都不需要用vim编辑,喜欢win下面的编辑器,那么可以通过下面的方式,通过编辑器的ftp功能远程加载文件的方式,教程如下:

Linux 作为开发环境的方法分享

9.其他安装推荐:(非必须安装)

安装ElasticSearch ,以及在yii2中的使用

YII2开发环境文件上传 – 安装vsftpd

linux防火墙 – apf 安装 ,配置,使用

php 安装 zend opcace

 

 

Yii2 Mongodb ActiveRecord 添加索引

public static function create_index(){
  $indexs = [
    ['cid' => -1],
    ['market_person' => -1],
    ['created_at_date' => -1,'market_person' => -1],
  ];
  
  $options = ['background' => true, 'socketTimeoutMS' => 300000];
  foreach($indexs as $columns){
    self::getCollection()->createIndex($columns,$options);
  }
}

$indexs里面的每一个数组就是一个索引,

$options 里面的background代表后台执行的意思,因为添加索引,如果不加这个选项,如果表很大,要执行几十分钟,那么库会被加锁。

Fecshop 功能添加部分

1.产品加入一个字段,收藏次数,客户收藏后,个数+1,客户取消后,收藏-1
2.后台加入一个产品收藏列表展示,只查看即可
3.在后台 , 展示前台的用户,在后台可以编辑前台用户信息
4.产品加入一个按照收藏个数排序的功能。
5.产品加入一个相关产品的列表,然后在前台产品页面可以调取出来
6.交叉产品,在购物车页面展示,也就是产品在加入购物车后,根据购物车的产品,展示对应的产品。

随着滚动条滚动的Tab切换 – js例子

在电商网站,我们有一些tab 想要他,随着下拉,tab自动切换的方式,

这样无论对seo还是客户体验都很不错,下面是实现的一个例子,代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml">  
<head>  
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
<title>随着滚动条滚动的Tab切换</title>  
<style>  
*{  
    list-style:none;  
    margin:0;  
    padding:0;  
    text-decoration:none;  
    font-family:'Microsoft YaHei';  
  
}  
li{  
    width:100px;  
    height:50px;  
    line-height:50px;  
    float:left;  
    border-right:2px solid #eee;  
    text-align:center;  
    cursor:pointer;  
}  
ul{  
    width:1200px;     
    margin:0 auto;  
}  
.nav{  
    height:52px;  
    width:100%;  
    background:#f5f5f5;  
}  
.nav .cur{  
    background:#fff;  
    border-top:2px solid #1a92cf;  
    color:#1a92cf;  
}  
.fixed{  
    position:fixed;  
    top:0;  
    left:0;  
}  
a{  
    color:#505050;  
}  
</style>  
</head>  
<body>  
<br/><br/><br/><br/><br/><br/><br/>  
<div class="nav" id="nav-container">  
    <ul id="nav-box">
        <li class="cur">text1-nav</li>  
        <li>text2-nav</li>  
        <li>text3-nav</li>  
    </ul>  
</div>  
<div id="text">  
    <div style="width:100%;height:500px;background:green;text-align:center;">text1</div>  
    <div style="width:100%;height:500px;background:yellow;text-align:center;">text2</div>  
    <div style="width:100%;height:500px;background:blue;text-align:center;">text3</div>   
</div>  
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>  
  
<script>  
var navContainer = document.getElementById("nav-container");  
var navBox = document.getElementById("nav-box");  
var text = document.getElementById("text");  
var navBoxChild = navBox.children;  
var textChild = text.children;  
var num = navContainer.offsetTop;  
var a = navContainer.offsetHeight;  
window.onscroll = function(){  
    var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;  
    if(scrollTop >= num){  
        navContainer.className = "nav fixed";  
        text.style.paddingTop = a +"px";  
    }else{  
        navContainer.className = "nav";  
        text.style.paddingTop = "";  
    }  
    //当导航与相应文档接触的时候自动切换  
    //method1  
    for(var i=0;i<navBoxChild.length;i++){  
        if( scrollTop + a >= textChild[i].offsetTop){  
            for(var j=0;j<navBoxChild.length;j++){  
                navBoxChild[j].className = "";  
            }  
            navBoxChild[i].className = "cur";  
       }  
    }  
};  
for(var i=0;i<navBoxChild.length;i++){  
    var interval;  
    navBoxChild[i].index = i;  
    navBoxChild[i].onclick = function(){  
        var self = this;  
        clearInterval(interval);  
    if(document.body.scrollTop){
      scroll = document.body;
    }else if(document.documentElement.scrollTop){
      scroll = document.documentElement;
    }
        interval = setInterval(function(){  
      if(scroll.scrollTop + a<=textChild[self.index].offsetTop){  
        scroll.scrollTop += 40;  
        if(scroll.scrollTop + a>=textChild[self.index].offsetTop){  
          scroll.scrollTop = textChild[self.index].offsetTop-a;  
          clearInterval(interval);  
        }  
      }else{  
        scroll.scrollTop /= 1.1;  
        if(scroll.scrollTop + a<=textChild[self.index].offsetTop){  
          scroll.scrollTop = textChild[self.index].offsetTop-a;  
          clearInterval(interval);  
        }  
      }  
    },40); 
    };  
}  
</script>    
</body>  
</html>

 

Geoip geoip-api-php 库包使用 – 通过ip 找到国家

通过js 接收了用户的ip,我们需要通过ip得到用户的国家,可以通过一个库包来实现,下面是详细:
1.下载geoip的数据库:

cd geoip/  
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz  
gzip -d GeoIP.dat.gz  
ls  
GeoLiteCity.dat

 

其他库: 查看

GeoLite免费数据库

 

2. 下载php库包:

wget http://www.maxmind.com/download/geoip/api/php/php-latest.tar.gz  
unzip php-latest.tar.gz

3.使用:

include("/www/web/develop/marketsystem/common/lib/geoip-api-php-1.14/src/geoip.inc");  
  
//得到国家。通过ip  
function getCountryByIp($ip){  
    $gi = geoip_open("/www/web/develop/marketsystem/common/lib/geoip/GeoIP.dat", GEOIP_STANDARD);  
    if(strstr($ip,":")){  
        $country_code = geoip_country_code_by_addr_v6($gi,$ip);  
        $country_name = geoip_country_name_by_addr_v6($gi,$ip);  
    }else{  
        $country_code = geoip_country_code_by_addr($gi,$ip);  
        $country_name = geoip_country_name_by_addr($gi,$ip);  
    }  
    geoip_close($gi);  
    return [  
        "country_code" => $country_code,  
        "country_name" => $country_name,  
    ];  
}

 

类似谷歌,在网站里面嵌入js,接收各种浏览器信息以及网站信息

需要3个步骤:js 收集,调动接口, php接收,通过ip找到国家,然后存储

1.

需要在网页里面嵌入js代码:

<script type="text/javascript">
  var _maq = _maq || [];
  _maq.push(['website_id', '3']);
  (function() {
    var ma = document.createElement('script'); ma.type = 'text/javascript'; ma.async = true;
    ma.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'static.tomtop-cdn.com/tomtop/js/trace.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ma, s);
  })();
</script>

上下面的代码,是在每一个页面都需要加入,您可以加入到页面的底部

如果您想收集某些页面的单独的数据,譬如:产品页面的sku,你可以加入下面的代码,这个代码必须在上面的代码的前面:

<script type="text/javascript">
  var _maq = _maq || [];
  _maq.push(['sku', '$sku']);
</script>

一定要注意,这个代码一定要在上面

如果传递购物车信息,可以用json个数传递

2. 全局加入的js代码可以看到有个trace.js    这个js文件就是下面的内容

(function () {
  
  var nVer = navigator.appVersion;
  var nAgt = navigator.userAgent;
  var browserName  = navigator.appName;
  var fullVersion  = ''+parseFloat(navigator.appVersion); 
  var majorVersion = parseInt(navigator.appVersion,10);
  var nameOffset,verOffset,ix;

  // In Opera, the true version is after "Opera" or after "Version"
  if ((verOffset=nAgt.indexOf("Opera"))!=-1) {
   browserName = "Opera";
   fullVersion = nAgt.substring(verOffset+6);
   if ((verOffset=nAgt.indexOf("Version"))!=-1) 
     fullVersion = nAgt.substring(verOffset+8);
  }
  // In MSIE, the true version is after "MSIE" in userAgent
  else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {
   browserName = "Microsoft Internet Explorer";
   fullVersion = nAgt.substring(verOffset+5);
  }
  // In Chrome, the true version is after "Chrome" 
  else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {
   browserName = "Chrome";
   fullVersion = nAgt.substring(verOffset+7);
  }
  // In Safari, the true version is after "Safari" or after "Version" 
  else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {
   browserName = "Safari";
   fullVersion = nAgt.substring(verOffset+7);
   if ((verOffset=nAgt.indexOf("Version"))!=-1) 
     fullVersion = nAgt.substring(verOffset+8);
  }
  // In Firefox, the true version is after "Firefox" 
  else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {
   browserName = "Firefox";
   fullVersion = nAgt.substring(verOffset+8);
  }
  // In most other browsers, "name/version" is at the end of userAgent 
  else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) < 
        (verOffset=nAgt.lastIndexOf('/')) ) 
  {
   browserName = nAgt.substring(nameOffset,verOffset);
   fullVersion = nAgt.substring(verOffset+1);
   if (browserName.toLowerCase()==browserName.toUpperCase()) {
    browserName = navigator.appName;
   }
  }
  // trim the fullVersion string at semicolon/space if present
  if ((ix=fullVersion.indexOf(";"))!=-1)
     fullVersion=fullVersion.substring(0,ix);
  if ((ix=fullVersion.indexOf(" "))!=-1)
     fullVersion=fullVersion.substring(0,ix);

  majorVersion = parseInt(''+fullVersion,10);
  if (isNaN(majorVersion)) {
   fullVersion  = ''+parseFloat(navigator.appVersion); 
   majorVersion = parseInt(navigator.appVersion,10);
  }

//////////////////////得到手机的设备名称
  operate_relase = "";
  var OS_Name = navigator.appVersion;
          if (OS_Name.indexOf("Win") != -1) {	 
        operate = "Windows";
        
        if ((OS_Name.indexOf("Windows 95") != -1)||
        (OS_Name.indexOf("Win95") != -1) ||
        (OS_Name.indexOf("Windows_95") != -1)
        ) {	 
          operate_relase = "Windows 95";
        }else if ((OS_Name.indexOf("Windows 98") != -1)||
        (OS_Name.indexOf("Win98") != -1)) {	
          operate_relase = "Win98";
          
        }else if ((OS_Name.indexOf("Windows NT 5.0") != -1)||
        (OS_Name.indexOf("Windows 2000") != -1)) {	
          operate_relase = "Windows 2000";
          
        }else if ((OS_Name.indexOf("Windows NT 5.1") != -1)||
        (OS_Name.indexOf("Windows XP") != -1)) {	
          operate_relase = "Windows XP";
          
        }else if (OS_Name.indexOf("Win16") != -1) {	
          operate_relase = "Windows 3.11";
          
        }else if (OS_Name.indexOf("Windows NT 5.2") != -1) {	
          operate_relase = "Windows Server 2003";
          
        }else if (OS_Name.indexOf("Windows NT 6.0") != -1) {	
          operate_relase = "Windows Vista";
          
        }else if (OS_Name.indexOf("Windows NT 6.1") != -1) {	
          operate_relase = "Windows 7";
          
        }else if ((OS_Name.indexOf("Windows NT 4.0") != -1)||
        (OS_Name.indexOf("WinNT4.0") != -1) ||
        (OS_Name.indexOf("WinNT") != -1)||
        (OS_Name.indexOf("Windows NT") != -1)) {	
          operate_relase = "Windows NT 4.0";
        }else if (OS_Name.indexOf("Windows ME") != -1) {	
          operate_relase = "Windows ME";
        }

        
          } else if (OS_Name.indexOf("Mac") != -1) {
        operate = "Mac OS";	
          } else if (OS_Name.indexOf("X11") != -1) {
        operate = "Unix";	
          } else if (OS_Name.indexOf("Linux") != -1) {
        operate = "Linux";
          } else if (OS_Name.indexOf("SunOS") != -1) {
        operate = "Sun OS";
          } else if (OS_Name.indexOf("OpenBSD") != -1) {
        operate = "Open BSD";
        
      } else if (OS_Name.indexOf("QNX") != -1) {
        operate = "QNX";
      } else if (OS_Name.indexOf("BeOS") != -1) {
        operate = "BeOS";
      } else if (OS_Name.indexOf("OS/2") != -1) {
        operate = "OS/2";
      } else if ((OS_Name.indexOf("nuhk") != -1) 
      || (OS_Name.indexOf("Googlebot") != -1)
      || (OS_Name.indexOf("Yammybot") != -1)
      || (OS_Name.indexOf("Openbot") != -1)
      || (OS_Name.indexOf("Slurp") != -1)
      || (OS_Name.indexOf("MSNBot") != -1)
      || (OS_Name.indexOf("Ask Jeeves/Teoma") != -1)
      || (OS_Name.indexOf("ia_archiver") != -1)
      ) {
        operate = "Search Bot";
      }else{
        operate = "unknow";
      }
  /////////////////////

  //通过参数,得到url中参数的值
  function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
      results = regex.exec(location.search);
    return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
  }
  //得到当前的时间
  function getDate(){
    var currentdate = new Date(); 
    
    month = currentdate.getMonth();if(month<10){month = "0"+month;}
    day = currentdate.getDate();if(day<10){day = "0"+day;}
    hours = currentdate.getHours();if(hours<10){hours = "0"+hours;}
    minutes = currentdate.getMinutes();if(minutes<10){minutes = "0"+minutes;}
    second = currentdate.getSeconds();if(second<10){second = "0"+second;}
    var datetime =  currentdate.getFullYear() + "-"  
        + month  + "-" 
        + day + " "
                + hours + ":"  
                + minutes + ":" 
                + second;
    return datetime ;
  }
  
  
  
  //得到设备名称
  function getDevice(){
    var isMobile = {
      Android: function() {
      return navigator.userAgent.match(/Android/i);
      },
      webOS:function() {
      return navigator.userAgent.match(/webOS/i);
      },
      BlackBerry: function() {
      return navigator.userAgent.match(/BlackBerry/i);
      },
      iPhone: function() {
      return navigator.userAgent.match(/iPhone/i);
      },
      iPad: function() {
      return navigator.userAgent.match(/iPad/i);
      },
      iPod: function() {
      return navigator.userAgent.match(/iPod/i);
      },
      Opera: function() {
      return navigator.userAgent.match(/Opera Mini/i);
      },
      Windows: function() {
      return navigator.userAgent.match(/IEMobile/i);
      },
      any: function() {
      return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iPad() ||isMobile.iPod() || isMobile.iPhone() || isMobile.Opera() || isMobile.Windows());
      }
    };
    var str = "";
    if( isMobile.Android() ) { str = "Android";  }
    if( isMobile.webOS() ) { str = "webOS";  }
    if( isMobile.BlackBerry() ) { str = "BlackBerry";  }
    if( isMobile.iPhone() ) { str =  "iPhone";  }
    if( isMobile.iPad() ) { str =  "iPad";  }
    if( isMobile.iPod() ) { str =  "iPod";  }
    if( isMobile.Opera() ) { str =  "Opera";  }
    if( isMobile.Windows() ) { str =  "Windows";  }
    if(str){
      return "Mobile:"+str;
    }else{
      return "PC";
    }
    
  }
  
  //////////////
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  };
   
  function guid() {
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
  }
  ////得到唯一标示码uuid 
  var uuid = guid();
  ////设置cookie,默认是设置天  expires代表的是天数。
  function Set_Cookie( name, value, expires, path, domain, secure )
  {
    // set time, it's in milliseconds
    
    domain = document.domain;
    
    domain = domain.replace("www.","");
    
    var today = new Date();
    today.setTime( today.getTime() );

    /*
    if the expires variable is set, make the correct
    expires time, the current script below will set
    it for x number of days, to make it for hours,
    delete * 24, for minutes, delete * 60 * 24
    */
    if ( expires )
    {
      expires = expires * 1000 * 60 * 60 * 24;
    }else{
      expires = expires * 20 * 365 * 1000 * 60 * 60 * 24;
    }
    var expires_date = new Date( today.getTime() + (expires) );
    
    this_expires = expires/1000;
    //document.write("thisdomain:"+domain+"<br/>");
    document.cookie = name + "=" +escape( value ) +
    ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) +
    ( ( path ) ? ";path=" + path : "" ) +
    ( ( domain ) ? ";domain=" + domain : "" ) +
    ( ( secure ) ? ";secure" : "" );
    
    
  }
  //得到cookie
  function Get_Cookie( check_name ) {
    // first we'll split this cookie up into name/value pairs
    // note: document.cookie only returns name=value, not the other components
    var a_all_cookies = document.cookie.split( ';' );
    var a_temp_cookie = '';
    var cookie_name = '';
    var cookie_value = '';
    var b_cookie_found = false; // set boolean t/f default f

    for ( i = 0; i < a_all_cookies.length; i++ )
    {
      // now we'll split apart each name=value pair
      a_temp_cookie = a_all_cookies[i].split( '=' );


      // and trim left/right whitespace while we're at it
      cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

      // if the extracted name matches passed check_name
      if ( cookie_name == check_name )
      {
        b_cookie_found = true;
        // we need to handle case where cookie has no value but exists (no = sign, that is):
        if ( a_temp_cookie.length > 1 )
        {
          cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
        }
        // note that in cases where cookie is initialized but no value, null is returned
        return cookie_value;
        break;
      }
      a_temp_cookie = null;
      cookie_name = '';
    }
    if ( !b_cookie_found )
    {
      return null;
    }
  }
  //删除cookie
  function Delete_Cookie( name, path, domain ) {
    if ( Get_Cookie( name ) ) document.cookie = name + "=" +
    ( ( path ) ? ";path=" + path : "") +
    ( ( domain ) ? ";domain=" + domain : "" ) +
    ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
  }
  
  
    var params = {};
  
  // 设置  cid   如果新的cid访问,那么cid将会被覆盖。 
  cid = getParameterByName("cid");
  var cookie_cid = Get_Cookie("cid");
  
  if(cid){
    //set cid cookie.
    params.cid = cid;
    Set_Cookie( "cid", cid, 7, "/", '', '' );
  }else if(cookie_cid){
    params.cid = cookie_cid;
  }
  
  //设备
  params.devide = getDevice();
  params.user_agent = navigator.userAgent;
  var userLang = navigator.language || navigator.userLanguage; 
  params.browser_name 	= browserName;  //浏览器名称
  params.browser_version 	= majorVersion;  //浏览器版本
  params.browser_date 	= getDate();  //浏览器时间
  params.browser_lang 	= userLang;   //浏览器语言
  params.operate 			= operate;     //操作系统
  params.operate_relase 	= operate_relase;  //操作系统详细
  
  
  
    //Document对象数据
  //通过加入下面的代码,来辨别是否是老客户
  //标准为:refer domain为空,或者不包含  && _fta 这个cookie存在
  //在数据分析中,客户中间客户点击刷新页面,也会被标示成return:1,因此,在分析的时候,需要找UID
  //最小的那个,查看refer。
    if(document) {
    params.domain = document.domain || '';   //域名
    params.url = document.URL || '';    //当前url
    params.title = document.title || '';   //当前title
    params.refer_url = document.referrer || '';  //来源referrer
    
    
    //thisrefer =  document.referrer || '';
    //refer_fta_cookie = Get_Cookie( '_fta' );
    
    //此cookie用来判断客户是否是持续的访问网站,如果设置的是6个小时,如果客户在6个小时内第二次访问网站
    //系统会认为这是客户的一次连贯的访问。第二次访问后,此cookie会更新超时时间
    _fto = Get_Cookie( '_fto' );  
    // //永久cookie,这个cookie存在,那么说明是老客户了
    _fta = Get_Cookie( '_fta' );   
    
    if(_fto){
      //继续的访问,访问的延伸。无所谓refer,和return
      //更新超时时间:
      Set_Cookie( '_fto',1, 0.25, '/', '', '' );   //online - one day
    }else{
      //相当于第一次访问。
      
      thisreferrer = document.referrer || '';
      // 存在refer 则记录refer  ,如果不存在,则设置redirect
      if(!thisreferrer){
        thisreferrer_domain = "redirect";
      }else{
        thisreferrer_domain = thisreferrer.replace('http://','').replace('https://','').split(/[/?#]/)[0];
      }
      // 第一次访问网站的时候会记录访问来源。
      Set_Cookie( '_ftreferdomain',thisreferrer_domain, 1, '/', '', '' );   //refer  - one day
      // 第一次访问的时候会设置_fto为6个小时。
      Set_Cookie( '_fto',1, 0.25, '/', '', '' );   //online - one day
          
        
      //如果存在_fta 则代表这个客户肯定访问过网站信息。
      if(_fta){
        //老客户,设置的cookie:来源域名,是否在线,是否是老客户
        Set_Cookie( '_ftreturn',1, 36500, '/', '', '' );   // is return  - one day
      }else{
        //新客户
        Set_Cookie( '_ftreturn',0, 36500, '/', '', '' );   // is return  - one day
      }
    }
    //referrer 域名,是否是老客户,是否是在线状态
    params.first_referrer_domain = Get_Cookie('_ftreferdomain');
    params.is_return = Get_Cookie('_ftreturn');
    //params.online = Get_Cookie('_fto');

    
    //设置uuid,如果 _fta存在,那么设置uuid为它的值,如果不存在,那么重新获取uuid。
    if(cookie_uuid = Get_Cookie('_fta') ){
      params.uuid = cookie_uuid;
    }else{
      params.uuid = uuid;
      Set_Cookie( '_fta', uuid, 36500, '/', '', '' );
    }
    
    
    
    }
    //Window对象数据
    if(window && window.screen) {
    //params.sh = window.screen.height || 0;
    //屏幕分辨率和屏幕的画质
    if(window.devicePixelRatio){
      devicePixelRatio = window.devicePixelRatio;
      params.device_pixel_ratio = devicePixelRatio;
      params.resolution = (window.screen.width*devicePixelRatio || 0) +"x"+ (window.screen.height*devicePixelRatio || 0);
    }else{
      params.resolution = (window.screen.width || 0) +"x"+ (window.screen.height || 0);
    }
    params.color_depth = window.screen.colorDepth || 0;
    }
  
    
  
  
    //解析_maq配置
  if(_maq) {
    for(var i in _maq) {
      x = _maq[i][0];
      if(x){
        //email
        //当前的customer email如果和cookie一样,那么使用customer email
        //如果不一样,那么保存customer email到cookie _fte
        //对于customer name同样也是这样
        if(x == 'login_email'){
          current_customer_email = _maq[i][1];
          //存在cookie 
          if(cookie_customer_email = Get_Cookie( '_fte' )){
            //当前cookie存在,并且与传递过来的相同
            if(current_customer_email != cookie_customer_email){
              Set_Cookie( '_fte', current_customer_email, '36500', '/', '', '' );
            }
          //不存在cookie
          }else{
            Set_Cookie( '_fte', current_customer_email, '36500', '/', '', '' );
          }
          params[x] = current_customer_email;
        
        }else{
          params[x] = _maq[i][1];
        }
      }
    }
  }
  //如果cookie中存在客户邮箱,那么从cookie中获取
  if(!params.login_email){
    if(Get_Cookie( '_fte' )){
      params.login_email = Get_Cookie( '_fte' )
    }
  }
  
    //拼接参数串,形成url,然后通过图片的方式传递数据
    var args = '';
    for(var i in params) {
    if(args != '') {
      args += '&';
    }
    args += i + '=' + encodeURIComponent(params[i]);
    }
     
    //通过Image对象请求后端脚本
    var img = new Image(1, 1);
    img.src = 'http://trace.tomtop.com/trace.php?' + args;
 })();

原理:js手机的数据,通过参数的方式拼成一个url  :http://trace.tomtop.com/trace.php? xxxxx ,然后把数据传递给trace.php

 

2. ip找到国家(穿插部分)

Geoip geoip-api-php 库包使用 – 通过ip 找到国家

 

3. php 存储(market.php)这里的geoip就是第二部分的通过ip找到国家

<?php
//exit;
//date_default_timezone_set('Asia/Shanghai');
# 时区
ini_set('date.timezone','UTC');
# ip 库
include("/www/web/market/common/lib/geoip-api-php-1.14/src/geoip.inc");

# log 测试函数
function logdd($info){
  if(is_array($info) || is_object($info)){
    $info = var_export($info,true);
  }
  //echo $info;exit;
  $logfile = './logs/my.log';
  $handle = fopen($logfile,"a+");/*根据需要更改这里的参数*/ 
  $contents = fwrite($handle,$info."\n"); 
  fclose($handle);
      
}


#得到IP地址。
function get_client_ip() {
  $ipaddress = '';
  if (getenv('HTTP_CLIENT_IP'))
    $ipaddress = getenv('HTTP_CLIENT_IP');
  else if(getenv('HTTP_X_FORWARDED_FOR'))
    $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
  else if(getenv('HTTP_X_FORWARDED'))
    $ipaddress = getenv('HTTP_X_FORWARDED');
  else if(getenv('HTTP_FORWARDED_FOR'))
    $ipaddress = getenv('HTTP_FORWARDED_FOR');
  else if(getenv('HTTP_FORWARDED'))
     $ipaddress = getenv('HTTP_FORWARDED');
  else if(getenv('REMOTE_ADDR'))
    $ipaddress = getenv('REMOTE_ADDR');
  else
    $ipaddress = 'UNKNOWN';
  return $ipaddress;
}

//得到国家。通过ip 支持ipv6  已经测试。
function getCountryByIp($ip){
  $gi = geoip_open("/www/web/market/common/lib/geoip/GeoIP.dat", GEOIP_STANDARD);
  if(strstr($ip,":")){
    $country_code = geoip_country_code_by_addr_v6($gi,$ip);
    $country_name = geoip_country_name_by_addr_v6($gi,$ip);
  }else{
    $country_code = geoip_country_code_by_addr($gi,$ip);
    $country_name = geoip_country_name_by_addr($gi,$ip);
  }
  geoip_close($gi);
  return [
    "country_code" => $country_code,
    "country_name" => $country_name,
  ];
}

#得到递增id
/*
function increament($m,$db_name,$tablename){
  
  $ids = "ids";
  $db_name = "tracedb";
  $db = $m->selectDB($db_name);
  $col = $m->selectDB($db_name)->$ids;
  
  $query = array('name'=>'trace_2014_11_24');
  $update = array('?inc'=>array('id'=>1));
  $result = $db->command(
            array(
            "findandmodify" => "ids",
            "query" => $query,
            "update" => $update,
            )
          );

  return $result['value']['id'];
} 
*/

$delay_time = " -0 hours";	
$insertTimeStamp = strtotime(date('Y-m-d H:i:s').' '.$delay_time);
$insertDateTime = date('Y-m-d H:i:s',$insertTimeStamp);
$insertDate = date('Y-m-d',$insertTimeStamp);
$get 						= array();
$get['ip'] 					= get_client_ip();
#$get['server_datetime']	= date('Y-m-d H:i:s',strtotime('-12 hours'));    //+3 hours  延后12个小时,美国时间
$get['service_date_str']	= $insertDate;
$get['service_datetime'] 	=  new MongoDate($insertTimeStamp);   #北京时间
$get['service_timestamp'] 	=  (float)$insertTimeStamp;
foreach($_GET as $k=>$v){
  $get[$k] = $v;
}
  # ip6 example
  #$get['ip'] = "2001:0DB8:0:0:0:0:1428:0000";
$ip 					= $get['ip'];
$website_id 			= $get['website_id'];
$countInfo 				= getCountryByIp($ip);
$get['country_code'] 	= $countInfo['country_code'];
$get['country_name'] 	= $countInfo['country_name'];


#连接到mongodb
$m = new MongoClient('mongodb://localhost:27017');
$database_name 		= "tracedb";  
#trace_网站编号_年份_月_initial_data
$collection_name 	= "trace_".$website_id."_".date("Y_m",strtotime($delay_time))."_initial_data";
$db 		= $m->$database_name;
$collection = $db->$collection_name;
#得到递增id。
#$id = increament($m,$database_name,$collection_name);

#$get["_id"] = $id; 
#var_dump($get);
#订单信息
if($get['order']){
  $get['order'] = json_decode($get['order'],true);
  $get['order_status'] = 'fail';
}
#购物车信息。
if($get['cart']){
  $get['cart'] = json_decode($get['cart']);
}

#购物车信息。
if($get['search']){
  $get['search'] = json_decode($get['search']);
}


#插入数据到mongodb
if(!empty($get) && is_array($get)){
  $collection->insert($get);
  echo "Insert succsessfully";
}

   
   
   
   
   
   
   
   
   

这样就把数据保存到mongodb里面去了。

然后就可以通过mongodb的mapreduce进行统计分析数据了。

php 安装 zend opcace

1.linux 安装

wget http://pecl.php.net/get/zendopcache-7.0.5.tgz
tar xzf zendopcache-7.0.5.tgz 
cd zendopcache-7.0.5
/usr/local/php/bin/phpize
./configure   --with-php-config=/usr/local/php/bin/php-config
make
make install

Note:
If you want to use OPcache with » Xdebug, you must load OPcache before Xdebug.
如果你使用了Xdebug,您的配置中OPcache 要放到Xdebug前面。

在php.ini中加入:

zend_extension=/usr/local/php/lib/php/extensions/no-debug-zts-20100525/opcache.so
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1

 

其中:max_accelerated_files 设置的是缓存的文件,这个您需要到您的应用下面查看一下php文件的数量,可以通过下面的命令统计:

find ./* \( -name '*.php' -or -name '*.inc' \) -type f -print |wc -l

如果文件多,可以设置的高一些。

 

整理了一下其在php.ini中各参数的使用说明,供大家参考,有一些不太懂的,就留空了,还请高手赐教。

名字 默认 可修改范围 含义
opcache.enable “1” PHP_INI_ALL 是否启用opcache
opcache.enable_cli “0” PHP_INI_SYSTEM 是否在CLI(即命令行时)启用opcache
opcache.memory_consumption “64” PHP_INI_SYSTEM 为opcache分配多少共享内存,单位M
opcache.interned_strings_buffer “4” PHP_INI_SYSTEM interned string的内存大小
opcache.max_accelerated_files “2000” PHP_INI_SYSTEM 最大缓存的文件数目。

实际上这个值会使用第一个大于你配置的数字的下列素数

{ 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 },

如你将该值指定为400,则实际上该值为463.

opcache.max_wasted_percentage “5” PHP_INI_SYSTEM
opcache.use_cwd “1” PHP_INI_SYSTEM 如果置为1,则将当前路径加入到文件key中,

以避免可能产生的同文件名的文件key冲突

opcache.validate_timestamps “1” PHP_INI_ALL 如果置为1,则OPCACHE会自动检测文件的时间戳

(检测周期为revalidate_freq),

并根据文件的时间戳来更新opcode,如果置为0,

则只能手动去重启opcache或

重启webserver以使更新后的php文件生效

opcache.revalidate_freq “2” PHP_INI_ALL opcache自动检测文件是否更新的周期,单位秒。

如果是0,则每次请求时opcache都要进行检测。

当validate_timestamps为0时,本指令无效。

opcache.revalidate_path “0” PHP_INI_ALL
opcache.save_comments “1” PHP_INI_SYSTEM 是否保存文件中的注释
opcache.load_comments “1” PHP_INI_ALL 是否load comments,与save_comments联合起来使用,

如果该值为0,则即使save_comments为1,

那么php脚本中的comments也是不使用的

opcache.fast_shutdown “0” PHP_INI_SYSTEM 是否打开快速关闭,

打开时可使php在request shutdown时回收内存快

opcache.enable_file_override “0” PHP_INI_SYSTEM 如果置为1,则每次调用file_exist() is_file() is_readable()函数时,

opcache将要检查该文件是否被cache了,

这样增加了检查存在性和可读性的开销,

但避免了当validate_timestamps为disable时返回错误文件状态的风险。

opcache.optimization_level “0xffffffff” PHP_INI_SYSTEM 运行时控制优化的掩码(干什么的?)
opcache.inherited_hack “1” PHP_INI_SYSTEM 5.3以前使用。5.3后废弃
opcache.dups_fix “0” PHP_INI_ALL 为解决“cannot redecllare class” 时,可将其置为1
opcache.blacklist_filename “” PHP_INI_SYSTEM 设置黑名单文件,符合黑名单文件中定义的php文件将不被opcache。黑名单文件的例子如下:

; Matches a specific file.
/var/www/broken.php
; A prefix that matches all files starting with x.
/var/www/x
; A wildcard match.
/var/www/*-broken.php
一行为一条规则,支持通配符,注释以分号开头
opcache.max_file_size “0” PHP_INI_SYSTEM 被cache的文件的最大size,单位bytes。0表示不限
opcache.consistency_checks “0” PHP_INI_ALL 如果置为N,N非零,则opcache会每N个请求核实一下cache的检验和。

这会损害性能,应该只在debug时使用

opcache.force_restart_timeout “180” PHP_INI_SYSTEM 如果opcache处于非active状态,当N秒后opcache将自动重启
opcache.error_log “” PHP_INI_SYSTEM opcache自身的errorlog文件路径,为空时则使用stderr
opcache.log_verbosity_level “1” PHP_INI_SYSTEM 日志记录level,默认只有fatal error和error
opcache.preferred_memory_model “” PHP_INI_SYSTEM opcache首选使用的内存模型,为空时会选择最适当的模型。

常用的有,mmap shm posix 和win32

opcache.protect_memory “0” PHP_INI_SYSTEM 运行php脚本时保护共享内存防止意外的写入。

只对debug时有用。

opcache.mmap_base NULL PHP_INI_SYSTEM
0

 

 

顶级PHP大师的开发原则

1. 在合适的时候使用PHP – Rasmus Lerdorf

没有谁比PHP的创建者Rasmus Lerdorf明白PHP用在什么地方是更合理的,他于1995年发布了PHP这门语言,从那时起,PHP就像燎原之火,烧遍了整个开发阵营,改变了互联 网的世界。可是,Rasmus并不是因此而创建PHP的。PHP是为了解决web开发者的实际问题而诞生的。

和许多开源项目一样,PHP变得流行,流行的动机并不能用正常的哲学来进行解释,甚至流行得有些孤芳自赏。它完全可以作为一个案例,一个解决各种Web问题的工具需求所引起的案例,因此当PHP刚出现的时候,这种工具需求全部聚焦到PHP的身上。

但是,你不能奢望PHP可以解决所有问题。Lerdorf是第一个承认PHP只是一种工具的人,并且PHP也有很多力所不能及的情况。

根据工作的不同来选择合适的工具。我跑了很多家公司,为了说服他们部署和使用PHP,但是这并不意味着PHP对所有问题都适用。它只是可以一个解决大部分问题的front-end脚步语言。

作为一个web开发者,尝试用PHP解决所有问题是不科学的,同时也会浪费你的时间。当PHP玩不转的时候,不要犹豫,试用一下其他的语言吧。

2. 使用多表存储提高规模伸缩性 – Matt Mullenweg

没有人愿意质疑Matt Mullenweg在PHP方面的权威性,他开发了这个星球上最流行的blog系统,(依靠一个强大的社区力量支持): WordPress. 创建Wordpress以后,Matt和他的团队启动了WordPress.com平台,一个基于WordPress MU的免费blog站点。现在,Wordpress.com已经拥有大约400万用户, 这些用户每天提供超过 140,000篇的日志。 (要查看更多Wordpress.com的统计情况,请点击这里.)

如果有人知道如何让网站的规模伸缩自如,这个人一定是Matt Mullenweg。2006年的时候 Matt对Wordpress的数据结构进行了前瞻性的改进,并且解释了为什么Wordpress MU对每个blog使用独立的MYSQL表格, 而不是把所有的blog数据都塞进一个巨大的表格。

我们测试过这个方法,但是发现如果要扩展它的伸缩性,代价太高。如果用一个整体的数据结构,在大流量面前,你将会面临服务器 硬件的问题。在MU里面。用户们都被分布到独立的表格当中,并且可以轻易地组织起来。举个例子,WordPress.com把用户的数据分散存储到 4096个数据库中,这些数据库可以分散大规模的数据访问,实现流量和压力分流。

数据表的可迁移性让代码(blog)可以运行得更快,并且让系统具备更强的伸缩性。依靠强大的缓存策略和灵活的数据库运用策略, Matt向人们展示了时下最流行的Facebook和Wordpress.com都可以在PHP下稳定运行,并且处理惊人的访问量。

3. 千万不要相信用户 – Dave Child

Dave Child是Added Bytes (previously ilovejackdaniels.com) 网站的核心人物,这个网站以他出色的《cheat sheets for many programming languages》而闻名。 Dave为很多英国的公司服务,并且已经在编程世界里树立起相当的权威。

Dave为PHP开发者提供了很多深谋远虑的建议,并总结成了《writing secure code in PHP》:千万不要相信你的用户,他们甚至可能会伤害你。

有一条web开发的基本原则,我重复多少遍都觉得不够,那就是:千万不要相信你的用户,同时要假设你网站中的每个数据单元都是从用户那里收集来 的恶意代码。很多时候,你必须用javascript在客户端检验表单提交过来的内容, 如果你习惯了如此,那么,这是一个好习惯。如果安全性对你来说很重要,这就是最重要最需要学习的原则。

Dave目前正致力于为它的《Writing Secure PHP》系列书籍整理实例,书的最后他说:

最后,变得偏执一点吧。除非你认为你的站点永远不会受到攻击,否则就正视所有的问题,当问题真正发生的时候,你的情况会变得很糟。你需要把每个用户都看成会带来一场攻防站的黑客,想尽一切办法来保护站点的安全,同时想好相应问题的解决方案。

 4. 多使用PHP缓存 – Ben Balbo

Ben Balbo开发了Site Point,一个为developers和designers提供指导的网站。他是墨尔本PHP开发和开源俱乐部的成员, 因此他对PHP有一定的了解,同时对PHP caching有一定的想法和经验。

如果你拥有一个访问量很大,但更新并不频繁的站点(比如blog,基于某种CMS),或许它需要进行一些改造,这些改造不会花费太多的时间,但是对性能有突出的贡献。 如果要为一个复杂/更新频率很快的站点建立缓存机制,过程可能会很曲折,但是好处也是显而易见的。

PHP缓存技术有很多种,Ben为我们推荐了如下一些:

◆缓存函数的运行结果

◆设置过期时间

◆缓存IE下载的文件

◆模板缓存技术

◆Cache_Lite

由于PHP作为动态语言的特性,缓存机制对于更新频率并不快的站点来说非常重要。

5. 使用IDE, Templates和Snippets加速PHP开发 – Chad Kieffer

当Chad Kieffer从UI设计和数据库优化的工作中抽身出来的时候,他会在他的博客2 tablespoons上分享很多技术经验。由于Chad多方面的全面发展,他经常可以发现其他程序员不能发现的问题,并形成相关经验,尤其是他开发网站 的方法。他参与了网站开发的各个环节,因此他的建议对于提高网站开发的大局观非常有用。

Chad认为使用Eclipse PDT(Eclipse’s PHP development package) 这样的IDE,同时使用一些模板技术和开源项目可以有效地提高PHP的开发速度。

紧凑的计划,长长的to do lists以及deadlines让开发人员非常苦闷。不过有些功能,比如Eclipse Templates,可以有效减少编码的时间和出错的几率。

通常来说,任何项目都可以自动化,自动化程度越高, 你完成项目的时间就越短。花时间来开发使用频率很高的框架和模板,将会节省你以后更多时间。同时,使用像Eclipse and the PDT package这样的IDE,你会发现效率得到明显提高,IDE可以自动闭合,补全分号并且可以在本地debug。

 6. 利用好PHP的过滤函数 – Joey Sochacki

或许Joey Sochacki并不像Matt Mullenweg那样有名 ,但他也是一个经验丰富的开发者,并且通过他的博客Devolio分享了很多技术经验

Joey发现在编写php代码的过程中有很多地方需要进行过滤,但却并没有太多的coder关注php的内置过滤函数。

过滤数据是我们经常需要做的事情,但是很多功能丰富的PHP内置过滤函数却不为人知。使用类似filter_* 的PHP内置函数,我们几乎可以处理所有的过滤任务,包括数据类型验证/URL/email和IP地址验证/特殊字符处理等等。

过滤是一件复杂的事情,但是我相信joey的发现会给你很多启发,让你认识到PHP强大的过滤功能。

7. 使用PHP框架 – Josh Sharp

对于是否应该使用Zend, CakePHP, Code Igniter, 或者 其他PHP框架,一直存在着很多争议,但是在web开发者的心中,他们有自己衡量的标准。

Josh Sharp自己创建了一家提供面包和黄油服务的网站,因此他对于使用PHP框架来开发网站有一定的经验。他认为使用一个PHP框架来进行项目开发(use a PHP framework ),可以有效地节省时间,并且减少出错的几率。为什么?因为他觉得PHP实在是太好上手了。

PHP的易于使用有时候也有缺陷,因为并不严格的语法,经常会导致很多错误代码的诞生。但如果使用一个PHP框架,出错的几率就会大大减少。

PHP框架可以让你的代码结构更加规范,并且节省大量时间。

8. 不要使用PHP框架 – Rasmus Lerdorf

与Josh的观点恰恰相反,PHP的鼻祖Rasmus Lerdorf却认为最好不要使用PHP框架,为什么?因为不基于框架的PHP性能更好。Rasmus在Drupalcon 2008的演讲上,用“Hello World”的例子来对比了一些框架PHP和简单PHP之间的性能,结果显示框架PHP的性能要远远落后。

9. 使用批处理 – Jack D. Herrington

Jack Herrington对PHP世界并不陌生, 并且为大名鼎鼎的IBM developerWorks贡献过超过30篇的专搞, 同时出版过《PHP Hacks》的书,因此他是一个真正的专家。

Herrington推荐使用批处理和Cron来代替那些可以运行在后台的程序脚步,Web用户并不愿意在线等待你的处理过程,所以有些事情更适合放到后台来处理。

诚然,在某些情况下,这有点大材小用了,但是你可以清楚地看到,使用Cron, MySQL, PHP面向对象的方法以及Pear::DB这些便捷的工具来创建一个批处理工具并不是一件复杂的事情。

Jack认为使用cron, PHP和MySQL在后台处理一些任务,比起多进程的业务逻辑要划算得多。

两种方法我都尝试过,我认为Cron非常符合”Keep It Simple, Stupid” (KISS) 的原则,它让后台处理变得简单。与多进程的业务逻辑相比,它没有内存 溢出的风险。你可以创建一个简单的批处理脚本,并且在cron中运行,这个脚本会定时检查是否有任务需要处理,处理完之后就会自动退出,因此你不用担心是 否有进程卡壳,或者陷入死循环。

 10. 及时启用错误报告 – David Cummings

David Cummings有一个专门提供CMS软件服务的公司 ,并且获得过几次奖 ,他有非常丰富的PHP开发经验。David曾经写过《two PHP tips he wished he’d learned in the beginning》,其中一点就是:及时启用错误报告,这会节省大量的时间。

我告诉人们,最重要的事情就是最大程度地开启PHP的错误报告,为什么?因为PHP可能会隐藏很多小问题:

◆变量没有预定义

◆在代码片段中引用了不可用的变量

◆使用了未定义的常量这些因素看起来并不是什么大事,除非你在使用面向对象的方法编写一些类库。通常,关闭错误报告将可能使你付出更大的成本来维护你的代码。

错误报告可以帮你轻易地找到代码的问题所在,如果错误报告的等级够高,细微的错误都能被立即发现,帮助你节省整体debug的时间。

Yii2 Mongodb 数据库报错:remote connect close

我使用了mongodb的复制集,报错remote connect close

Yii2中我更改下了配置如下:

'mongodb_erp' => [
            'class' => 'yii\mongodb\Connection',
            'dsn' => 'mongodb://192.168.220.105:27017/erp,192.168.220.106:27017/erp?replicaSet=rs0&readPreference=secondaryPreferred', # primaryPreferred |  secondaryPreferred
      'options' => [
        'socketTimeoutMS' => 300000,  # 设置超时时间
        'connectTimeoutMS'=> 600000,  # 设置连接超时时间
      ],
],

 

将读改成副本读优先,对于读的优先级的设置解释如下:

/**
        const string RP_PRIMARY = "primary" ;  						# 始终用主读
        const string RP_PRIMARY_PREFERRED = "primaryPreferred" ;	# 主读优先
        const string RP_SECONDARY = "secondary" ;					# 始终用副本读
        const string RP_SECONDARY_PREFERRED = "secondaryPreferred" ;# 副本读优先
        const string RP_NEAREST = "nearest" ;						# 主和副本随机读
      */

另外,长连接可能存在失效问题,在连接mongodb的时候,需要循环一下:

Yii2 随机发生 php mongo报错:’Failed to connect to: XXXXX: Remote server has closed the connection’的解决方法