卑鄙是卑鄙者的通行证,高尚是高尚者的墓志铭。 ——北岛《回答》
游客 登录

SAE还是太寨了

这两天研究和试用的感觉,这东西不靠谱,BUG很多,容错和扩展能力都很可疑。

看到这样的问题回复,实在让人无法放心吧:

豆瓣的OAuth API实现不标准

今天写一个同步到豆瓣广播的东西,使用Douban OAuth接口的时候,总是返回400,提示 Signature does not match。仔细的一点点比较,发现豆瓣计算出的basestring和自己算出的不同:

豆瓣给出的basestring:
POST&http%3A%2F%2Fwww.douban.com%2Fservice%2Fauth%2Frequest_token
&OAuth%2520oauth_callback%3Dhttp%253A%252F%252F127.0.0.1%253A8888
%252F%252Fcallback%252Fdouban%26oauth_consumer_key%3D
0b540bcb7c26886d21fc236274739947%26oauth_nonce%3D3530399291666349798%26
oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1300717763%26oauth_version%3D1.0

计算出的basestring:
POST&http%3A%2F%2Fwww.douban.com%2Fservice%2Fauth%2Frequest_token
&oauth_callback%3Dhttp%253A%252F%252F127.0.0.1%253A8888
%252F%252Fcallback%252Fdouban%26oauth_consumer_key%3D
0b540bcb7c26886d21fc236274739947%26oauth_nonce%3D6647526010884738861%26
oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1300718038%26oauth_version%3D1.0

可以发现豆瓣的里面多了一段OAuth%2520,%2520实际上应该就是空格的两次URLEncode结果。

这里怎么会多出"OAuth "这么一段莫名奇妙的东西来呢。后来去豆瓣的API小组看了下,发现已经有很多人提出了这个问题。原来是豆瓣OAuth的实现不标准,标准里可选的字段realm豆瓣把它当成必选字段,OAuth%2520只是未传realm这个字段时豆瓣错误处理出现的一个表象。

豆瓣简略的API文档丝毫没有提及这个问题,并且还堂而皇之的让大家去“通读OAuth规范”。之前我还曾经抱怨过豆瓣9点奇慢无比的更新,以及douban java api客户端过多的第三方jar包依赖,总之这个技术上挺前卫的站点小毛病还是不少的。

开发者抛弃Google App Engine的13个原因

转载自:http://www.bityun.com/archives/341

yunster很遗憾,第一次发表GAE的文章就是负面的。虽然yunster前半年一直在搞GAE,但还是选择了中途放弃。原因多种多样,有 GAE在中国性能的问题,有稳定性的考虑,也有对开发通用性的前景担忧,当然还有更主要的原因是个人的坚持不够(呵呵,这个是主因)。yunster本人 是很羡慕像Livid这样的高手能够在GAE上开发出像V2EX这样的优秀项目,说到底yunster对GAE还是很期待的,毕竟是谷歌的服务,它的长久发展是毋庸置疑的。闲话少说,还是介绍这篇文章。

Carlos Bie是西班牙的一个开发人员。他的公司在几个月的延期后放弃了使用Google App Engine(GAE)。

在本周早些时候,Ble在他的博客中列了十三个原因关于他的公司为什么决定放弃Google App Engine。该博文在一天之内获得了89000个点击量和15个留言。一些是很同情他的遭遇,另一些则斥责他羞辱了Google,当这家公司已经做出很 多努力帮助他和他的团队更好利用这个平台。

一则留言来自Patrick Chanezon,Google的云计算及应用工具经理。出自对Chanezon的尊重和澄清他的观点,Ble更新他的博文但仍然觉得对他的批评中仍然有太多漏洞。

这里有Ble列出的关于他的团队遇到的五个问题:

1、GAE需要Python2.5, 这个版本太老了。使用Ubuntu的话就意味着你需要使用virtualenv或者chroot去保证可以正确使用SDK。 好吧,这不算什么。

2、你不能在你自己的域里用HTTPS, 因此一个安全的链接必须是建立在yourname.appspot.com上的,这太糟了。

3、没有请求可以超过30秒,否则就被强制断掉。天啊,这是最痛苦的事了。当我们上传数据到到数据库中(在no-sql 环境中叫datastore),上传的数据在30秒后被中断了。我们不得不分割文件和所有针对这种情况的挑战。运行后台任务(cron)不得不做得非常谨 慎,因为要遵守同样的规则。对于网站管理员来说,有太多的任务需要超过30秒了。你能想象么?

