gmail

哪里有绝对的可靠?Gmail这次也挂了,而且一挂就是几个小时。
我承认我有点过度依赖Google的服务了,最近还大爱Google Docs,热衷于把很多频繁更新的表格整理到Google Docs上去。
这次Gmail的Outage让我发现我居然有这么依赖Gmail,这几个小时积压的电子邮件居然到了让我回归钢笔A4纸时代…

Update:

Google在太平洋时间3点49分宣布正式解决了此问题,并公布了问题发生的原因:Google对一个欧洲的数据中心进行例行维修活动,而出口被转移到了另一个数据中心。结果一些新的代码造成了意想不到的问题,这些代码根据IP地址来判断用户地理位置,从而把他们引向较近的数据中心。结果,一个欧洲数据中心超载停机,并连锁反应引发了另一个数据中心出现问题。大约1个小时后,一切才得到控制。
Google表示这种事故非常罕见,而且他们将严加防范,以防止同类事故再次发生。

这样的解释可以理解,可是正常运行中的Gmail服务数据中心有这么高度平衡吗?

接着,赖昌星带我到他的书房,这里的书桌上摆着一台大屏幕的苹果电脑。“这里就是我一天中呆得最长时间的地方。”赖昌星说,他平时没有什么事件好干,除了看电视之外,就是上网“斗地主”。他说,如果有朋友来的时间,他喜欢与他们一起玩扑克游戏“斗地主”。不过,朋友不会天天来,所以,赖昌星迷上了网上的“斗地主”游戏。

“我都是上QQ玩‘斗地主’游戏,对手是在中国大陆的网友。”赖昌星说他经常一玩就是几个小时,最多每日玩十几个小时,“所以弄到睡眠不足。”记者问到赖昌星的QQ名字时,他说本来想实名注册,可是QQ的系统不允许他用“赖昌星”的名字注册,只好随便取一个名字了。

“除了上网‘斗地主’之外,我还会上《大都会》等网站看新闻。”赖昌星说,他强调自己来加10年,生活平淡,“在这里,我从来没有上过歌舞厅、卡拉OK和夜总会。”赖昌星说,喜欢自己在家里做些家乡菜。

今天看到这样一则新闻。关于赖昌星在加拿大的生活近况。注意粗体一段话:记者问到赖昌星的QQ名字时,他说本来想实名注册,可是QQ的系统不允许他用“赖昌星”的名字注册,只好随便取一个名字了。
处于垄断地位的软件企业喜欢搞一些所谓规则,比如这里腾讯QQ的关键字过滤机制。凭啥逃犯的名字就是非法用户名?

什么是 Google 数据 API?

Google 数据 API 提供用于在网络上读写数据的简单标准协议。
这些 API 可以使用基于 XML 的两种标准联合格式:Atom 或 RSS。它们还具有一个供稿发布系统,其中包括 Atom 发布协议和用于处理查询的一些扩展功能(使用 Atom 的标准扩展模型)。
许多 Google 服务都支持 Google 数据 API 协议。以下数据 API 可以使您的客户端应用程序与 Google 服务进行互动:

动机

这几天我在做自己的一个网站制作及托管服务的网站,牵涉到一些客户信息的管理什么的,最好是能做成OA之类的形式,就像Google Docs里的SpreadSheet一样,在线编辑,在线显示。不过我实在没时间去自己做这种东西,于是我在想是不是能直接通过API把Google Docs里的SpreadSheet直接嵌入我自己写的网页里去。不见得需要编辑功能,估计Google也不会提供,不然Google Docs不就变成带有后门的网站留言文本编辑器了么…(所谓带有后门,是指与一般留言文本编辑器不同,不是把数据存储在本地网站,而是存储在另一个服务器,在这里就是Google Docs服务器上)

php_google_docs1

站在巨人的肩上,感谢Lim Jiunn Haur写的WordPress插件Inline Google Docs早就提供了这个功能,我把这个插件拆开,去掉多余的,为我所用了。

php_google_docs2

使用方法

