电商项目中使用Redis实现秒杀功能

参与过抢购活动就知道,很明显的一点是商即便商品实际没有了也是可以下单成功的,但是在支付的时候会提示你商品没有了。

实现原理:list双向链表

使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行.(mysql事务在高并发下性能下降很厉害,文件锁的方式也是).

此处用到了Redis中的链表(list)数据类型:

‘栈’:从链表的头部添加元素,先进后出

‘队列’:从链表的尾部添加元素,先进先出

redis保存数据时都有key和value,key和value要么是String类型的,要么是byte[]类型的

第一步:先将商品库存存入队列

1

第二步:抢购开始,设置库存的缓存周期

2

第三步:客户端执行下单操作,下单前判断redis队列库存量

3

Redis的7个应用场景

一:缓存——热数据

热点数据(经常会被查询,但是不经常被修改或者删除的数据),首选是使用redis缓存,毕竟强大到冒泡的QPS和极强的稳定性不是所有类似工具都有的,而且相比于memcached还提供了丰富的数据类型可以使用,另外,内存中的数据也提供了AOF和RDB等持久化机制可以选择,要冷、热的还是忽冷忽热的都可选。

结合具体应用需要注意一下:很多人用spring的AOP来构建redis缓存的自动生产和清除,过程可能如下:

上面这种操作,如果并发量很小的情况下基本没问题,但是高并发的情况请注意下面场景:

为了update先删掉了redis中的该数据,这时候另一个线程执行查询,发现redis中没有,瞬间执行了查询SQL,并且插入到redis中一条数据,回到刚才那个update语句,这个悲催的线程压根不知道刚才那个该死的select线程犯了一个弥天大错!于是这个redis中的错误数据就永远的存在了下去,直到下一个update或者delete。

二:计数器

诸如统计点击数等应用。由于单线程,可以避免并发问题,保证不会出错,而且100%毫秒级性能!爽。

命令:INCRBY

当然爽完了,别忘记持久化,毕竟是redis只是存了内存!


三:队列


四:位操作(大数据处理)

用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。

想想一下腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,你能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下需要的内存会很恐怖,而且这种类似的需求很多,腾讯光这个得多花多少钱。。)好吧。这里要用到位操作——使用setbit、getbit、bitcount命令。

原理是:

redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示我们上面例子里面的用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统,上面我说的几个场景也就能够实现。用到的命令是:setbit、getbit、bitcount


五:分布式锁与单线程机制


六:最新列表

例如新闻列表页面最新的新闻列表,如果总数量很大的情况下,尽量不要使用select a from A limit 10这种low货,尝试redis的 LPUSH命令构建List,一个个顺序都塞进去就可以啦。不过万一内存清掉了咋办?也简单,查询不到存储key的话,用mysql查询并且初始化一个List到redis中就好了。


七:排行榜

谁得分高谁排名往上。命令:ZADD(有续集,sorted set)

最近在研究股票,发现量化交易是个非常好的办法,通过臆想出来规律,用程序对历史数据进行验证,来判断这个臆想出来的规律是否有效,这玩意真牛!

Git自动化部署踩过的坑

  1. 编写php【用于执行hook.sh,注意使www用户有可执行的权限】
$jsonData = file_get_contents('php://input', 'r');

$postArray = json_decode($jsonData,true);

if (isset($postArray['ref']) && $postArray['ref'] == 'refs/heads/master') {

file_put_contents('/home/wwwlog/hook_log-'.date('Ymd').'.txt',date('Y-m-d H:i:s').'|-------|'.$jsonData.PHP_EOL, FILE_APPEND);

$status = @system('/home/wwwroot/bin-shells/hook.sh',$out);

}

exit('ok');
  1. 编写sh
#!/bin/sh

#判断是不是远端仓库




IS_BARE=$(git rev-parse --is-bare-repository)

#if [ -z "$IS_BARE" ]; then

#echo >&2 "fatal: post-receive: IS_NOT_BARE"

#exit 1

#fi




#unset GIT_DIR

DeployPath="/home/wwwroot/cash_advance"




echo "==============================================="

cd $DeployPath

echo "deploying the test web"




#git stash




#git pull origin master




git fetch --all

git reset --hard origin/master

git pull

#gitbook build

sleep 15




time=`date`

echo "web server pull at webserver at time: $time."

echo "================================================"

 

  1. 相关权限问题:
  • 要使www用户有执行shell脚本的权限:
