用SAE的cron服务做站外cron job任务触发

网站的cron计划任务并不能简单地用后端语言(php)来解决,如果是Linux系统并且有权限可以使用cron的话当然很简单,但是如果不能使用系统的cron那就苦逼了。
邮件队列,数据抓取,数据(库)备份各种任务只能使用http请求来触发,就是说没人访问就不能进行各种操作。

wordpress上使用了备份到dropbox的插件,即使设定了定时备份时间,没有人访问的话,意味着这个备份根本无法完成。而且如果访问量少的话,考虑到脚本运行时间限制,一次备份可能要几天才能完成。

用php试过用set_time_limit()和sleep()以及一个循环来实现长时间脚本的运行,但占资源,影响了网站正常访问。

通用的解决方案就是在每个页面用javascript文件来发送请求,或者放张图片,地址为php脚本地址,像某些用图片统计的方式。最终都是为了发送个http请求。而且不能达到所谓的定时,更没有精度一说了。

有服务器管理权限的话,开个浏览器窗口用javascript定时发送请求也是解决方法。

除了直接用使用本站脚本进行http行为触发,一般用第三方的cron job服务。

之前用过几个国外的服务,基本上免费的都是time out时间不超过15秒,间隔必须在8分钟以上,而且每个月要登录再次激活,而且有的不提供log。

然后SAE上线了,即使开始用云豆计费(100云豆=1元),使用起来还是很便宜,具体没计算过。主要是绑定域名要备案,不知道多少人因为这个放弃或者直接用反向代理了。如果SAE像点点一样有个国外的免费代理服务也行啊。(3.24 SAE支持绑米,通过独立域名访问流量费为原来2倍)

跑题了,SAE提供了丰富的网站服务,包括MySQL,Storage,Memcache,Cron,Mail,TaskQueue,DeferredJob等通用服务,还有分词,地址信息服务,短信服务,当然后面两个基本是要收费的。

cron服务timeout时间是300s,可以某个时间点执行,也可以是每隔一定时间执行。 每个App最多32条cron,任务的间隔的最小时间衡量单元是分钟。更多设定请看文档。http://sae.sina.com.cn/?m=devcenter&catId=195

试了用cron做站外定时请求,只要在网站代码的config.yaml文件里直接写cron服务就行了。
name: test
version: 1
accesskey: somekey
cron:
– description: test
url: index.php
schedule: every 1 mins
timezone: Beijing

由于脚本路径只能是相对路径。因此多走一步用fetchurl服务

