【转】PHP环境下Memcache的使用方法

Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。它可以应对任意多个连接,使用非阻塞的网络IO。由于它的工作机制是在内存中开辟一块空间,然后建立一个HashTable,Memcached自管理这些HashTable。Memcache官方网站:http://www.danga.com/memcached,更多详细的信息可以来这里了解。

为什么会有Memcache和memcached两种名称?其实Memcache是这个项目的名称,而memcached是它服务器端的主程序文件名,知道我的意思了把~~~~。一个是项目名称,一个是主程序文件名,在网上看到了很多人不明白,于是混用了。

Memcache的安装

分为两个过程:memcache服务器端的安装和memcached客户端的安装。

所谓服务器端的安装就是在服务器(一般都是linux系统)上安装Memcache实现数据的存储。

所谓客户端的安装就是指php(或者其他程序,Memcache还有其他不错的api接口提供)去使用服务器端的Memcache提供的函数,需要php添加扩展。

PHP的Memcache

connect("db.nowamagic.net", 12000);
	//保存数据
	$mem->set('key1', 'This is first value', 0, 60);
	$val = $mem->get('key1');
	echo "Get key1 value: " . $val ."
"; //替换数据 $mem->replace('key1', 'This is replace value', 0, 60); $val = $mem->get('key1'); echo "Get key1 value: " . $val . "
"; //保存数组 $arr = array('aaa', 'bbb', 'ccc', 'ddd'); $mem->set('key2', $arr, 0, 60); $val2 = $mem->get('key2'); echo "Get key2 value: "; print_r($val2); echo "
"; //删除数据 $mem->delete('key1'); $val = $mem->get('key1'); echo "Get key1 value: " . $val . "
"; //清除所有数据 $mem->flush(); $val2 = $mem->get('key2'); echo "Get key2 value: "; print_r($val2); echo "
"; //关闭连接 $mem->close(); ?>

如果正常的话,浏览器将输出:

Get key1 value: This is first value
Get key1 value: This is replace value
Get key2 value: Array ( [0] => aaa [1] => bbb [2] => ccc [3] => ddd )
Get key1 value:
Get key2 value:

程序代码分析

初始化一个Memcache的对象:$mem = new Memcache;

连接到我们的Memcache服务器端,第一个参数是服务器的IP地址,也可以是主机名,第二个参数是Memcache的开放的端口:$mem->connect(“192.168.0.200”, 12000);

保存一个数据到Memcache服务器上,第一个参数是数据的key,用来定位一个数据,第二个参数是需要保存的数据内容,这里是一个字符串,第三个参数是一个标记,一般设置为0或者MEMCACHE_COMPRESSED就行了,第四个参数是数据的有效期,就是说数据在这个时间内是有效的,如果过去这个时间,那么会被Memcache服务器端清除掉这个数据,单位是秒,如果设置为0,则是永远有效,我们这里设置了60,就是一分钟有效时间:$mem->set(‘key1‘, ‘This is first value’, 0, 60);

从Memcache服务器端获取一条数据,它只有一个参数,就是需要获取数据的key,我们这里是上一步设置的key1,现在获取这个数据后输出输出:

$val = $mem->get(’key1′); 
echo "Get key1 value: " . $val;

现在是使用replace方法来替换掉上面key1的值,replace方法的参数跟set是一样的,不过第一个参数key1是必须是要替换数据内容的key,最后输出了:

$mem->replace('key1', 'This is replace value', 0, 60);
$val = $mem->get('key1');
echo "Get key1 value: " . $val;

同样的,Memcache也是可以保存数组的,下面是在Memcache上面保存了一个数组,然后获取回来并输出:

$arr = array('aaa', 'bbb', 'ccc', 'ddd');
$mem->set('key2', $arr, 0, 60);
$val2 = $mem->get('key2');
print_r($val2);

现在删除一个数据,使用delte接口,参数就是一个key,然后就能够把Memcache服务器这个key的数据删除,最后输出的时候没有结果:

$mem->delete('key1');
$val = $mem->get('key1');
echo "Get key1 value: " . $val . "
";