4、每个服务器发起的到其它站点的GET或POST请求如果5秒内没有完成将被中止。你可以配置到最多十秒。这就使得频繁访问Twitter或者Facebook变得不可能,所以你需要中间服务器。再一次,这加倍了你完成一个简单任务的时间。

5、你不能使用基于C语言的python库,只能是原生库。忘掉那些你用过的强大的第三方库吧。

Ble写道十月份GAE再次挂掉了。他说他们遇到了500错误代码,60%的时间系统宕掉了。用户不能访问或者注册该站点。

Ble承认他应该更小心一些但他曾信任的Google。

“对于我们来说,GAE已经象Wave或者Buzz一样失败了。我们已经在经济上付出了代价。我曾经很固执因为有这样伟大的公司做后盾。然而我学到了重要的一课。好公司一样会犯错。我应该在花掉这么多钱之前事先做一些调研和收集一些证明。我太瞎了。”

很多批评家们并不同情Ble的遭遇,他们说他应该做更多研究,肯定有设计的缺陷。

然而接下来是Eugene Ciurana的留言,一本GAE书籍的作者。

“Carlos, 我在很多年前写过关于GAE的第一本书,出版后我有很多机会去讲述python和Java的平台。我比较早地意识到关于”GAE是如何工作的”变成”如何 当心GAE”,最终建议大家不要在重要的应用上使用GAE。我欣赏你的博文,并发推推荐它,因为这是一个非常重要的警示。我工作中的大部分内容是如何扩展 和提高可用性。我曾很喜欢它是因为它提供了RESTful服务。真实的应用仍然是基于数据中心的或者放在Amazon或Rackspace上的。关于 NoSQL系统,Datastore很难成为一个成功的例子,它对GAE是够用的但你现在知道了它的缺点。记住! 永远用正确的工具做正确的事。我的建议是,你应该区分到底要基于文档还是基于列的数据存储,还要适应检索的需要,你应该看一眼mongoDB和HBase。我们正在大企业客户的生产环境中同时使用它们,没有什么问题。祝你好远,Eugene Ciurana”

Chanezon在他的留言里说Google已经了解到Ble的很多观点,他说公司正在修复这些问题。

Chanezon强调读GAE文档的重要性。他说局限性已经列出来了。这个服务设计的初衷就是了为了高扩展性的应用,即需要快速扩展到大量用户或数据。他提到Gri.pe就是一个典型的例子。

你觉得如何?Ble说的有道理吗?

原文翻译:atutest

文章来源:http://www.readwriteweb.com/could/

Oracle Coherence

Coherence原名Tangosol,在被Oracle收购之后,改称Oracle Coherence,是一个“企业级”的内存分布式集群缓存框架, 具有无需中央控制、自管理、易扩展的、高可用行的特点。

其官方网站称:Coherence 基于可靠的(reliable)、高度可伸缩(highly scalable)的点对点(peer-to-peer )集群协议,提供了可复制的(replicated )、分布式的(distributed )(分区的(partitioned))数据管理和缓存服务。Coherence 不存在单点故障,当某台服务器无法操作或从网络断开时,它可以自动且透明地进行故障切换并重新分布它的集群化数据管理服务。当新服务器加入或故障服务器重 启时,它会自动加入集群,Coherence 会将服务切回到该服务器,透明地重新分布集群负载。Coherence 包含网络级的容错特性和透明的软重启功能,以支持服务器自我修复。

Coherence同时提供了java、.net、c++版本的实现。使用Coherence java版本的话,仅需要简单的使用get和set方法来访问;一台机器上的jvm把数据交给Coherence缓存之后,集群中的其他机器就可以自动共享访问该数据,并且即使这台机器死掉,缓存中的数据也不会丢失。

Timeout in java network IO

在稳定可靠的网络环境下编写程序的时候,很容易就忘记真实世界有多么险恶。响应缓慢的对方应用程序、恶劣拥堵的网络将使网络连接变得变得速度低下甚至死掉。很少有人愿意花费时间来处理这些网络超时问题,但是忽略这个问题,就会造成应用程序无限期的阻塞。如果是单线程或者使用线程池的程序,阻塞的网络连接最终会卡死运行中的程序;如果为每个链接启动新的线程或者进程,最终会产生大量僵死的并发线程/进程,耗尽系统资源。最近已经见过无数存在这种问题的程序,出现问题的时候可以阻塞在某个网络IO上面有几天甚至几个月时间——直到有人发现然后去重启他们。这些程序通常需要访问一些很慢的站点(比如yahoo us),或者从一些高负载的机器上进行hessian调用。