$f = new SaeFetchurl();
$content = $f->fetch(‘http://example.com/cron.php’);

每分钟进行的定时计划,根据SAE的计费方式,进行超过30个小时的cron后,没有出现错误,计费如下:
HTTP:0.03云豆
CRON:0.21云豆
Fetch URL:0.03云豆
总计才0.27云豆。

用sae的好处是易用,定时精确度高,有日记,便宜(用送的云豆也算免费了),稳定,并且支持附加数据进行权限验证。账户里还有几千云豆,用来做cron任务好像根本用不完。

有没有其他玩法?天气预报数据的自动更新,rss数据的定时抓取,定时计算排行榜(当然最好是SAE上的),还有服务器状态的检查(像dnspod的服务器状态检测和短信提醒)等。结合dropbox的话,本地(dropbox)markdown格式文章的自动发布和更新等等。如果网站在SAE上当然用SAE的网站服务更加方便。

命名纠结

网站目录命名纠结

javascript的文件夹是要用javascript还是js,还是scripts?
图片文件夹是要用images,image还是img?
css文件夹是要用css,styles,还是style?
单数还是复数这是个很纠结的问题。
MVC模型中是要用model,view,controller,还是它们的复数形式?
纠结久了,也没有一个规范。一个网站和另一个网站命名方式可能都不一样。

想仔细,发现前端文件夹的命名其实就三类:
1.最简洁的方式: js,css,img
2.单数形式:javascript,style,image
3.复数形式:scripts,styles,images

可是实际情况可能是三种方式都用了。所以最终决定全部使用最简洁的形式,即第一种,url中也减少了字符数。
其他采用单数形式,例如上传文件夹用upload而不是uploads。

api返回数据命名纠结

一开始的命名完全随意的,后来纠结于是要用error还是用code还是用error_code表示错误代码。用要用message还是用msg表示错误信息。
最后基本是采用 {“error”:error-code,”message:”message”,”data”:some-data}这样的形式。
规范如下:
error表示错误代码,必需,整数,值为0时表示正常,此时message可选。当error为其他值时,即表示出错状态,此时message为必填。
data为可选。

QQ登录的返回数据
{“ret”:2021,”msg”:”请先登录”}
{“error”:100007,”error_description”:”param access token is wrong or lost “}

刚好看到了这篇博文,http://www.jsbug.com/blog/?p=477 发现命名规范基本相同,但是觉得命名更合理一些。
{"code": code-value, "msg": msg-value, "data": data-value}

例如error语义不明,是错误字符还是错误数值。message和msg明显msg字符少,而且msg大家都懂,QQ的ret一开始没看懂。

说到底api返回数据的命名只是给开发者看以及确保以后维护方便,要是早点定个规范也就不会那么混乱了。

url命名纠结

习惯网站的操作统一用一个api命名的controller来处理,url使用api/module/action的形式。
关于CRUD,开始的时候,纠结在于删除是用delete还是del,更新是用update还是edit,新增(插入)是用add,new,create还是insert。嗯,就是get完全没不纠结了。
最后统一使用 add,get,update,del

html5需要个picture标签?

responsive design 并不是 responsive image,它能创建适应性的布局可以缩放图片但不能智能加载适应屏幕大小的图片。在小尺寸屏幕上缩放大图片确定是浪费流量浪费时间的事。

解决方法一是后端检测浏览器信息输出不同的img地址,responsive design目的就是为了将前端行为与后端分开,这样做耗力耗时间而且浪费服务器资源。
二是前端用javascript检测屏幕大小(包括resize行为),自动加载适应屏幕大小的图片,但是用这样的方法,img标签的src属性是动态生成的,如果不幸javascript被禁用,那图片就显示不出来了。当然可以放张小图,但是总觉得麻烦。

我们只能寄望于html增加新的标签并且浏览器原生支持实现responsive image的功能。

Scott Jehl 提出的解决方式是用 标签和Media Query 让浏览器自动选择合适的图片。当然现在只能用javascript来实现了。不过这是一种十分可行而且语义十足的方式,希望在某一天可以被w3c列入html规范。

代码如下:

<picture alt="A giant stone face at The Bayon temple in Angkor Thom, Cambodia">
<!-- smallest size first - no @media qualifier -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg">
<!-- medium size - send to viewport widths 400px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5.jpg" media="(min-width: 400px)">
<!-- large size - send to viewport widths 800px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_z.jpg" media="(min-width: 800px)">
<!-- extra large size - send to viewport widths 1000px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_b.jpg" media="(min-width: 1000px)">
<!-- extra large size - send to viewport widths 1300px wide and up -->
<source src="http://farm8.staticflickr.com/7144/6547286841_c6160b34e2_o.jpg" media="(min-width: 1200px)">
<!-- Fallback content for non-JS or non-media-query-supporting browsers. Same img src as the initial,     unqualified source element. -->
<noscript><img src="http://farm8.staticflickr.com/7144/6547286841_635bbd97e5_m.jpg" alt="A giant stone face     at The Bayon temple in Angkor Thom, Cambodia"></noscript>
</picture>

原文在此:http://www.w3.org/community/respimg/2012/02/21/a-sample-picture-implementation/
demo在此:http://scottjehl.com/picturefill/
github:https://github.com/scottjehl/picturefill

##问题
因为浏览器不支持,即使google也还不知道有这样的标签。在这样的代码中是没有img标签的,爬虫并不能像原来一样找到img标签并列入索引。那么是否会对SEO造成很大的影响(尤其是图片网站)。即使某一天支持了,google认识了新的标签,但是百度什么的不认怎么办(连robots.txt都不大认识)。

HTML5 Page Visibility API

说明

HTML5的页面可见性API(暂时这么叫吧)可以用来检测页面对于用户的可见性,如果用户打开另一个tab上或者在其他窗口上,那么我们可以通过该API来检测变化并作出相应的操作。这个和android的生命周期有点相似。虽然IE只有在IE10才支持,不过给了个十分清晰的例子。链接见最后。

然后我们想到在这个API出来前我们是怎么检测页面可见性的?
在window上绑定onblur/onfocus可以检测页面是否处于激活状态(active),但不能检测到可见状态。
当然通过检测窗口最小化可以知道页面肯定是不可见了,但是似乎没有兼容所有浏览器的方法。
参考:https://github.com/evilmartians/visibility.js

它可以做什么

  • 减少内存使用
    停止或者暂停不可见页面的耗资源操作。

  • 节省服务器资源
    举例,当一个进行ajax轮询的页面不可见时,通过关闭轮询或者加大间隔时间可以节省一定的服务器资源。

  • 视频暂停和自动开始
    视频或者动画、游戏(不只是flash,包括html5动画)播放时如果需要切换到其他页面,用户不再需要点暂停,而且切换到该页面时自动从中断点播放。
    demo:http://www.samdutton.com/pageVisibility/

属性

document.hidden(document.webkitHidden)

如果页面不可见,返回true,否则返回false.不支持返回undefined。

document.visibilityState(document.webkitVisibilityState)

  • “visible” 当前页面至少部分可见
  • “hidden” 页面不可见(可能是后台tab或者最小化了)
  • “prerender” 预渲染中并且不可见,该状态可以是开始状态并且向其他两个状态变化但不可能从其他状态转变到该状态

使用

监听事件visibilitychange(webkitvisibilitychange)

//startSimulation and pauseSimulation defined elsewhere
function handleVisibilityChange() {
  if (document.webkitHidden) {
    pauseSimulation();
  } else {
    startSimulation();
  }
}
document.addEventListener("webkitvisibilitychange", handleVisibilityChange, false);
/*
 * If this is a browser that doesn’t support this API, we should
 * start the simulation as soon as this part of the script executes.
 * If it does support the API, then we should start if we’re already
 * visible at this point.  Otherwise, we’ll wait until we’re told
 * that we should start.
 *
 * Browsers that don't support this API will return undefined (a false-y
 * value) for document.webkitHidden, effectively defaulting to visible.
 */
  if (!document.webkitHidden) startSimulation();

参考

  • http://www.aoao.org.cn/blog/2012/01/pause-page/
  • http://www.igvita.com/2011/06/25/html5-visibility-api-page-pre-rendering/
  • W3:http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html
  • Google:http://code.google.com/intl/zh-CN/chrome/whitepapers/pagevisibility.html
  • Firefox:https://developer.mozilla.org/en/DOM/Using_the_Page_Visibility_API
  • IE:http://ie.microsoft.com/testdrive/Performance/PageVisibility/Default.html

【未完】

x-webkit-speech:为搜索框增加语音输入功能

现在一个大网站没有一个这样的语音搜索都不好意思出来跟别人打招呼了,用不用是一回事,有那么一个麦克风在那总觉得有一点酷(你也可以说装逼)。google chrome满足了我们这些没事折腾的人的装逼愿望。

试试:

实际上w3c对语音输入有官方的说明:http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html
使用语音输入作用有:
1.提供除了键盘和鼠标的另一个输入方法,针对pc用户,当键盘不可用时也可以使用语音输入。
2.为手机用户提供更加方便的输入方法。

1.支持的浏览器

x-webkit-speech是webkit内核浏览器的私有属性(废话)。但现在只能在google的chrome 11以上才能使用。
实现过程大概是捕捉到语音后,数据发送到google的服务器进行语音识别,然后返回结果。所以没有足够强大大的研发能力和服务器资源,真没法支持这个服务。
作为普通话不标准经常被别人吐槽的人,使用语音搜索还是能十分准确地返回关键词,我顿时感动得一塌糊涂。

2.支持的标签

输入标签有input和textarea,实际上目前只有input支持。

3.检测浏览器是否支持

if (document.createElement("input").webkitSpeech === undefined) {
    alert("Speech input is not supported in your browser.");
}

4.直接使用

为input标签加上x-webkit-speech属性

<input type="search" x-webkit-speech />

捕捉到语音输入后会直接将关键词填入到输入框里。

5.监听输入

若要监听输入变化以便做其他处理,使用onwebkitspeechchange属性添加处理函数。

<input type="search" x-webkit-speech onwebkitspeechchange="onChange()"/>
<script type="text/javascript">
    function onChange() {
        alert('changed');
    }
</script>

6.注意:

如果原input中value不为空,输入会直接添加在原有文字后面。既然用webkit就要用placeholder了,不要再使用value为作输入提示了。

7.其他

不小心发现了编码为中文的网页中读fuck会直接显示fuck,在编码为英文(en)的网页中读fuck会显示f***。这难道是说英文中fuck是粗话,中文中fuck就成正常问候语了?有兴趣的同学自己试试。

立冬

今天是立冬。起得很晚。6点半醒,然后一段一段地做梦。过去和现在杂揉在一起,醒来又想不起什么。

昨天早上收到了博姿的礼物。软软的。拆开是一条黑白相间的围巾。发短信说能不能明天才拆。想想明天11月8日不是什么特殊的日子。我只记得11月11日。上网查了阴历,才发觉明天是个几乎从未记起也算不上遗忘的日子。因为快递的过于神速礼物提前一天送到了。

而对于我来说这仅仅让我可以轻易做个好好睡一觉或者明天开始重新做人的决定。

博姿说,做教师是件特别伤感情的事。想象你的感情一年一年,一批一批的溜走。哪有那么多可以流的。这是原话。我无以回应。只能说节哀吧。

从离开高中起就没回过普宁,没回过二中,没问候过高中的老师。离开广东到武汉。然后转眼三年过去。

依旧无法忍受这个城市的灼灼夏天和凛冽冬日。季节永远的棱角分明。

武大有很多的可爱之处,可是这个城市没有。

于是逃离这里成了一个最理所当然的目标。回南方吧,回广东吧。

大学写了不多的信,收到比我回信多的信件。因为邮政的关系,有些信没有收到。因为自己的各种原因,有些信没有回复。然后日子这样一路跑到了大四。想想很后悔。

一个人遗忘的总会有另一个人帮你记起。包括那些你向别人提起的理想。但已经不好意思说因为太忙忘记了。

删掉那些矫情的表达,以为自此风月无关,却依旧被一些小温暖弄得手足无措,难以淡定。

昨天还是为域名续了费,生活还在继续,不记录点什么总觉得对自己的生活不忠。总在写代码的时候想要去写技术总结,然后在结束后却又很懒。

这些都是我的生活,断断碎碎,悲喜交加。

Thank you for reading.

[END | winter of 2011]

用userdata和localstorage做跨浏览器本地储存

为网站做一个搜索历史本地储存,想法是对于ie外的浏览器可以直接使用localstorage,但是对于不争气的IE,难道只能使用cookies?
然后搜到hacker news上的一篇文章。
Store.js – cross browser local storage without using cookies or flash (github.com)

http://github.com/marcuswestin/store.js

于是才知道IE下的userData。

1.浏览器支持

userData是微软为IE在系统中开辟的存储空间。因此只支持windows+IE。意外的是从IE5.5就已经开始userData了。

2.保存位置

在XP下,一般位于C:\Documents and Settings\用户名\UserData,有些时候会在C:\Documents and Settings\用户名\Application Data\Microsoft\Internet Explorer\UserData。
在Vista下,位于C:\Users\用户名\AppData\Roaming\Microsoft\Internet Explorer\UserData。
userData的保存形式为XML文件。下面是支付宝保存的userData数值。
alipayuserdata[1].xml>>

3.大小限制

Security Zone Document Limit (KB) Domain Limit (KB)
Local Machine 128 1024
Intranet 512 10240
Trusted Sites 128 1024
Internet 128 1024
Restricted 64 640
线上使用时,单个文件大小限制为128KB,一个域名下文件大小限制为1024KB,文件数应该没有限制。在受限站点里这两个值分别是64KB和640KB,所以如果考虑到各种情况的话,单个文件最好能控制64KB以下。

4.使用

userData可以绑定到,几乎所有标签上。
官方文档还加了说明:
Setting the userData behavior class on the html, head, title, or style object causes an error when the save or load method is called.

apply to:
A, ABBR, ACRONYM, ADDRESS, AREA, B, BIG, BLOCKQUOTE, BUTTON, CAPTION, CENTER, CITE, CODE, DD, DEL, DFN, DIR, DIV, DL, DT, EM, FONT, FORM, hn, HR, I, IMG, INPUT type=button, INPUT type=checkbox, INPUT type=file, INPUT type=hidden, INPUT type=image, INPUT type=password, INPUT type=radio, INPUT type=reset, INPUT type=submit, INPUT type=text, KBD, LABEL, LI, LISTING, MAP, MARQUEE, MENU, OBJECT, OL, OPTION, P, PLAINTEXT, PRE, Q, S, SAMP, SELECT, SMALL, SPAN, STRIKE, STRONG, SUB, SUP, TABLE, TEXTAREA, TT, U, UL, VAR, XMP

可以使用style方式也可以用js来创建支持userData的对象。
html方式

js创建
o=document.createElement(‘input’);
o.type=’hidden’;
o.addBehavior(‘#default#userData’);
//等同于 o.style.behavior=”url(‘#default#userData’)”;
document.body.appendChild(o);

userData提供了以下属性和方法

属性
expires 设置或者读取过期时间
XMLDocument 读取文件的XML DOM

方法
getAttribute 读取指定属性的值
load 打开文件
removeAttribute 删除指定的属性
save 保存文件
setAttribute 为指定属性赋值

5.目录限制

localStorage不能跨域访问,而userData更加严格,不能跨目录访问。
如:

http://example.com/path1

只能是http://example.com/path1/example.php目录下网页文件才可访问 。
而http://example.com/path1/path2目录下所有文件是访问不到path1保存的数据的。

cookie通过设定domain可以跨子域访问。

既然这样,为什么不用cookie来做本地储存?
1.容量小,约4KB
2.cookie可能被禁用
3.cookie不够安全
4.每次cookie都被发送到服务端,增加带宽。这和我们做本地储存的目的不符。
4.javascript被禁用的情况

6.封装

不想再自己写了,下面是来自sofish.de的代码

        /**
         * @ NAME: Cross-browser TextStorage
         * @ DESC: text storage solution for your pages
         * @ COPY: sofish, http://sofish.de
         */

        typeof window.localStorage == 'undefined' && ~function(){

            var localStorage = window.localStorage = {},
                prefix = 'data-userdata',
                doc = document,
                attrSrc = doc.body,
                html = doc.documentElement,

                // save attributeNames to <html>'s
                // data-userdata attribute
                mark = function(key, isRemove, temp, reg){

                    html.load(prefix);
                    temp = html.getAttribute(prefix);
                    reg = RegExp('\\b' + key + '\\b,?', 'i');

                    hasKey = reg.test(temp) ? 1 : 0;

                    temp = isRemove ? temp.replace(reg, '').replace(',', '') :
                            hasKey ? temp : temp === '' ? key :
                                temp.split(',').concat(key).join(',');

                    html.setAttribute(prefix, temp);
                    html.save(prefix);

                };

            // add IE behavior support
            attrSrc.addBehavior('#default#userData');
            html.addBehavior('#default#userData');

            //
            localStorage.getItem = function(key){
                attrSrc.load(key);
                return attrSrc.getAttribute(key);
            };

            localStorage.setItem = function(key, value){
                attrSrc.setAttribute(key, value);
                attrSrc.save(key);
                mark(key);
            };

            localStorage.removeItem = function(key){
                attrSrc.removeAttribute(key);
                attrSrc.save(key);
                mark(key, 1);
            };

            // clear all attributes on <body> that using for textStorage
            // and clearing them from the 'data-userdata' attribute's value of <html>
            localStorage.clear = function(){

                html.load(prefix);

                var attrs = html.getAttribute(prefix).split(','),
                    len = attrs.length;

                for(var i=0;i<len;i++){
                    attrSrc.removeAttribute(attrs[i]);
                    attrSrc.save(attrs[i]);
                };

                html.setAttribute(prefix,'');
                html.save(prefix);

            };

        }(); 

7.可用的框架

(1)store.js
store.js是轻量的JS框架。
用store.set(‘key’,'value’)和store.get(‘key’,'value’)基本满足了需求。如果是以json形式储存和解析的话,要使用json.js来使IE支持json对象。除了原生的javascript还可以找到jQuery版本的store.js
(2)其他
USTORE.js https://github.com/hugeinc/USTORE.js
Box.js https://github.com/kbjr/Box.js

8.参考

http://news.ycombinator.com/item?id=1468802

http://msdn.microsoft.com/en-us/library/ms531424%28v=VS.85%29.aspx

http://www.cnblogs.com/QLeelulu/archive/2008/03/29/1129322.html

http://sofish.de/1872

至于localStorage就不说了。

春走了夏也去秋意浓

春走了夏也去秋意浓,那天去图书馆还书的时候在走廊上的报纸看到的。

“春走了,夏也去,秋意浓,秋去冬来美景不再”(《秋蝉》)。

天气在渐渐变冷,大一的军训刚刚结束。

每天在睡觉时都会梦到乱七八糟的关于代码和交互的东西,掺杂着各种焦虑情绪和奇怪的幻想。早上睁开眼睛都会觉得困。早期设计的不完善导致了后期的不断修改甚至功能重写。这也算是经验吧。

今天是2011年10月3日。离上次网站更新已经过了4个月。想写很多东西,却在开始落笔的时候又开始迷茫。写了又删,写写停停,基本也只有自己看了。

国庆放假,大活不开,只能在宿舍继续纠结于交互,代码和语义。

吃饭的时候遇到了大一同在青协的同学,最后还是想不起他的名字。

周五还是放了电影。中途下起了小雨,后来越来越大,大家都撑着伞在看,四十多个人。有一大一男生被雨淋湿依旧坐在前面看。

那些新生在一年之后也许不会再有在雨夜搬小凳撑着雨伞去看露天电影了。我们原本是打算雨一下大就收拾装备撤离的。

删了之前不多的东西,换个主题,重新开始吧。