“未分类”目录存档

SimCassandra: MemcacheDB是否支持二进制value

2011年03月7日,星期一

<?php

$m = new Memcache();

$m->addServer(”127.0.0.1″, 21201);

$mid = pack(”H*”, “620062″);

$v1 = “/woshao/$mid/v1″;

$v2 = “/woshao/$mid/v2″;

$m->set(”k1″, $v1);

$m->set(”k2″, $v2);

var_dump($v1);

var_dump($v2);

var_dump($m->get(”k1″));

var_dump($m->get(”k2″));

var_dump(bin2hex($m->get(”k1″)));

var_dump(bin2hex($m->get(”k2″)));

?>

(尚未完成……)

foreach、array_map和array_reduce的执行效率

2011年02月27日,星期天

<?php

define(”ELEMENTS”, 10000);

function microtime_float() {

list($usec, $sec) = explode(” “, microtime());

return ((float)$usec + (float)$sec);

}

$a = array();

for($i=0; $i<ELEMENTS; $i++) {

$a[] = array(’id’ => $i);

}

$iStart = microtime_float();

function mapfun($i) {

return $i['id'];

}

$r2 = array_map(”mapfun”, $a);

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

$iStart = microtime_float();

$r1 = array();

foreach($a as $i) {

$r1[] = $i['id'];

}

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

$iStart = microtime_float();

function reducefun($r, $i) {

$r[] = $i['id'];

return $r;

}

$r3 = array_reduce($a, “reducefun”);

$iFinish = microtime_float();

$aCosts[] = $iFinish - $iStart;

var_dump($aCosts);

?>

SimCassandra: MemcacheDB是否支持二进制key

2011年02月24日,星期四

<?php

$m = new Memcache();

$m->addServer(”127.0.0.1″, 21201);

$mid = pack(”H*”, “620062″);

$k1 = “/woshao/$mid/k1″;

$k2 = “/woshao/$mid/k2″;

$m->set($k1, “v1″);

$m->set($k2, “v2″);

var_dump($k1);

var_dump($k2);

var_dump($m->get($k1));

var_dump($m->get($k2));

?>

用户登录的进化史

2011年02月23日,星期三

关于用户登录、验证的常见做法,是在验证用户的用户名和密码之后将用户的ID保存在Cookie中:

user_id=123456

验证的时候,将user_id从Cookie中取出,然后从数据库中获取对应ID的用户:

GET /users/123456

如果获取失败,就重定向到登录界面,否则算验证登录成功。这时候,一般也会看看该用户是否已经被封禁,如果被封的话,同样也会重定向到登录界面。

为了防止通过篡改Cookie的user_id值实现更换用户(也许这个用户本身不属于篡改者),还会保存一个将用户ID和密钥字符串放在一起摘要的摘要字符串(摘要算法可以使用MD5):

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd

这样可以在一定程度上防止冒名顶替的事情发生。

但是现有的网站一般都提供了“记住我”选项,而且一般勾选该选项都会导致用户的登录信息在Cookie中保存一个月甚至更长的时间。

如果在此期间,用户访问了一些不安全的网站,暴露了自己保存在Cookie中的用户信息,由于登录信息的摘要字符串在Cookie消失之前是不会改变的,这就有可能使不安全网站的拥有者使用用户本人的用户信息登录进网站。

为了解决这个问题,我们可以引入login_sum_expires参数,定义该摘要字符串的过期时间(Unix时间戳记):

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd;login_sum_expires=1234567890

同时,login_sum的计算公式中也加入该时间,保证随着过期时间的不同,摘要字符串也不一样。

之后再设置一个比较合适的过期时间(一般是20min),超过这个时间之后,即宣告该摘要字符串过期,同时为其设置一个新的过期时间并随之产生新的摘要字符串。

这样,即使用户被不安全网站窃取了Cookie中的用户信息,20min(甚至更短)之后由于其持有的用户信息已失效,就没有办法冒名顶替了。

真的是这样吗?

事实上,不安全网站拥有者持有该信息进入网站后,网站无法识别其是否是真正的用户。网站会按照一般流程,自动为摘要字符串续期!

为了能够识别用户的合法性,我们可以引入login_ip参数,记录该用户登录时的IP地址:

user_id=123456;login_sum=abcdabcdabcdabcdabcdabcdabcdabcd;login_sum_expires=1234567890;login_ip=222.222.222.222

同时,login_sum的计算公式中也加入该IP地址,保证随着登录IP的不同,摘要字符串也不一样。