用法很简单,后面的相关下载中提供了一个RAR包,当中包含两个文件夹,styles里包含了一个CSS,用于修饰SpreadSheet表格显示的效果,这里的风格基本上是白灰风格,如上图所示。鉴于保密需求我做了模糊处理,反正就那么个意思。你需要在你的网站中加入这个CSS或者将这个集成到你的CSS中,或者干脆不要,自己写。
includes文件夹是库,其中Zend是Google Data API库,由Google发布。gdocs是本文所述的库。加载Google Data API库的代码在gdocs下gdocs.php中,你可能需要做一点路径修改之类。至少在你的全局include库的地方加入Zend的位置,并include gdocs.php这个文件。

[code=’php’]
/* Add Zend library to path */
$path = dirname (__FILE__) . “/includes”;
set_include_path (get_include_path () . PATH_SEPARATOR . $path);
[/code]

另有一个全局配置文件config.php,我并没有给出,你自己要补上。示例如下:

[code=’php’]
/* Site Configuration */
define(‘SITE_URL’, ‘http://www.yourdomain.com/’);

/* Google Docs Settings */
define(‘GOOGLE_DOMAIN’, ‘yourdomain.com’);
define(‘GOOGLE_USERNAME’, ‘username@yourdomain.com’);
define(‘GOOGLE_PASSWORD’, ‘YourGooglePassword’);
define(‘PROXY_HOST’, ”);
define(‘PROXY_PORT’, ”);
define(‘PROXY_USERNAME’, ”);
define(‘PROXY_PASSWORD’, ”);
[/code]

调用显示Google Docs中包含的文档时,PHP代码如下:
[code=’php’]
gdocs_list();
[/code]
具体的显示风格请自行修改includes的gdocs下,gdisplay.php文件。

调用显示某一个具体的SpreadSheet的时候,$atts[‘type’]置’spreadsheet’,st_id为SpreadSheet的ID,例如’pNrLCrMF9srEYeIRCLO5kEF’,wt_id为工作表ID,例如’od6’,PHP代码如下:
[code=’php’]
$atts[‘type’] = $_GET[‘type’];
$atts[‘st_id’] = $_GET[‘st_id’];
$atts[‘wt_id’] = $_GET[‘wt_id’];
$atts[‘style’] = ‘googledocs’;
$atts[‘headings’] = NULL;

gdocs_display($atts);
[/code]

注意事项

  • 按照原作者Lim Jiunn Haur代码中的声明,本作品源代码依旧遵循GNU Public License
  • 修改之后的代码示例只包含了必要的我能用到的功能,就是对Google Docs中Document和SpreadSheet的列表,以及SpreadSheet的显示功能,其他功能并未写出实例,但是包含的Google Data API是完整的,应该可以写出进一步功能的代码,这里留给读者去自行研究吧。

相关下载

参考资料

引言

[singlepic=18468]

上图:艺术陈列馆问题(Art Gellery Problem),是一个NP-Complete问题。这里显示了一种解,四个摄像头覆盖了整个艺术陈列馆的每个角落。

P和NP就不讲了,Assert(不明白P和NP的读者不会到达这里看到这一段话)。
P是否等于NP的问题目前仍有争论,我对算法理解不深,暂时没有自己的看法。具体的讨论可以参见:WikiPedia的P=NP?讨论。不过做点猜想的话,我觉得,从数学美的角度来看,我觉得P=NP。当然,从理性思考角度来看,就像“你觉得有没有外星人”这个问题一样,理性思考的人会毫不犹豫地给出“当然有”的答案,P不应该等于NP。当然大部分计算机科学家认为P≠NP。

If P=NP, then the world would be a profoundly different place than we usually assume it to be. There would be no special value in “creative leaps,” no fundamental gap between solving a problem and recognizing the solution once it’s found. Everyone who could appreciate a symphony would be Mozart; everyone who could follow a step-by-step argument would be Gauss… —— Scott Aaronson, MIT

这段话给出的启示还是很令人震惊的:任何懂得欣赏交响乐的人都可以成为莫扎特一样的音乐神童,任何懂得步步演绎问题的人都可以成为高斯一样的数学天才,因为P=NP,则说明解决问题和理解、认识和描述问题本身没有本质区别。
谈点自己的看法:Google的出现其实为我们提出了一种佐证,当你把一个问题描述清楚的时候,基本也就解决了。当你真正遇到一个问题,只要想清楚应该用什么关键字去搜索,问题的答案正在Google上等着你。

The main argument in favour of P≠NP is the total lack of fundamental progress in the area of exhaustive search. This is, in my opinion, a very weak argument. The space of algorithms is very large and we are only at the beginning of its exploration. […] The resolution of Fermat’s Last Theorem also shows that very simply [sic] questions may be settled only by very deep theories. —— Moshe Y. Vardi, Rice University

最简单的问题却要通过最复杂的理论来解决。

Being attached to a speculation is not a good guide to research planning. One should always try both directions of every problem. Prejudice has caused famous mathematicians to fail to solve famous problems whose solution was opposite to their expectations, even though they had developed all the methods required. —— Anil Nerode, Cornell University

避免偏见,应当总是向两个方向努力。

NP-Complete问题

Cook在1971年给出并证明了有一类问题具有以下性质:

  1. 这类问题中任何一个问题至今未找到多项式时间算法;
  2. 如果这类问题中的一个问题存在有多项式时间算法,那么这类问题都有多项式时间算法(就是多项式时间内,这类问题可互相规约)。

这类问题中的每个问题称为NP完全(NP-Complete,NPC)。

NP-Hard问题

如果判定问题A满足A∈NP且NP中的任何一个问题都可在多项式时间内规约为A,则称A为NP完全(NP-Complete,NPC)。若NP中的任何一个问题都可以在多项式时间规约为判定问题A,则称A为NP难(NP-Hard,NPH)。
显然NPC⊆NPH。
NP完全和NP难问题的区别是NP难问题无需判断A是否属于NP。验证一个问题A是否为NPC的关键有两点:

  1. 一是NP中任何一个问题是否可在多项式时间内规约为A;
  2. 其次,是否存在一个字符串,其规模为实例规模的多项式函数,以及是否存在一个多项式时间的验证算法。

由于NPC里包含很多著名的组合最优化问题,经过几代数学家的努力,迄今没有找到多项式时间算法,人们猜想NPC中的任何一个问题没有多项式时间算法,即P∩NPC=∅。
这里有个图可以帮助你理解这几种问题的相互关系。可以看到,如果P=NP,数学上是比较美的。

[singlepic=18467]

一种实践方法

当然上面讲的都是理论。实践当中没有这么复杂,当你遇到一个问题,想判断这个问题是否是NPC时,只需要找一个类似的已知的NPC问题,然后想一个这两个问题之间的多项式时间转换方法即可。
这里有一个列表,包含已知的著名NP-Complete问题的列表。重要的NPC问题现在已知3000+。

概述. 在美国第44任总统就职日这个历史性的日子,让我们来分析一下新的白宫网站(whitehouse.gov)代码结构吧。网站基于ASP.NET构建。

[singlepic=18466]

whitehouse.gov网站使用IIS 6.0. 网站的HTTP头信息中包含键值对”Server: Microsoft-IIS/6.0″。网站并没有使用微软公司最新版本服务器软件,IIS/7。

whitehouse.gov网站使用ASP.NET 2.0. HTTP头中标识自己的程序版本为”X-Aspnet-Version: 2.0.50727″。这个信息可以去掉,这样可以为每次服务器响应节省30字节带宽。

whitehouse.gov网站使用JQuery 1.2.6. JQuery JavaScript库,使用了其最小集版本,位于/includes/文件夹下。很多开发者使用Google服务器托管的JQuery以便提升性能和减小下载脚本的延迟时间。这样做可以提升站点性能。

<script type="text/javascript" src="/includes/eop/jquery-1.2.6.min.js"></script>

whitehouse.gov网站使用GZIP压缩. 所有网站文本都采用了GZIP压缩,显著地提升了性能。

未压缩大小:  48218 bytes
压缩后大小:   8370 bytes
节省带宽:           ~80%

whitehouse.gov网站使用Vary: Accept-Encoding. 在HTTP头中添加Vary: Accept-Encoding是一种强制代理不向不能解码GZIP的客户端发送GZIP内容的手段。

whitehouse.gov网站使用Cache-Control: privatemax-age. 它使用了”max-age=85895“,大约1 天。这样,html页面应该只会在你的计算机中缓存1天。

whitehouse.gov网站使用Web Trends Live追踪技术. WebTrends声称是“领先的网页分析和客户为中心智能市场营销解决方案(leading provider of web analytics and consumer-centric marketing intelligence solutions)。”

whitehouse.gov网站使用meta keywords标记. 这样做很奇怪,因为Internet上的访问者很少有不知道这里是干什么的。meta标记如下:

<meta name="keywords" content="President, Barack Obama, White House,
United States of America, 44th President, White House history, President Obama,
Barck, Barek, Barak, Barrack, Barrak, Obma, Barack" />

这样并不会对提升whitehouse.gov网站的Google PageRank有多少贡献。我认为搜索Barack Obama的用户无论如何都会被导引到这个网站的。

whitehouse.gov网站使用ViewState. 这是一个隐藏表单,允许网站在浏览器中存储服务器端数据。ASP.NET自动解析发送到浏览器的ViewState信息。浏览器不应该解析这个信息。

whitehouse.gov网站使用WebResource.axd. 这是ASP.NET生成的一个脚本文件。通常它们不能被有效地缓存,并且我发现它们的确降低了性能。

whitehouse.gov网站包含了很多空白字符. 如果你的浏览器启用了GZIP,这并不会带来性能影响,但是如果去掉这些空白字符,网站代码可以减小20%以上。

whitehouse.gov网站包含注释标签. 网站使用了很多HTML注释分割页面代码区域。如果能将这些注释写在服务器端代码中,可以在编译页面时不将注释编译在最终页面代码中,从而提升效率。

<!-- Start -->
<!-- End -->

whitehouse.gov网站包含很长很长的ASP.NET ID. 页面中的很多元素包含非常长的ID,是浪费带宽的主要因素之一。这些长ID可以很容易地在服务器端替换成短ID。

<a id="ctl09_rptNavigation_ctl00_rptNavigationItems_ctl01_hlSubNav"...

whitehouse.gov网站使用的GIF多于PNG. 网站使用的GIF图片多于PNG图片。PNG格式的图片可以更加优化,从而节省带宽和消耗。网站中使用了两个GIF动画。

whitehouse.gov网站使用了5个层叠式样式表(stylesheets)文件和12个JavaScripts脚本文件. 如果能把这两类文件合并成两个文件,网站可以更快而且更轻量级。当然这是针对访问首页的访客而言。奇怪的是,用于修饰管理页面的层叠式样式表也被加载进了普通用户的访问中:

/* admin styles */
/* cms */
.adminNavigation {width:996px; position:relative; z-index:100;}

whitehouse.gov网站使用了高度压缩的JPG. 如果你距离屏幕较远,或者视力不是那么好,这些图片看起来还行。开发者对JPG图片采用了高度压缩。这里显示的图片被放大并且轻微地二次压缩过。

[singlepic=18465]

whitehouse.gov网站使用了image sprites技术. 这项技术可以大幅提升站点性能,因为它将若干个小图片合并成了一张图片。这是一项先进的技术。采用了image sprites技术的图片是”nav-sprite.png“.

whitehouse.gov网站使用了Packer. Dean Edward的Packer是一个用于压缩JavaScript文件的工具。JavaScript脚本将在被下载时自动解压缩,这是一项很差的优化手段, 因为往往JavaScript经过GZIP压缩之后,比经过YUI压缩器(YUI Compressor)压缩之后更小。[参见 jquery-plugins.js]

eval(function(p,a,c,k,e,d)...

使用Packer的决定不像是一个深入了解GZIP或者文本压缩技术的人做出的。压缩之前的文件确实变小了,然而这导致了最终需要被下载的文件变大了。[http://dean.edwards.name/packer/]

whitehouse.gov大小821 KB, 在我的线路上(cable modem)使用了1.58秒完成加载。这个数字大约是新的、基于图片的网站的平均值。

加载时间:  1.58 seconds
总计大小:  821 KB

whitehouse.gov网站包含了几个隐藏链接. 在源文件里,大部分是在JavaScript中,大约嵌入了6个链接。这样,这些幸运的人就得到了来自白宫网站的外链(译者注:PageRank 9啊!9!)。

www.youngpup.net
http://sorgalla.com/jcarousel/
http://billwscott.com/carousel/
http://www.codylindley.com

whitehouse.gov网站使用了一个不透明的favicon.ico. 为了站点在加入书签后具有更好的视觉效果(译者注:对很多非IE浏览器而言,无论是否加入书签,favicon文件都会显示在标签栏),一个具有透明背景的favicon是更好的选择。这项改变对于一个知道怎么修改的人来说可以在10分钟内完成。

结论是,whitehouse.gov网站还是很吸引人的。虽然它并不算是一个非常有效率的站点,并且过多地注意了视觉效果。一个网站优化专家可以在几天之内将它的加载速度提升到现在的两倍。

最后,记住本届政府和奥巴马总统并不是写这些代码的人。

翻译自:http://dotnetperls.com/Content/whitehouse-gov-Site.aspx

“/”应用程序中的服务器错误。
验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保<machineKey>配置指定了相同的validationKey和验证算法。不能在群集中使用AutoGenerate。

概括地说,就是POST执行时引起的“验证视图状态MAC失败”错误。
原因很简单:当你想使用传统POST提交信息到另一个aspx页面时出现这个错误。如果提交的action是本页面则不会出错。
解决方案:也很简单,去掉form中的runat=”server”即可。

这几天有点小活,要求是ASP.NET的,虽然我不怎么看好ASP.NET,但是人家要求了我也就照做吧。
最复杂的部分是一个TreeView。出于兼容性和简单性考虑没有用ASP.NET的TreeView控件,而是参考一篇文章自行写了一个出来。

[singlepic=18460]

技术指标如下:

  • 实现:Div+CSS+JavaScript+ASP.NET(C#)
  • 功能:可以实现无限级的TreeNode
  • 标准:在IE 7和Firefox 3.1下显示效果相同,兼容XHTML 1.0 Strict最严格的网页标准

效果如左图所示。最后一个函数可能会因为超宽看不到全部,具体请参照文章附件中的源代码。
附件下载:http://download.nocoo.us/Download/Archive/TreeViewCode.rar

TreeNode.cs

[code=’c#’]
///

/// TreeView结点类
///

public class TreeNode
{
private string name;
private string href;
private List subNodes = new List();

///

/// 结点名
///

public string Name { get { return name; } }
///

/// 结点链接
///

public string Href { get { return href; } }
///

/// 下级结点个数
///

public int Count { get { return this.subNodes.Count; } }
///

/// 获取或者设置下级结点
///

/// 序号 /// 下级结点
public TreeNode this[int index]
{
get { return subNodes[index]; }
set { subNodes[index] = value; }
}

///

/// 构造函数
///

/// 结点名 public TreeNode(string name)
{
this.name = name;
this.href = null;
}

///

/// 构造函数
///

/// 结点名 /// /// 结点链接 public TreeNode(string name, string href)
{
this.name = name;
this.href = href;
}

///

/// 添加下级结点
///

/// 新结点 public void Add(TreeNode node)
{
subNodes.Add(node);
}
}
[/code]

TreeView.cs

[code=’c#’]
///

/// TreeView
///

public class TreeView
{
private List nodes = new List();

///

/// 填充测试用数据
///

public void FillTestData()
{
TreeNode node1 = new TreeNode(“中国”, “#”);
TreeNode node11 = new TreeNode(“华北地区”, “#”);
TreeNode node111 = new TreeNode(“河南省”, “#”);
TreeNode node112 = new TreeNode(“河北省”, “#”);
TreeNode node113 = new TreeNode(“山东省”, “#”);
TreeNode node1131 = new TreeNode(“青岛市”, “#”);
TreeNode node1132 = new TreeNode(“济南市”, “#”);
TreeNode node11321 = new TreeNode(“市中区”, “#”);
TreeNode node11322 = new TreeNode(“历下区”, “#”);
TreeNode node11323 = new TreeNode(“槐荫区”, “#”);
TreeNode node11324 = new TreeNode(“天桥区”, “#”);
TreeNode node11325 = new TreeNode(“长清区”, “#”);
TreeNode node1133 = new TreeNode(“菏泽市”, “#”);
TreeNode node1134 = new TreeNode(“济宁市”, “#”);
TreeNode node1135 = new TreeNode(“德州市”, “#”);
TreeNode node12 = new TreeNode(“东北地区”, “#”);
TreeNode node13 = new TreeNode(“西北地区”, “#”);
TreeNode node14 = new TreeNode(“华东地区”, “#”);
TreeNode node15 = new TreeNode(“西南地区”, “#”);
TreeNode node16 = new TreeNode(“华南地区”, “#”);
TreeNode node17 = new TreeNode(“华中地区”, “#”);
TreeNode node18 = new TreeNode(“港澳台地区”, “#”);

node1132.Add(node11321);
node1132.Add(node11322);
node1132.Add(node11323);
node1132.Add(node11324);
node1132.Add(node11325);

node113.Add(node1131);
node113.Add(node1132);
node113.Add(node1133);
node113.Add(node1134);
node113.Add(node1135);

node11.Add(node111);
node11.Add(node112);
node11.Add(node113);

node1.Add(node11);
node1.Add(node12);
node1.Add(node13);
node1.Add(node14);
node1.Add(node15);
node1.Add(node16);
node1.Add(node17);
node1.Add(node18);

nodes.Add(node1);
nodes.Add(new TreeNode(“俄罗斯”, “”));
nodes.Add(new TreeNode(“美国”));
nodes.Add(new TreeNode(“韩国”));
nodes.Add(new TreeNode(“澳大利亚”));
nodes.Add(new TreeNode(“印度”, “”));
nodes.Add(new TreeNode(“加拿大”, “”));
}

///

/// 获取TreeView的html代码
///

///
public string GetHtmlString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(“

    “);
    for (int i = 0; i < nodes.Count; i++) { sb.AppendLine(GetNodeHtml(nodes[i], 1, (i + 1).ToString())); } sb.AppendLine("

“);
return sb.ToString();
}

private string GetNodeHtml(TreeNode thisNode, int depth, string id)
{
StringBuilder sb = new StringBuilder();
bool hasSub = (thisNode.Count > 0) ? true : false;
sb.AppendLine(string.Format(“

  • “, depth, id));
    sb.AppendLine(string.Format(

    {3}

    “,
    hasSub ? “closedir” : “nodir”,
    depth,
    id,
    (thisNode.Href == null || thisNode.Href.Equals(“”)) ? thisNode.Name : string.Format(“{1}“, thisNode.Href, thisNode.Name)
    ));
    if (hasSub)
    {
    sb.AppendLine(string.Format(“

    “, depth, id));
    sb.AppendLine(“

      “);
      for (int i = 0; i < thisNode.Count; i++) { sb.AppendLine(GetNodeHtml(thisNode[i], depth + 1, string.Format("{0}_{1}", id, i + 1))); } sb.AppendLine("

    “);
    sb.AppendLine(“

    “);
    }
    sb.AppendLine(“

  • “);
    return sb.ToString();
    }
    }
    [/code]

    ASP.NET页面调用:

    [code=’c#’]
    <% CMC.TreeView list = new CMC.TreeView(); list.FillTestData(); Response.Write(list.GetHtmlString()); %>
    [/code]

    ASP.NET页面JavaScript:

    [code=’js’]
    defaultNodeState();

    function defaultNodeState()
    {
    var nodeState = getCookie(“nodeState”);
    if(nodeState == null)
    {
    nodeState = “,|,|,”;
    setCookie(“nodeState”,nodeState);
    }
    var layer = nodeState.split(‘|’);
    for(var i=0;i