【转】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

在CentOS 6上安装Kdump

最近把扔在客厅的下载机从Ubuntu换成了centos,每天都会出现kernel panic,为了调试,我选择了Kdump来导出核心vmcore,在看了N多网上说的安装Kdump方法无果之后,果断GOOGLE搜索,终于找到一个可以用的方法。

一.安装kexec-tools

[root@CentOS ~]# yum install kexec-tools

二.添加 crashkernel=64M 在grub.conf中
vi /boot/grub/grub.conf

kernel /vmlinuz-2.6.32-279.14.1.el6.i686 ro root=/dev/mapper/VolGroup-lv_root rd_NO_LUKS rd_NO_MD rd_LVM_LV=VolGroup/lv_swap crashkernel=auto LANG=zh_CN.UTF-8 rd_LVM_LV=VolGroup/lv_root KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet crashkernel=64M

然后重启电脑,待重启后使用free -m会发现总内存比添加之前少了64M。

三.设置Kdump导出的vmcore保存位置。

[root@CentOS ~]# df
文件系统                 1K-块      已用      可用 已用% 挂载点
/dev/mapper/VolGroup-lv_root
                      51606140   1883516  47101184   4% /
tmpfs                   477572         0    477572   0% /dev/shm
/dev/sda1               495844     53469    416775  12% /boot
/dev/mapper/VolGroup-lv_home
                     1868714292 197403328 1576385756  12% /home

mkdir /var/crash
vi /etc/kdump.conf 添加

ext4 /dev/mapper/Volume00-var

四.启动Kdump服务。

[root@CentOS ~]# service kdump start
Starting kdump:                                            [确定]

五.测试Kdump运行

echo 1 > /proc/sys/kernel/sysrq
echo c > /proc/sysrq-trigger

这时系统会马上panic,重启后检查/var/crash 会发现目录中有类似于127.0.0.1-2012-12-17-21:53:58目录生成,则代表Kdump运行正常。

参考:http://linuxczar.net/archives/383

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 

【转】Chrome去除干扰码插件,简单快捷!

来自:http://www.ra1nker.com/99.html

清除隐藏文字的脚本,方便拷贝一些经过特别处理过的网页文字,快捷键alt+q
将以下代码保存为cleanhidemod.user.js。再增加插件