最后我们把所有的保存在Memcache服务器上的数据都清除,会发现数据都没有了,最后输出key2的数据为空,最后关闭连接:

$mem->flush();
$val2 = $mem->get('key2');
echo "Get key2 value: ";
print_r($val2);
echo "
";

Memcache的使用

使用Memcache的网站一般流量都是比较大的,为了缓解数据库的压力,让Memcache作为一个缓存区域,把部分信息保存在内存中,在前端能够迅速的进行存取。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单台Memcache的内存容量的有限的。我这里简单提出我的个人看法,未经实践,权当参考。

分布式应用

Memcache本来支持分布式,我们客户端稍加改造,更好的支持。我们的key可以适当进行有规律的封装,比如以user为主的网站来说,每个用户都有User ID,那么可以按照固定的ID来进行提取和存取,比如1开头的用户保存在第一台Memcache服务器上,以2开头的用户的数据保存在第二胎Mecache服务器上,存取数据都先按照User ID来进行相应的转换和存取。

但是这个有缺点,就是需要对User ID进行判断,如果业务不一致,或者其他类型的应用,可能不是那么合适,那么可以根据自己的实际业务来进行考虑,或者去想更合适的方法。

减少数据库压力

这个算是比较重要的,所有的数据基本上都是保存在数据库当中的,每次频繁的存取数据库,导致数据库性能极具下降,无法同时服务更多的用户,比如MySQL,特别频繁的锁表,那么让Memcache来分担数据库的压力吧。我们需要一种改动比较小,并且能够不会大规模改变前端的方式来进行改变目前的架构。

我考虑的一种简单方法:

后端的数据库操作模块,把所有的Select操作提取出来(update/delete/insert不管),然后把对应的SQL进行相应的hash算法计算得出一个hash数据key(比如MD5或者SHA),然后把这个key去Memcache中查找数据,如果这个数据不存在,说明还没写入到缓存中,那么从数据库把数据提取出来,一个是数组类格式,然后把数据在set到Memcache中,key就是这个SQL的hash值,然后相应的设置一个失效时间,比如一个小时,那么一个小时中的数据都是从缓存中提取的,有效减少数据库的压力。缺点是数据不实时,当数据做了修改以后,无法实时到前端显示,并且还有可能对内存占用比较大,毕竟每次select出来的数据数量可能比较巨大,这个是需要考虑的因素。

Memcache的安全

我们上面的Memcache服务器端都是直接通过客户端连接后直接操作,没有任何的验证过程,这样如果服务器是直接暴露在互联网上的话是比较危险,轻则数据泄露被其他无关人员查看,重则服务器被入侵,因为Mecache是以root权限运行的,况且里面可能存在一些我们未知的bug或者是缓冲区溢出的情况,这些都是我们未知的,所以危险性是可以预见的。为了安全起见,我做两点建议,能够稍微的防止黑客的入侵或者数据的泄露。

内网访问

最好把两台服务器之间的访问是内网形态的,一般是Web服务器跟Memcache服务器之间。普遍的服务器都是有两块网卡,一块指向互联网,一块指向内网,那么就让Web服务器通过内网的网卡来访问Memcache服务器,我们Memcache的服务器上启动的时候就监听内网的IP地址和端口,内网间的访问能够有效阻止其他非法的访问。

# memcached -d -m 1024 -u root -l 192.168.0.200 -p 11211 -c 1024 -P /tmp/memcached.pid

Memcache服务器端设置监听通过内网的192.168.0.200的ip的11211端口,占用1024MB内存,并且允许最大1024个并发连接。

设置防火墙

防火墙是简单有效的方式,如果却是两台服务器都是挂在网的,并且需要通过外网IP来访问Memcache的话,那么可以考虑使用防火墙或者代理程序来过滤非法访问。一般我们在Linux下可以使用iptables或者FreeBSD下的ipfw来指定一些规则防止一些非法的访问,比如我们可以设置只允许我们的Web服务器来访问我们Memcache服务器,同时阻止其他的访问。