在Java程序中,处理这种问题还是比较容易的。JDK中的传统Socket接口,在JDK1.1之后提供了用于设置超时的参数,一个在Socket构造函数中指定,用于设置连接超时(connect timeout),用于设定socket connect操作的超时时间;一个用于读取超时(sotimeout),用于设定socket read操作的超时时间。如果在指定的时间内这些方法调用没能完成,就会抛出一个SocketTimeoutException.

JDK或者第三方提供http协议访问类库,作为基于Socket的程序,也都提供了设置设置超时的接口。这些接口通常直接调用了Socket提供的接口。但是JDK中的HttpURLConnection,在1.5版本之后才提供了setConnectTimeout和setReadTimeout用于设置链接超时和读取超时,之前的版本只能自己去修改实现了。

在未指定超时时间时,Socket操作是会无限期阻塞的——这真是个悲剧。而在1.4及之前的jdk中,URLConnection没有提供超时设置的接口,又放大了这个悲剧。JDK1.5之后,为了保持向前兼容,默认情况下URLConnection仍然是无超时设置的,这就需要我们每次涉及到这些操作时,都要有意识的设置超时参数。基于URLConnection的一些其他实现(Webservice, Hessian等)也有同样的问题。

另外,java socket虽然有connect和read(recv)的超时设置,却没有write(send)的超时设置。可以轻松的构造一个阻塞在write上面的client:
Server端代码:
        ServerSocket ss = new ServerSocket(8889);
        Socket s = ss.accept();
        Thread.sleep(100 * 1000);
Client端代码:
        Socket s = new Socket("127.0.0.1", 8889);
        s.setSoTimeout(100);
        s.getOutputStream().write(new byte[1000000]);

如果真有必要处理这样的情况,就需要启动一个定时器线程,在超时之后,把socket close掉了。

论Object.wait()要放到while循环里

wait()放while里面算是一个常识性的准则。为什么要这样呢,如果放到if里面会有什么后果?今天水木有人贴出了一段出错的代码,对这个问题现身说法:

public class A {
        private Object[] queue = new Object[1024];
        private int cMsg;

        public synchronized boolean accept(Object msg, Object token)  {
                if (cMsg >= queue.length) {
                        try {
                                wait();
                        }
                        catch (InterruptedException e) {
                                return false;
                        }
                }

                queue[cMsg++] = token;
                queue[cMsg++] = msg;
                return true;
        }

        public synchronized Object[] getMessages()  {
                if (cMsg == 0) {
                        return null;
                }

                Object[] tmp = (Object[]) Arrays.copyOf(queue, cMsg);
                Arrays.fill(queue, 0, cMsg, null);
                cMsg = 0;
                notify();
                return tmp;
        }
}

这个代码在大并发下测试,抛出了java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 1025异常。

要分析原因的话,就是wait()被唤醒后,队列已经满了,cMsg >= queue.length这个条件已经不满足了,再往后移下标的话就数组越界了。

问题是为什么wait()唤醒后队列会满。在代码里,将队列清空后,才执行notify(),这个时候它应该只唤醒了一个线程,那么谁把队列填满的呢

答案是一个阻塞在accept上面的线程。首先要知道一点:其他线程收到信号并不是在notify调用的那一刻!notify的信号是在退出同步函数后才发出的,从退出同步函数,到信号发出,这中间有个时间差,因而就有可能出现以下执行序列:

1. getMessages方法中的 notify()调用
2. getMessages退出,此线程A释放类实例上的monitor
3. 一个阻塞在accept上的线程B,得以进入accept方法,因为此时数组被清空,线程B填入数据,下表+2;accept不断的调用,直到数组被填满,而阻塞在wait()调用上
4. notify信号发出,一个线程C被唤醒。这时没有再判断数组下标位置,直接想数组中塞数据,数组越界。

解决这个问题的方法当然就是把if改为while:

                while (cMsg >= queue.length) {
                        try {
                                wait();
                        }
                        catch (InterruptedException e) {
                                return false;
                        }
                }

在被唤醒后,重新判断条件是否满足。

如何停止一个超时的线程

经常,我们启动了一组线程,让他们去工作,并等待他们完成,获取他们的返回结果。为了保证程序不会卡死在这些线程的执行上,我们为线程设定了一个超时时间,希望线程如果超过这个时间没有完成,就终止执行。

在线程启动的时候,为其建立一个定时器就可以计算超时时间了。那么,问题就是,如何在一个线程已经超时的时候,停止这个线程的执行。

这是个从线程出现以后就在纠结的问题。