// ==UserScript==
// @name ra1nker.com// @match http://*/*
// ==/UserScript==
var debug=false; //debug or release switch!!
var CleanHideObj=new CleanHideClass();
function CleanHideClass()
{
//functions;id mean cleanhid ,AppID mean call from firefox or mozilla (different! call from overlayFF or overlayMZ!)
this.on=function(id,bMZ)
{
//this.strBundle = document.getElementById("cleanhide-strings"); //get the strings use in js!
if(id == 0)
{
var page=/* document.getElementById("content").contentDocument */document; //main page
this.RemoveHide(page,"span");
this.RemoveHide(page,"font");
this.RemoveHide(page,"div");
this.RemoveHide(page,"p");
//alert(this.strBundle.getString("CleanHideSuccess"));
//document.getElementById("statusbar-display").label=this.strBundle.getString("CleanHideSuccess");
}}
this.getobjstyle=function(node,prop)
{
try
{
return node.ownerDocument.defaultView.getComputedStyle(node, "").getPropertyValue(prop);
}
catch(e)
{
if(debug)
{
alert(e.message);
}return "200";
}
}
this.RemoveHide=function(page,objname)
{
try
{
var minValue=25;
var objs=page.getElementsByTagName(objname); //get all element

if(objs.length == 0) //no span or font object found!
{
if(debug)
{
alert("no object found!:"+objname);
}
}
else
{
//determean every object!
for(var i=objs.length-1;i>=0;i--) //must from last to first!!! for detect every element!!
{
var node=objs.item(i); //get one element

//if this object is invisible,we delete it!!
if(this.getobjstyle(node,"display") == "none" ||
this.getobjstyle(node,"visibility") =="hidden" ||
parseInt(this.getobjstyle(node,"font-size")) == 0) //if this element'style is none,than remove it!!
{
if(debug)
{
alert("remove invisiable!");
}
node.parentNode.removeChild(node);

}
else //normal object!have color!
{
if(node.parentNode) //if it has parent node!!
{
var orgNode=node;
var orgColor=this.getobjstyle(orgNode,"color");
var orgRGB=getRGB(orgColor); //get org node's RGB only color.
var parentNode=node.parentNode
var parentBgColor=this.getobjstyle(parentNode,"background-color");

var isLast=false;
regtest=/rgba\((\s)*0,(\s)*0,(\s)*0,(\s)*0\)/;
while(regtest.test(parentBgColor)) //parent bg is transparent!!,get last no transparent node!!
{

if(parentNode.parentNode == null)
{
isLast=true;
break;
}
parentNode=parentNode.parentNode;
parentBgColor=this.getobjstyle(parentNode,"background-color");
parentBgColor=parentBgColor+"";
if(!regtest.test(parentBgColor))
{

break;
}
}
if(isLast) //body is RGB(255,255,255)!!!
{
if(debug)
{
alert("last!!");
}
if((Math.abs(orgRGB[0]-255)<=minValue)&&(Math.abs(orgRGB[1]-255)<=minValue)&&(Math.abs(orgRGB[2]-255)<=minValue))
{
orgNode.parentNode.removeChild(orgNode); //color is near white,we delete it!
}
}
else
{
if(debug)
{
alert("compare color and parent bgcolor!");
}
parentBgRGB=getRGB(parentBgColor);
if((Math.abs(orgRGB[0]-parentBgRGB[0])<=minValue)&&(Math.abs(orgRGB[1]-parentBgRGB[1])<=minValue)&&(Math.abs(orgRGB[2]-parentBgRGB[2])<=minValue))
{
orgNode.parentNode.removeChild(orgNode); //color is near white,we delete it!
}
}
}//end of hava parent
}//if invisible
}//for ... every object
}//object lentgth == 0

//find sub frames
var itemFrames = page.getElementsByTagName("frame");
var itemiFrames = page.getElementsByTagName("iframe");

var frame, iframe;
if (itemFrames.length > 0)
{
for (var i = 0 ; i< itemFrames.length ; i++)
{
frame = itemFrames[i].contentDocument;
this.RemoveHide(frame,objname); // recursion for frames
}
}
if (itemiFrames.length > 0)
{
for (var i = 0 ; i < itemiFrames.length ; i++)
{
iframe = itemiFrames[i].contentDocument;
this.RemoveHide(iframe,objname); // recursion for iframes
}
}
}
catch(e)
{
if(debug)
{
alert(e.message);
}
}
}

}
//this function for get R G B data from string rgb(r,g,b) or blue,etc
function getRGB(colorString)
{
var RGB = new Array;
var tempSting=colorString.substring(4,colorString.length-1);
var tempArray=tempSting.split(",");

RGB[0]=parseInt(tempArray[0]);
RGB[1]=parseInt(tempArray[1]);
RGB[2]=parseInt(tempArray[2]);

return RGB;
}
function keys(evt)
{ //var evt=evt?evt:(window.event?window.event:null);

if(evt.keyCode == 81 && !evt.shiftKey && !evt.ctrlKey && evt.altKey)
{
CleanHideObj.on(0,1);
alert("Ra1nker:成功去除干扰码");
}

}
document.addEventListener('keydown', keys, false);

【原创】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']; ?>

FreeBSD开机自动同步时间。

最近把VPS系统换成了FreeBSD,发现每次重启之后时间都会不正确,使用以下的方法可以让FreeBSD在开机的时候自动使用NTP来校对时间。

编辑 /etc/rc.conf 加入以下代码:

ntpdate_enable="YES" # Run ntpdate to sync time on boot (or NO). 
ntpdate_program="ntpdate" # path to ntpdate, if you want a different one. 
ntpdate_flags="us.pool.ntp.org" # Flags to ntpdate (if enabled).