vi /etc/passwd 将/sbin/nologin改为/bin/bash

 

  • 要将/root/.ssh/下的文件拷贝到www用户的宿主目录下:/home/www/.ssh/,因为web是以www用户来访问的;并将私钥加入到本地计算机的ssh-agent中:

➜ su www【切换到www用户】

➜ eval “$(ssh-agent -s)”

Agent pid 78370

➜ ssh-add /home/www/.ssh/id_rsa

Enter passphrase for /Users/Leo/.ssh/id_rsa: <myPassphrase>

Identity added: /Users/Leo/.ssh/id_rsa (/Users/Leo/.ssh/id_rsa)

  • 在git根目录下需要给隐藏目录.git 755的权限:

chmod -R 755 ./.git

git 的全局sudo -Hu www git config –global credential.helper store # 永久保存

sudo -Hu www git config –global user.name “name”

sudo -Hu www git config –global user.email “shample@gmail.com” # 邮箱请与github上一致

  • 修改ini文件:将system函数从禁用列表中删除

 

  1. 将外网可访问的http://xxx.xxx.com/hook.php配置到github后台的Web Hooks配置项中。

MySQL的JOIN(一):用法

JOIN的含义就如英文单词“join”一样,连接两张表,大致分为内连接,外连接,右连接,左连接,自然连接。这里描述先甩出一张用烂了的图,然后插入测试数据。

1

笛卡尔积:CROSS JOIN

要理解各种JOIN首先要理解笛卡尔积。笛卡尔积就是将A表的每一条记录与B表的每一条记录强行拼在一起。所以,如果A表有n条记录,B表有m条记录,笛卡尔积产生的结果就会产生n*m条记录。下面的例子,t_blog有10条记录,t_type有5条记录,所有他们俩的笛卡尔积有50条记录。有五种产生笛卡尔积的方式如下。

内连接:INNER JOIN

内连接INNER JOIN是最常用的连接操作。从数学的角度讲就是求两个表的交集,从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录。有INNER JOIN,WHERE(等值连接),STRAIGHT_JOIN,JOIN(省略INNER)四种写法。至于哪种好我会在MySQL的JOIN(二):优化讲述。示例如下。

左连接:LEFT JOIN

左连接LEFT JOIN的含义就是求两个表的交集外加左表剩下的数据。依旧从笛卡尔积的角度讲,就是先从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录(见最后三条)。

右连接:RIGHT JOIN

同理右连接RIGHT JOIN就是求两个表的交集外加右表剩下的数据。再次从笛卡尔积的角度描述,右连接就是从笛卡尔积中挑出ON子句条件成立的记录,然后加上右表中剩余的记录(见最后一条)。

外连接:OUTER JOIN

外连接就是求两个集合的并集。从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录,最后加上右表中剩余的记录。另外MySQL不支持OUTER JOIN,但是我们可以对左连接和右连接的结果做UNION操作来实现。

USING子句

MySQL中连接SQL语句中,ON子句的语法格式为:table1.column_name = table2.column_name。当模式设计对联接表的列采用了相同的命名样式时,就可以使用 USING 语法来简化 ON 语法,格式为:USING(column_name)。
所以,USING的功能相当于ON,区别在于USING指定一个属性名用于连接两个表,而ON指定一个条件。另外,SELECT *时,USING会去除USING指定的列,而ON不会。实例如下。

自然连接:NATURE JOIN

自然连接就是USING子句的简化版,它找出两个表中相同的列作为连接条件进行连接。有左自然连接右自然连接普通自然连接之分。在t_blog和t_type示例中,两个表相同的列是id,所以会拿id作为连接条件。
另外千万分清下面三条语句的区别 。
自然连接:SELECT * FROM t_blog NATURAL JOIN t_type;
笛卡尔积:SELECT * FROM t_blog NATURA JOIN t_type;
笛卡尔积:SELECT * FROM t_blog NATURE JOIN t_type;

补充

博客开头给出的第一张图除去讲了的内连接、左连接、右连接、外连接,还有一些特殊的韦恩图,这里补充一下。

引用

http://www.cnblogs.com/fudashi/p/6572101.html

http://blog.csdn.net/wjc19911118/article/details/9716391
http://blog.csdn.net/taylor_tao/article/details/7068511

nginx 之 proxy_pass详解