# iptables -F
# iptables -P INPUT DROP
# iptables -A INPUT -p tcp -s 192.168.0.2 –dport 11211 -j ACCEPT
# iptables -A INPUT -p udp -s 192.168.0.2 –dport 11211 -j ACCEPT

转自:http://www.nowamagic.net/librarys/veda/detail/332

【转】PHP array操作10个小技巧

其实任何一门计算机语言中对array(数组)的操作都是一门学问,PHP也不例外。下面笔者想向各位介绍关于PHP中array操作的10个小技巧及相关的函数。
1、向array中添加元素
php是一个弱类型语言。因此不必象c语言那样为php array声明长度。向其中添加元素的过程也是声明和初始化的过程。

$capitals = array(
  'Alabama' => 'Montgomery',
  'Alaska'  => 'Juneau',
  'Arizona' => 'Phoenix'
);

继续添加元素也很简单

$capitals['Arkansas'] = 'Little Rock';

如果不是关联数组而只是数字索引的数组可以使用array_push()和array_unshift()函数增加元素
2、从array中删除元素
从数组中移除元素可以使用unset() 函数

unset($capitals['California']);

也可以使用array_pop()或array_shift()函数从数组头或尾部顺序移除元素
3、array键值互换
如果希望新建数组的键是老数组的值而值是老数组的键,简而言之就是键值对调,则可以使用array_flip()函数完成操作

$capitals = array(
  'Alabama' => 'Montgomery',
  'Alaska'  => 'Juneau',
  'Arizona' => 'Phoenix'
);
$states = array_flip($capitals);
// $states = array(
//  'Montgomery' => string 'Alabama',
//  'Juneau'     => string 'Alaska',
//  'Phoenix'    => string 'Arizona'
// );

4、合并数组
如果希望将两个或多个数组合并成一个新的数组,array_merge()函数可以帮上这个忙^_^

$stateCapitals = array(
  'Alabama' => 'Montgomery',
  'Alaska'  => 'Juneau',
  'Arizona' => 'Phoenix'
);
$countryCapitals = array (
  'Australia' => 'Canberra',
  'Austria'   => 'Vienna',
  'Algeria'   => 'Algiers'
);
$capitals = array_merge($stateCapitals, $countryCapitals);

5、修改array中的值
譬如希望将数组的中值全部改为小写后大写首字母,使用回调函数递归地对每个数组成员调用是个不错的方法,php中这个函数是php_map()

function capitalize($element)
{
  $element = strtolower($element);
  return ucwords($element);
}
$capitals = array(
  'Alabama' => 'montGoMEry',
  'Alaska'  => 'Juneau',
  'Arizona' => 'phoeniX'
);
$capitals = array_map("capitalize", $capitals);

6、根据array的键为数组排序

$capitals = array(
  'Arizona' => 'Phoenix',
  'Alaska'  => 'Juneau',
  'Alabama' => 'Montgomery'
);
ksort($capitals);

7、随机化array元素的顺序

$capitals = array(
'Arizona' => 'Phoenix',
'Alaska' => 'Juneau',
'Alabama' => 'Montgomery'
);
shuffle($capitals);

8、查找键或值是否存在
查找是否存在值使用in_array()函数

$capitals = array(
  'Arizona' => 'Phoenix',
  'Alaska'  => 'Juneau',
  'Alabama' => 'Montgomery'
);
if (in_array("Juneau", $capitals))
{
  echo "Exists!";
} else {
  echo "Does not exist!";
}

查找是否存在键使用array_key_exists()函数

$capitals = array(
  'Arizona' => 'Phoenix',
  'Alaska'  => 'Juneau',
  'Alabama' => 'Montgomery'
);
if (array_key_exists("Alaska", $capitals))
{
  echo "Key exists!";
} else {
  echo "Key does not exist!";
}

9、数组查找
这个是老生常谈了,基本上都用的到array_search()函数

$capitals = array(
  'Arizona' => 'Phoenix',
  'Alaska'  => 'Juneau',
  'Alabama' => 'Montgomery'
);
$state = array_search('Juneau', $capitals);
// $state = 'Alaska'