Java最初提供了一个Thread.stop方法用于终止线程,但是这个方法随后就因难以解决的线程安全问题被标记为deprecated,不建议继续使用。Thread.stop的原理是让线程抛出ThreadDeath异常(确切的说,它是一个Error),由于程序都不会捕获这个Error,所以这个线程依次退出调用栈,最终退到栈底而终止。在退栈的过程中,会执行所有的finally代码块,并释放线程持有的所有的锁!看起来这是一个设计相当完美的方案,当初那帮设计者应该会被自己的聪明智慧感动得流泪了吧。然而在实际使用的时候,由于被终止的线程释放了所有的锁,被这些锁保护的对象都变得可以被其他线程所访问,从而引发了意想不到的问题——问题可大可小,可能根本察觉不到,也可能造成莫名其妙的错误。

在剥夺了Thread.stop方法后,JDK转而建议使用共享条件变量来控制线程退出,就好似这样:

Class MyThread extents Thread{

    public volatile boolean stop = false;

    public void run(){
        dosomething1();
        if(stop){
            return;
        }

        dosomething2();
        if(stop){
            return;
        }

        dosomething3();

    }

}

需要停止时只要此线程的stop设为true就行了。这需要小心翼翼的编码,如果在最耗时的操作中间没有对退出标识进行判断,那所有其他的工作也就是白费了。

设置条件变量仍然没有办法处理线程被阻塞的情况(如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()等方法时)。一个阻塞中的线程是没法检查条件变量的,它只有等到条件满足,解除阻塞时,才能对条件变量进行判断。这时候可以调用Thread.interrupt方法打破阻塞。Thread.interrupt会使目标线程抛出InterruptedException异常,这个异常通常在代码中被捕获,线程因而跳过阻塞的请求,继续执行。

所以应如下停止一个线程:

MyThread thread = new MyThread();
//wait for some time.
thread.stop = true;
thread.interrupt();

事情还没有完。有些阻塞的线程是不响应Thread.interrupt方法的(例如阻塞在socket.accept() 等旧式IO请求上),Thread.interrupt可以打断的阻塞调用只有Object.wait, Thread.join和Thread.sleep三种。对于这些情况没有通用的处理方法,只能却别对待。例如对于阻塞在socket.accept()的线程,我们可以调用socket.close()来接触阻塞。需要说的是,这种不响应Thread.interrupt的阻塞线程,也不会响应Thread.stop。

综上:多线程是强大的工具,但是也面临很多难以解决的问题,停止正在执行的线程就是其中之一。

难题有多难

2010-06-14的有道难题有多难?

答案是非常难。

内部赛时间延长到5个小时,还是只有46个人曾经提交,只有四个人做对了一题……

晚上很困,特地泡了两杯茶。看来多此一举了,反正出来的都是wrong answer。这个比赛平台很高级,也不会给出错误的用例,只能眼睁睁的看着错误。

但是,现在喝完很精神,可以不睡觉了……

Google App Engine的性能问题

在春节之后,就明显的感觉到google app engine的稳定性和性能都变差了,部分时段还出现down机或者访问缓慢、大量请求失败的情况,cpu cost飙升到以前的好几倍。看到其他一些使用google app engine的人也出现了类似的问题。

Pcworld在最近的一篇报道中写到,google在推出app engine企业版两周后,发现数据存储的性能急剧下降,数据的读写速度降低,并且容易失败。数据存储的性能问题在两个月以前已经出现,在企业版推出后愈发严重了。app engine的服务压力在以每两个月25%的速度增长,这么汹涌的速度即使是google也感受到了痛苦。

Memcachedb与Tokyo Tyrant

Memcachedb与Tokyo Tyrant这两个东西,出发点不同,但结果却有点殊途同归。Memcachedb以bdb作为后端,给memcached增加持久化功能、事务支持及主辅同步等功能;Tokyo Tyrant 则是为 Tokyo Cabinet提供了网络接口和前端缓存。最终的结果,都是出现了一个支持高并发的分布式持久存储系统,适合于高速读写、无严格事务要求的应用场景。Tokyo Tyrant 同时提供了兼容Memcachedb的接口,因而理论上memcached的各种client也可用于Tokyo Tyrant 。

Memcachedb比较一提,因为它是国内的老牌门户sina的开源作品,广泛用于新浪博客等产品线中,像digg.com这样的站点也使用了memcachedb。虽然在此之前,cache+key-value数据库的方案其实已然大行其道,但首先开源的新浪,还是值得表扬表扬的。

Memcachedb引入bdb持久存储的代价还是不小的,虽然数据是先写到bdb的过程是异步的,性能上的cutoff还是相当客观;其并发写入的性能降到memcached的一般一下,不过读数据的性能损失较小。

共41篇,第1/5页 首页 1 2 3 4 5 下一页 尾页