在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。
假设下面四种情况分别用 http://192.168.1.1/proxy/test.html 进行访问。
说明:http://127.0.0.1也可以改成对应的域名或加端口号
第一种:
location /proxy/ {
    proxy_pass http://127.0.0.1/;
       #header头参数也保持
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

代理到URL:http://127.0.0.1/test.html
第二种(相对于第一种,最后少一个 / )
location /proxy/ {
    proxy_pass http://127.0.0.1;
}

代理到URL:http://127.0.0.1/proxy/test.html
第三种:
location /proxy/ {
    proxy_pass http://127.0.0.1/aaa/;
}

代理到URL:http://127.0.0.1/aaa/test.html
第四种(相对于第三种,最后少一个 / )
location /proxy/ {
    proxy_pass http://127.0.0.1/aaa;
}

代理到URL:http://127.0.0.1/aaatest.html

PHP集群session共享

集群的概念没有多复杂,其实就是多台电脑为了同一个目标在一起工作。在Web应用中,就是多个服务器提供一个站点的服务。

搭建PHP集群的第一步就是设置负载均衡。首先我们需要三台主机:

Nginx负载:192.166.5.111
PHP应用1:192.168.5.112
PHP应用2:192.168.5.113

1

先前,在PHP应用所在的主机,我们需要安装Nginx或者apache等这类web服务器,然后再在前面使用Nginx作为负载。Nginx 负载和php应用之间的通信是在应用层的,Nginx负载其实就相当于一个代理。但是,现在情况不同了。Fastcgi技术的应用允许在php应用层可以不用再安装web服务器。现在PHP5.5版本已经将fpm作为内部模块支持了。在这种情况下,Nginx 负载和php应用之间的通信是在传输层的,二者之间使用socket进行通信。当然了,这需要fpm服务的支持。

2

Nginx设置

首先对Nginx(192.168.5.111)进行设置,编辑nginx.conf配置文件

http{
         ……
         upstream onmpw_phpApps{
            server 192.168.18.88:9000;
            server 192.168.18.191:9000;
        }
        ……
       Server{
         listen        80;
         server_name   load.onmpw.com   ##这里是域名
         root           /www/onmpw         
         ……
         location ~ \.php$ {
                   root         /www/onmpw   ##这里是PHP应用所在目录
                   fastcgi_pass   onmpw_phpApps;
                   ……
         }
      }
}

以上是对Nginx进行的设置。其中只是包含了关键的部分,其余的和平常我们使用Nginx+PHP作为web服务的时候进行的设置相同。

PHP所在主机设置

这里的设置就比较简单了。

首先编辑php-fpm.conf文件,修改监听的ip和端口,然后启动fpm服务

主机192.168.5.112

Listen = 192.168.5.112:9000   //这里的端口可以自行设置。保存退出
# /usr/local/php/sbin/php-fpm   //开启服务

主机192.168.5.113

Listen = 192.168.5.113:9000
# /usr/local/php/sbin/php-fpm

到这里就对PHP的主机设置完成了。当然了,代码需要在两台主机上各上传一份儿。

好了,经过上面的设置,一个基本的PHP集群就已经搭建完成了。但是有一个问题,这种情况如果只是访问静态资源或者不进行交互的话是没有问题的。如果需要交互,那就涉及到一个session共享的问题。默认情况下PHP是将session存在本地磁盘上的。那这两台主机之间如何共享session呢,接下来我们就来解决这个问题。

PHP主机之间Session共享

之前在网上看到过一种解决方式。由于PHP是将session存储在文件中,那我们可以在Nginx负载主机上面搭建一个分布式文件系统(NFS),让两台PHP主机的session都存放在此文件系统中。以此来达到共享session的目的。

3

我个人比较倾向于将session存储到数据库中。因此这里我介绍的是将session存储到redis中。所以我们需要增加一台Redis服务器

Redis服务器:192.168.5.114

4

PHP默认情况下是不支持对Redis的操作的。所以这里我们需要自己手动安装第三方的扩展,使其支持对Redis的操作。关于如何使PHP支持Redis,我们可以参考《PHP操作Redis的两种方式》

在这里我就认为我们的PHP已经支持Redis了。接下来是将session存储到Redis中,有两种方式:一种是直接修改PHP的配置文件php.ini;另一种是重写session机制。

修改PHP配置文件php.ini将session存储到Redis

使用vim打开php.ini,需要修改的有这两项:session.save_handler和session.save_path。

session.save_handler = Redis

//不需要密码验证
session.save_path = “tcp://192.168.5.114:6379”

//Redis 需要密码验证
session.save_path = “tcp://192.168.5.114:6379?auth=password”

修改完成,保存退出。然后重启php-fpm服务

# kill -INT `cat /usr/local/php/var/run/php-fpm.pid`
# /usr/local/php/sbin/php-fpm

两台PHP主机都做按照以上步骤操作。经过以上步骤,对于session的所有信息都保存到了Redis中。从而实现了session的共享。

通过重写session机制将session存储到Redis

通常,在很多情况下我们是没有权限修改php.ini文件的。这时候我们可以通过重写session机制来修改session信息的存储。

对于重写session,php已经为我们提供了SessionHandlerInterface 接口。我们只要实现这个接口就可以了。关于如何重写session机制,大家可以参考《PHP重写session机制》这篇文章。该类的完整代码在github上,大家有兴趣的和可以点此查看

总结

PHP集群的架构方式有很多种,但是其原理都大同小异。关键是找出最适合自己项目的最佳方案。例如:对于session存储方式的选择,你也可以选择使用memcache或者mysql数据库等。总之最适合自己的就是最优的。希望本文对大家有所帮助。

如何架构高可用的Web站点

一、什么是高可用

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。

假设系统一直能够提供服务,我们说系统的可用性是100%。

如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。

很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时。

百度的搜索首页,是业内公认高可用保障非常出色的系统,甚至人们会通过www.baidu.com 能不能访问来判断“网络的连通性”,百度高可用的服务让人留下啦“网络通畅,百度就能访问”,“百度打不开,应该是网络连不上”的印象,这其实是对百度HA最高的褒奖。

 

二、如何保障系统的高可用

我们都知道,单点是系统高可用的大敌,单点往往是系统高可用最大的风险和敌人,应该尽量在系统设计的过程中避免单点。方法论上,高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。

保证系统高可用,架构设计的核心准则是:冗余。

有了冗余之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过“自动故障转移”来实现系统的高可用。

接下来我们看下典型互联网架构中,如何通过冗余+自动故障转移来保证系统的高可用特性。

 

三、常见的互联网分层架构

常见互联网分布式架构如上,分为:

(1)客户端层:典型调用方是浏览器browser或者手机应用APP

(2)反向代理层:系统入口,反向代理

(3)站点应用层:实现核心应用逻辑,返回html或者json

(4)服务层:如果实现了服务化,就有这一层

(5)数据-缓存层:缓存加速访问存储

(6)数据-数据库层:数据库固化数据存储

整个系统的高可用,又是通过每一层的冗余+自动故障转移来综合实现的。

四、分层高可用架构实践

【客户端层->反向代理层】的高可用

20170305211812759
【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余来实现的。以nginx为例:有两台nginx,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtual IP提供服务。

20170305211812759
自动故障转移:当nginx挂了的时候,keepalived能够探测到,会自动的进行故障转移,将流量自动迁移到shadow-nginx,由于使用的是相同的virtual IP,这个切换过程对调用方是透明的。

 

【反向代理层->站点层】的高可用

20170305211812759
【反向代理层】到【站点层】的高可用,是通过站点层的冗余来实现的。假设反向代理层是nginx,nginx.conf里能够配置多个web后端,并且nginx能够探测到多个后端的存活性。

20170305211812759
自动故障转移:当web-server挂了的时候,nginx能够探测到,会自动的进行故障转移,将流量自动迁移到其他的web-server,整个过程由nginx自动完成,对调用方是透明的。

 

【站点层->服务层】的高可用

20170305211812759
【站点层】到【服务层】的高可用,是通过服务层的冗余来实现的。“服务连接池”会建立与下游服务多个连接,每次请求会“随机”选取连接来访问下游服务。

20170305211812759
自动故障转移:当service挂了的时候,service-connection-pool能够探测到,会自动的进行故障转移,将流量自动迁移到其他的service,整个过程由连接池自动完成,对调用方是透明的(所以说RPC-client中的服务连接池是很重要的基础组件)。

 

【服务层>缓存层】的高可用

20170305211812759
【服务层】到【缓存层】的高可用,是通过缓存数据的冗余来实现的。

缓存层的数据冗余又有几种方式:第一种是利用客户端的封装,service对cache进行双读或者双写。

 

20170305211812759
缓存层也可以通过支持主从同步的缓存集群来解决缓存层的高可用问题。

以redis为例,redis天然支持主从同步,redis官方也有sentinel哨兵机制,来做redis的存活性检测。

20170305211812759
自动故障转移:当redis主挂了的时候,sentinel能够探测到,会通知调用方访问新的redis,整个过程由sentinel和redis集群配合完成,对调用方是透明的。

 

说完缓存的高可用,这里要多说一句,业务对缓存并不一定有“高可用”要求,更多的对缓存的使用场景,是用来“加速数据访问”:把一部分数据放到缓存里,如果缓存挂了或者缓存没有命中,是可以去后端的数据库中再取数据的。

这类允许“cache miss”的业务场景,缓存架构的建议是:

20170305211812759
将kv缓存封装成服务集群,上游设置一个代理(代理可以用集群冗余的方式保证高可用),代理的后端根据缓存访问的key水平切分成若干个实例,每个实例的访问并不做高可用。

20170305211812759
缓存实例挂了屏蔽:当有水平切分的实例挂掉时,代理层直接返回cache miss,此时缓存挂掉对调用方也是透明的。key水平切分实例减少,不建议做re-hash,这样容易引发缓存数据的不一致。

 

【服务层>数据库层】的高可用

大部分互联网技术,数据库层都用了“主从同步,读写分离”架构,所以数据库层的高可用,又分为“读库高可用”与“写库高可用”两类。

 

【服务层>数据库层“读”】的高可用

20170305211812759
【服务层】到【数据库读】的高可用,是通过读库的冗余来实现的。

既然冗余了读库,一般来说就至少有2个从库,“数据库连接池”会建立与读库多个连接,每次请求会路由到这些读库。

20170305211812759
自动故障转移:当读库挂了的时候,db-connection-pool能够探测到,会自动的进行故障转移,将流量自动迁移到其他的读库,整个过程由连接池自动完成,对调用方是透明的(所以说DAO中的数据库连接池是很重要的基础组件)。

 

【服务层>数据库层“写”】的高可用

20170305211812759
【服务层】到【数据库写】的高可用,是通过写库的冗余来实现的。

以mysql为例,可以设置两个mysql双主同步,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtual IP提供服务。

20170305211812759
自动故障转移:当写库挂了的时候,keepalived能够探测到,会自动的进行故障转移,将流量自动迁移到shadow-db-master,由于使用的是相同的virtual IP,这个切换过程对调用方是透明的。

 

五、总结

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。

方法论上,高可用是通过冗余+自动故障转移来实现的。

整个互联网分层系统架构的高可用,又是通过每一层的冗余+自动故障转移来综合实现的,具体的:

(1)【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余实现的,常见实践是keepalived + virtual IP自动故障转移

(2)【反向代理层】到【站点层】的高可用,是通过站点层的冗余实现的,常见实践是nginx与web-server之间的存活性探测与自动故障转移

(3)【站点层】到【服务层】的高可用,是通过服务层的冗余实现的,常见实践是通过service-connection-pool来保证自动故障转移

(4)【服务层】到【缓存层】的高可用,是通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与sentinel保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性

(5)【服务层】到【数据库“读”】的高可用,是通过读库的冗余实现的,常见实践是通过db-connection-pool来保证自动故障转移

(6)【服务层】到【数据库“写”】的高可用,是通过写库的冗余实现的,常见实践是keepalived + virtual IP自动故障转移

原文:http://lib.csdn.net/article/architecture/65600

 

Linux下如何查看版本信息

Linux下如何查看版本信息, 包括位数、版本信息以及CPU内核信息、CPU具体型号等等,整个CPU信息一目了然。

  1、# uname -a   (Linux查看版本当前操作系统内核信息)
   Linux localhost.localdomain 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 athlon i386 GNU/Linux
  2、# cat /proc/version (Linux查看当前操作系统版本信息)
      Linux version 2.4.20-8 (bhcompile@porky.devel.redhat.com)
      (gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) #1 Thu Mar 13 17:54:28 EST 2003
  3、cat /etc/redhat-release(Linux查看版本当前操作系统发行版信息CentOS还是Ubuntu之类的)
 
  Red Hat Linux release 9 (Shrike)

  4、# cat /proc/cpuinfo (Linux查看cpu相关信息,包括型号、主频、内核信息等)
 

  processor        : 0
     vendor_id         : AuthenticAMD
  cpu family        : 15
  model             : 1
  model name      : AMD A4-3300M APU with Radeon(tm) HD Graphics
  stepping         : 0
  cpu MHz          : 1896.236
  cache size       : 1024 KB
  fdiv_bug         : no
  hlt_bug          : no
  f00f_bug        : no
  coma_bug      : no
  fpu                : yes
  fpu_exception   : yes
  cpuid level      : 6
  wp                : yes
  flags             : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr

                           sse sse2 syscall mmxext lm 3dnowext 3dnow
  bogomips      : 3774.87
  5、# getconf LONG_BIT  (Linux查看版本说明当前CPU运行在32bit模式下, 但不代表CPU不支持64bit)

32
6、# lsb_release -a

lnmp环境给php版本升降级

在日常开发中,难免会遇到服务器环境跟软件环境的php版本不相一致的情况出现,这就涉及到对lnmp环境中的php进行升降级的操作。今日我进行了一次实验,成功对php进行了升降级。步骤如下:

1.关闭lnmp所有服务(包括nginx、php-fpm、mysql)

2.下载最新版lnmp一键安装包:https://lnmp.org/download.html(即便之前安装的不是这个版本的lnmp一键安装包,无关紧要)

3.按这个升级说明,选择想要更换的php版本:https://lnmp.org/faq/lnmp1-2-upgrade.html

4.等待四五十分钟,升级完成后要重启php-fpm,并查看phpinfo.php,看是否升降级成功。

nginx+php出现No input file specified解决办法

今天在自己本地的开发环境突然出现了No input file specified错误,反复检查返现自己的配置文件和配置路径以及权限都没有问题。经过反复的排查终于发现了问题,现将问题及解决分享如下:

问题原因分析

在GitHub上下载了一个开源的tp5项目,之前自己本地的网站运行都没有问题。但是安装了这个开源项目后就发现本地其他网站都无法访问了。访问就是No input file specified错误。在网上也找了解决办法,但是都不是,看来这个错误有点儿诡异。
后来反复尝试,重启电脑后问题得到解决但是再次运行下载的tp5开源项目后其他网站又出现了这样的错误No input file specified 而且只有这一个网站运行没有问题。
据此将错误圈定在该开源项目的nginx配置文件中。再来看看该配置文件:

server {
    listen 80;
    server_name local.test.com;
    access_log /data/wwwlogs/local.test.com.log combined;
    error_log /data/wwwlogs/local.test.com_error.log error;
    index index.html index.htm index.php;
    root /data/php/test;

    add_header X-Powered-Host $hostname;
    fastcgi_hide_header X-Powered-By;

    if (!-e $request_filename) {
        rewrite  ^/(.+?\.php)/?(.*)$  /$1/$2  last;
        rewrite  ^/(.*)$  /index.php/$1  last;
    }

    location ~ \.php($|/){
        fastcgi_index   index.php;
        fastcgi_pass    127.0.0.1:9000;
        include         fastcgi_params;
        set $real_script_name $fastcgi_script_name;
        if ($real_script_name ~ "^(.+?\.php)(/.+)$") {
            set $real_script_name $1;
        }
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param SCRIPT_NAME $real_script_name;
        fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
        fastcgi_param PHP_VALUE       open_basedir=$document_root:/tmp/:/proc/;
    }

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

    location ~ .*\.(js|css)?$ {
        access_log   off;
        error_log    off;
        expires      12h;
    }

在以上的配置中其他都是常规的配置。因为我使用cgi。在fastcgi参数中有一行可能大家也注意到了。

fastcgi_param PHP_VALUE open_basedir=$document_root:/tmp/:/proc/;

就是这句。这句的主要作用是设置fastcgi的可操作目录从而防止跨站的,将open_basedir限定在了本项目的目录和/tmp/以及/proc/中。

问题解决

刚刚说了是在配置的fastcgi配置中多了一句防止跨站的语句。那么这句话他其实是影响了整个fastcgi的参数,这样因为我的其他网站的路径是/data/php/xxx/这样的目录,而不在本开源项目的目录/data/php/test/所以fastcgi就无法找到。
所以在这句之前加#注释这句或者删除这句重启系统或重启nginx就可以了。

线上部署的建议

那么到底要不要使用这句呢?在线上环境中当然是可以的。在线上项目部署中对于open_basedir中最好别使用$document_root这样的变量。如果有多个项目在线上服务器中那么可以把所以项目放置在一个统一的目录中。例如我的线上是wwwroot目录下放置其他网站。例如/wwwroot/test1 /wwwroot/test2那么我可以配置为

fastcgi_param PHP_VALUE open_basedir=/wwwroot/:/tmp/:/proc/;