10、使用php标准函数库
一口气介绍这个多操作array的函数,如果您还觉得不过瘾,可以继续查看Standard PHP Library 中的内容^_^

$capitals = array(
  'Arizona' => 'Phoenix',
  'Alaska'  => 'Juneau',
  'Alabama' => 'Montgomery'
);
$arrayObject = new ArrayObject($capitals);
foreach ($arrayObject as $state => $capital)
{
  printf("The capital of %s is %s
", $state, $capital); } // The capital of Arizona is Phoenix // The capital of Alaska is Juneau // The capital of Alabama is Montgomery

转自:http://www.cnphp.info/10-tips-for-php-array-manipulation.html

php从一组数字(字符)随机选择一个数字或者一串字符

转自:http://www.blogguy.cn/show-376-1.html

第一个函数: 随机选择一串数字或者字符中的一个。

function GetID(){         
 $characters = array("99","149","150","151","145","146","147","141","142","143","137","138","139","133","134","135","129","130","131","125","126","127","96","97","98","105","106","107","108","109","110","111","112","115","116","117","118","119","120","121","122","123","103","102","100","153","154","155");   
 shuffle($characters);   
 $ReqID = $characters[mt_rand(0, count($characters))];   
 return $ReqID;   
  }         
  
  $ReqID .= GetID();   
  
  echo $ReqID;    

第二个函数:指定字符,返回指定长度的随机串

function code($nc, $a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {   
   $l=strlen($a)-1; $r='';   
   while($nc-->0) $r.=$a{mt_rand(0,$l)};   
   return $r;   
}   
echo(code(10));  

第三个函数:指定字符返回8位随机串

$acceptedChars = 'azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQSDFGHJKLMWXCVBN0123456789';    
$max = strlen($acceptedChars)-1;   
$password = null;   
for($i=0; $i < 8; $i++) {    
 $password .= $acceptedChars{mt_rand(0, $max)};    
}   
echo $password."

";

第四个函数:指定字符,返回序列号格式的字符串

function GetID($x){         
  
    $characters = array("A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5", "6", "7", "8", "9");   
    shuffle($characters);   
  
    for (; strlen($ReqID)<$x;){   
      $ReqID .= $characters[mt_rand(0, count($characters))];   
    }   
  
    return $ReqID;   
       
  }         
  
  
  $ReqID .= GetID(3);   
  $ReqID .= "-";   
  $ReqID .= GetID(4);   
  $ReqID .= "-";   
  $ReqID .= GetID(3);   
  
  echo $ReqID 

【原创】Nginx Accesskey使用COOKIE作为KEY

网上有一篇文章是介绍使用COOKIE作为ACCESSKEY的KEY值的文章,以下是完整的方案:

NGINX 虚拟主机配置:

server
  {
    listen 80;
    server_name  www.yourdomian.com;
    root  /home/wwwroot/test;

if ( $http_cookie ~* " ng_cookie=(.+?)(?:;|$)")
{
    set $ng_cookie $1;
}

location /down/ {
  accesskey             on;
  accesskey_hashmethod  sha1;
  accesskey_arg         "token";
  accesskey_signature   "jzbk$ng_cookie";

}

location ~ .*\.(php|php5)?$ {
include php.conf;
        }

}

PHP部分:

0) $r.=$a{mt_rand(0,$l)}; 
   return $r; 
}
$key=code('32');//生成一个32位的随机值

$File='pure-ftpd'; //文件名

if(!isset($_COOKIE['ng_cookie'])) {
setcookie("ng_cookie",$key,time()+1800); //设置COOKIE有效期半小时
header("Refresh: 0"); //刷新页面
}

$token=sha1(jzbk.$_COOKIE['ng_cookie']); //KEY

echo '
'; echo "http://www.yourdomian.com/down/$File?token=$token"; //下载地址 echo '
'; echo $_COOKIE['ng_cookie']; ?>

WordPress前台不加载自带jQuery,去版本号等信息

现在很多WordPress主题和插件都使用jQuery,而且一般会直接调用Google的jQuery库,减轻我们服务器的负担,但WordPress本身会加载jQuery,这样就造成了重复载入,如下图所示:

那么如何让WordPress前台不加载本身jQuery?其实很简单,直接打开/wp-includes/script-loader.php文件,查找“jquery.js”字符串,在第122行能找到这么一句:

	$scripts->add( 'jquery', '/wp-includes/js/jquery/jquery.js', array(), '1.7.2' );

由于在后台必须得用到这个库,我们不能将这一句直接删除或者注释掉。那么加个判断,让这一句只在后台的时候运行就信了,将其改为:

	if(is_admin()){
	$scripts->add( 'jquery', '/wp-includes/js/jquery/jquery.js', array(), '1.7.2' );}

这样就OK啦!

注:由于直接修改了WordPress核心文件,如果你以后进行版本升级,记得查看这次修改是否被覆盖!

去掉版本号等信息

WordPress默认会加载wp_head,里面包含了版本号在内的很多信息,如下图所示:

如何去除这些多余的信息呢?也许你会说直接删除,但是你安装的插件一般都需要wp_head来加载插件必须的css和JS文件,所以我们不能直接删除。只要在主题根目录下的function.php中加入如下代码:

//移除顶部多余信息
function wpbeginner_remove_version() { 
return ; 
} add_filter('the_generator', 'wpbeginner_remove_version');//wordpress的版本号
remove_action('wp_head', 'feed_links', 2);// 包含文章和评论的feed 
remove_action('wp_head', 'index_rel_link');//当前文章的索引 
remove_action('wp_head', 'feed_links_extra', 3);// 额外的feed,例如category, tag页 
remove_action('wp_head', 'start_post_rel_link', 10, 0);// 开始篇 
remove_action('wp_head', 'parent_post_rel_link', 10, 0);// 父篇 
remove_action('wp_head', 'adjacent_posts_rel_link', 10, 0); // 上、下篇. 
remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 );//rel=pre
remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0 );//rel=shortlink 
remove_action('wp_head', 'rel_canonical' ); 
remove_action('wp_head', 'wlwmanifest_link'); // 外部编辑器
remove_action( 'wp_head','rsd_link');//ML-RPC