这时候,即使用户被不安全网站窃取了Cookie中的用户信息,由于IP地址不同,网站也会视该登录信息失效,从而解决冒名顶替问题。

login_sum_expires参数已无用处!

(未完待续……)

SimCassandra: 比较long型整数

2011年02月22日,星期二

Cassandra的一大特性就是各Column的顺序在插入的时候就已经确定了,这表明Cassandra本身可能使用了插入排序的算法。

虽然之前研究了如何将Unix时间戳记转化为长整型数,但是如果不能进行比较的话就无法完成插入排序。

函数首先将两个长整型数unpack到两个数组中,每个数组4个元素,从1到4,分别代表从低到高的各个字。

$a1 = unpack(”v*”, $l1);

$a2 = unpack(”v*”, $l2);

然后从高位依次进行比较,如果相同就比较低16位,不同就直接返回,直到全部比较完毕。

注意:这里还是不能直接unpack成32位,PHP不能支持32位无符号整型数。

function compareLong($l1, $l2) {

$a1 = unpack(”v*”, $l1);

$a2 = unpack(”v*”, $l2);

for($i=4; $i>=1; $i–) {

$r = (int)$a1[$i] - (int)$a2[$i];

if($r != 0) {

break;

}

}

return $r;

}

$l1 = pack(”H*”, “58ba06b3de9c0400″);

$l2 = pack(”H*”, “58ba06b3de9c0400″);

var_dump(compareLong($l1, $l2));

BytesType的比较可能和LongType类似,只不过比较方向相反,从第一个字节开始。

SimCassandra: 实现timestamp到long的转换

2011年02月21日,星期一

为了实现Cassandra的LongType,必须编写代码实现将Unix时间戳记转化为64位长整型,并以低位在先的binary方式保存在变量中。

在网上找了一些解决方案,大体上都是通过第三方扩展,或者通过将低32位和高32位分开保存,再做处理的。有些方案的结果根本就是错误的。

还好,目前应对的问题仅是Unix时间戳记,而且microtime()函数是将小数点前面的部分和后面的部分分开保存的,这一切都有利于实现这个函数。

首先,和一般的算法一样,将高低32位分开。由于microtime返回的字符串实际代表的浮点数小数部分有8位十进制位,而Cassandra常用的只有6位,因此后两位就在算法中忽略掉。将除数的小数点也向前6位,这样保证得到的结果与直接的整型运算相符。注意:这样做的前提是,我们预先知道Unix时间戳记的高32位代表的数字不会溢出。

$iHi = (int)($sec / 4294.967296);

然后,取得剩余的浮点数,并乘以100,为下一步做准备。

$fRemain = (float)(($sec - $iHi * 4294.967296 + $usec) * 100);

考虑到这是个64位长整型,其低32位只能是无符号整数,PHP也没有办法处理。因此,需要暂时提取中间的16位,保证数据正确。本应该除以0.065536,但是之前已经乘以100了,这里就直接除以6.5536。

$iMed = (int)($fRemain / 6.5536);

继续获取剩余的浮点数。

$fRemain = (float)($fRemain - $iMed * 6.5536);

那么低16位就是剩余数字乘以10000了。

$iLow = (int)($fRemain * 10000);

最后,按照低位在前的方式打包所有部分,并串接起来,得到的变量就是我们要的长整型数据了。这一步是力求接近Cassandra的实现代码,目前还没有考虑Memcache协议是否允许key中包含0×00。如果不允许的话,在保存的时候还需要使用bin2hex转化为字符串。

以下为全部代码:

function get_long_timestamp($sMicrotime = ”) {
if(!$sMicrotime) $sMicrotime = microtime();
list($usec, $sec) = explode(” “, $sMicrotime);
$iHi = (int)($sec / 4294.967296);
$fRemain = (float)(($sec - $iHi * 4294.967296 + $usec) * 100);
$iMed = (int)($fRemain / 6.5536);
$fRemain = (float)($fRemain - $iMed * 6.5536);
$iLow = (int)($fRemain * 10000);
return (pack(”v”, $iLow) . pack(”v”, $iMed) . pack(”V”, $iHi));
}

$sMicrotime = microtime();
var_dump($sMicrotime);
var_dump(bin2hex(get_long_timestamp($sMicrotime)));

下面可能会研究Memcache协议的key中是否允许出现0×00。

分析一下新浪微博API目前的问题

2010年08月22日,星期天