P.S:大部分内容来自:http://www.cmhello.com/remove-wordpress-jquery-and-other-information.html

使用SAE的jQuery节约又拍流量

使用又拍云加速BLOG几天了,在又拍后台的日志里看到,每日使用流量最多的就是jquery,为了节约又拍的流量,并且更好的加速,我们把jquery换成SAE的链接,方法如下。

P.S:如果你和我一样在WP中设置了CDN,我觉得只有方法1适合你!

1.在主题的header.php中找到相关语句改成一下样式:




2.或者是在主题的functions.php添加以下的代码:

//Making jQuery Google API in SAE
function my_scripts_method() {
if (!is_admin()){
wp_deregister_script( 'jquery' );
wp_register_script( 'jquery', 'http://lib.sinaapp.com/js/jquery/1.7.2/jquery.js');
wp_enqueue_script( 'jquery' );
}
}
add_action(‘wp_enqueue_scripts‘, ‘my_scripts_method’);

如果你需要其他版本,可以访问 http://lib.sinaapp.com/?path=/jquery 获取链接。

[转]WordPress在Nginx中启用Apache的mod_rewrite

其实不是真的可以另到 Nginx 支持 Apache 的 mod_rewrite 的.因为如果你的web服务器是 Nginx ,那么你的 WordPress 就会提示你的服务器环境不支持 mod_rewrite 了,但实际上是支持的,例如安装了 WP Super Cache 这插件就有这样的提示了,现在我们只要添加一行代码就可以关闭这个提示,欺骗WordPress ,让它认为是支持 mod_rewrite 方法是在主题的 functions.php 文件添加下面的代码:

add_filter('got_rewrite', 'nginx_has_rewrites');
function nginx_has_rewrites() {
    return true;
}