新浪微博在今年上半年的时候开始了开放平台的内测,我烧网作为国内知名的博客阅读社区有幸成为其最初的合作网站,并根据自身需要通过OAuth方式模拟出了类似“人人连接”(人人Connect)的“新浪微博连接”。新浪微博用户可以直接通过微博帐号登录我烧网,无需二次注册。在代码编写和功能测试过程中,我们得到了新浪微博同仁们大力的支持与耐心的指导,在此我深表谢意。

当然,新浪微博开放平台还处于初创阶段,很多问题尚未推敲周全。我最近在编写同步功能的时候就深深体会到了这一点,也就此机会探讨一下问题和可能的解决方案。

1.Access Token永久有效

作为合作网站,我们当然希望用最少的代码完成最多的功能。如果Access Token有时效性,我们就需要使用更多的代码来进行有效性验证。因此,Access Token永久有效让合作网站的开发工作变得十分容易了。但是对于用户来讲就不那么乐观了。也许有些初级用户抱着试试看的态度授权了某个应用,之后就不想再使用了,永久有效的授权就意味着即使这个用户不再使用这个应用,应用本身也可以假借用户本人进行操作。也许你会说:将应用授权取消掉不就行了?然而,我们的大部分用户都是非常初级的用户,他们或许永远无法理解什么叫做“应用授权”,那么他们也就永远无法进行“取消授权”的操作。

不过,对于这个问题,确实是没有什么好的解决方案。如果按照类似Session Key的方式进行处理,就意味着超时过后,应用本身需要用户再次授权。而这对某些异步操作几乎是不可能的(以我烧网为例,同步叨叨到新浪微博的代码会被封装为一个异步的扩展类。但是我们需要在真正执行这部分代码的时候才会知道Access Token是否过期,而且如果真的过期的话,由于这部分代码是异步代码,我们根本就无法让用户再次授权。)。比较折中的方案就是,在授权界面上,给用户留两个选项“长期使用”和“我只是来逛逛”,默认是“我只是来逛逛”。“长期使用”就是像现在这样永久有效的授权,“我只是来逛逛”就只授权24小时。这样可以在一定程度上解决之前提到的问题。

2.授权取消

最近在编写代码的时候,我们发现无论用谁提供的代码(新浪、OAuth、我们自己编写的)都无法成功进行update操作(发布微博信息),新浪微博API永远返回400错误(source parameter(appkey) is missing)。我们知道,使用OAuth访问API是不需要source参数的。这可能是新浪微博API的一种判断机制:如果OAuth验证失败就尝试使用基本验证。因此我们就尝试提供source参数,API马上给我们返回403错误(auth failed)。我们一度认为是我们对OAuth方式POST数据不太理解,为此还查阅了大量的资料,阅读了大量的范例代码,无果。后来重新注册一个新的帐号并且授权我烧网之后,一切问题都不存在了。由此我们意识到很有可能是我们最初使用的Access Token已经取消授权了。那为什么新浪微博没有给予我们这部分通知呢?

Access Token的生命周期,在新浪微博那边应该是非常完整的,只不过没有完整的体现在API上罢了。这部分解决方案主要有两种:回调通知和返回过期状态。

回调通知:就是在用户取消授权的同时,新浪微博访问应用的某个URL,通知应用该用户已经取消了授权,Access Token即时失效。这需要应用开发者在配置应用的时候填写相应的回调地址,而且这不适用于桌面应用。

返回过期状态:就是应用在进行操作的时候,如果Access Token失效,就不要单纯的返回403错误。而是详细的返回如403-1(Access Token过期)、403-2(用户已取消授权)等信息,这样应用可以根据返回值自动更新用户的授权状态,自我完善Access Token的生命周期。

以上观点均出自作者本人,与我烧网无关。

加入我烧网

2009年12月17日,星期四

今后大家可以通过我烧网了解我的博客以及我的最新动态。我的我烧网主页是http://woshao.com/Timandes/。欢迎大家关注我!

认证码:8cf3dc776a84d44020a56a07352b4676

把RSS加入了我烧网

2009年11月20日,星期五

我已正式把我的RSS加入我烧网,我的认证字符串为(acc9f9e7ccc7cd4e4b515baeb3c7b29b),大家现在也可以通过我烧网实时了解我最新更新的文章了。

把RSS加入了我烧网

2009年08月13日,星期四

我已正式把我的RSS加入我烧网,我的认证字符串为(9a681b2748b9f917ca242161099f4508),大家现在也可以通过我烧叨叨实时了解我最新更新的文章了。