另外,因为Nginx运行PHP是用PHP-FPM的,所以有时会导致WordPress认为php没有写权限,表现为无法自动升级,和升级插件等,下面这个代码可以修改这个错误.

add_filter('filesystem_method', 'nginx_make_filesystem_direct');
function nginx_make_filesystem_direct() {
    return 'direct';
}

来自:http://imcat.in/wordpress-on-nginx-apache-mod_rewrite-enabled/

[转载]准确判断PHP上传的文件头

上传文件的功能是各类站点的基本功能,但是网络上也存在一些用心不良的人伪装图片或其他文件,上传一些木马或病毒到服务器上。普通php函数文件类型函数无法正确识别,安全的做法应该是头文件判断:

";
echo "gif:".checkTitle('img/weather.gif')."
"; echo "gif:".checkTitle('img/7768752.gif')."
"; echo "gif:".checkTitle('img/gif001.gif')."
"; echo "gif:".checkTitle('img/gif002.gif')."
"; echo "gif:".checkTitle('img/gif007.gif')."
"; echo "jpeg:".checkTitle('img/weather.jpg')."
"; echo "jpeg:".checkTitle('img/0001.jpg')."
"; echo "jpeg:".checkTitle('img/1.jpg')."
"; echo "jpeg:".checkTitle('img/0002.jpg')."
"; echo "jpeg:".checkTitle('img/4.jpg')."
"; echo "jpeg:".checkTitle('img/5.jpg')."
"; echo "jpeg:".checkTitle('img/71885.jpg')."
"; echo "jpeg:".checkTitle('img/imgad.jpg')."
"; echo "png:".checkTitle('img/weather.png')."
"; echo "png:".checkTitle('img/1.png')."
"; echo "png:".checkTitle('img/2.png')."
"; echo "png:".checkTitle('img/3.png')."
"; echo "png:".checkTitle('img/4.png')."
"; echo "png:".checkTitle('img/5.png')."
"; echo hexdec(89)."
"; echo hexdec(50)."
"; //6072,102119 ?>

使用WP Super Cache+CDN Sync Tool自动同步图片损坏的解决办法

今天终于把大家推荐的W3TC给换掉了,原因无他。消耗资源过多,并且效果不好。根据各种GOOGLE发现可以使用CDN Sync Tool+WP Super Cache来替代W3TC,在经过各种测试之后发现通过CDN Sync Tool自动同步到又拍云的图片全部都无法打开,把图片下载回本地发现无法打开,直接报错,如下图:

 

在使用Beyond CompareHA的16以16进制方式对文件进行对比后发现从又拍云中取回的文件比原始的多了一些0D并且比原始文件要稍微大一点。

根据各种GOOGLE之后得知,CDN Sync Tool使用的是FTP_ACSII方式对文件进行上传的。

 

ASCII模式
复制时候会进行调整,主要体现为对不同操作系统的回车/换行/结束符等进行转译。
比如,回车符号在Unix下是\n(0A),Windows下是\r\n(0D0A),Mac下是\r(0D)。当在一个Windows操作系统上用ASCII方式从Unix服务器上下载文件时——无论是文本文件还是二进制文件——都会进行检测和转换:每检测到一个0A,则认为是回车符号,自动插入0D形成Windows下的回车符。显然,如果下载的是文本文件,这种转换是很有用的——我们能在Windows下看到分行后的文本,否则我们看到的是中间夹杂着小黑方块的不换行的一堆文字;然而如果下载的是二进制文件(比如exe或rar),这种转换无异于画蛇添足,破坏了整个文件。

 

根据GOOGLE到的东西,我们来对源码做一点小手术,强制CDN Sync Tool来使用FTP_BINARY的二进制方式来上传文件。

1.打开 wp-content/plugins/cdn-sync-tool/lib/Cst.php
2.找到 ftp_put($this->cdnConnection, $filename, $file, FTP_ACSII); 这一句
3.修改成 ftp_put($this->cdnConnection, $filename, $file, FTP_BINARY);

现在再重新上传图片,就不会出问题了。