技术,互联网,eLearning …

Twitter的RPC框架:Finagle

今天在google reader上无意中看到了twitter开源出来的RPC框架Finagle项目。简单看了介绍,立刻引起了我的兴趣,一是Finagle提供了RPC服务器端和客户端所需的众多成熟的功能,另外则是Finagle是用我所喜欢的scala语言写的。

Finagle基于Netty,支持众多rpc调用方式,包括request-response, streaming  和 pipelining;同时也提供对于保持状态的支持。

客户端特性

  • Connection Pooling
  • Load Balancing
  • Failure Detection
  • Failover/Retry
  • Distributed Tracing (a la Dapper)
  • Service Discovery (e.g., via Zookeeper)
  • Rich Statistics
  • Native OpenSSL bindings
  • Sharding

服务器端特性

  • Backpressure (to defend against abusive clients)
  • Service Registration (e.g., via Zookeeper)
  • Native OpenSSL bindings

支持的协议

  • HTTP
  • Streaming HTTP (Comet)
  • Thrift
  • Memcached/Kestrel

juc同步框架

本文是个人对Doug Lea的The java.util.concurrent Synchronizer Framework一文的简单理解。

J2SE 1.5引入了java.util.concurrent(juc)包,其中包含了一个小的基于AbstractQueuedSynchronizer的同步框架,大部分juc内的同步器(Synchronizers)都基于该框架实现。

同步器需要包含两种类型的方法:

  • 至少一个acquire方法,用于在同步状态允许线程运行之前阻塞组程;
  • 至少一个release方法,用于更改同步状态使得其它线程从阻塞状态恢复;

java.util.concurrent包含多种类型的同步器,每种同步器都支持以下特性:

  • 非阻塞同步和阻塞同步
  • 同步等待超时
  • 可取消(通过中断机制)

同步器可能仅管理独占(exclusive,同一时刻一个阻塞点只有一个线程可以通过)状态,或者也可能管理共享(share,一个阻塞点可能有多个线程通过)状态。

同步器的基本思想很简单,acquire操作:

while (synchronization state does not allow acquire) {
  enqueue current thread if not already queued;
  possibly block current thread;
}
dequeue current thread if it was queued;

release操作:

update synchronization state;
if (state may permit a blocked thread to acquire)
    unblock one or more queued threads;

支持上述操作需要以下三个组件:

  • 原子化管理的同步状态(synchronization state)
  • 线程阻塞和取消阻塞(Blocking and unblocking threads)
  • 维护队列(Maintaining queues)

同步状态

AbstractQueuedSynchronizer使用一个32位的整形变量state维护同步状态,并且暴露出getStae, setState, compareAndSetState方法来存取和更新状态。其中读和写操作是通过volatile语义,compareAndSetState则是通过硬件的compare-and-swap 或 loadlinked/store-conditional指令来保持操作的原子性。

阻塞

在JSR166出现之前,如果不采用内置的monitor机制,没有合适的用于同步器的阻塞和取消阻塞线程的API。具备此功能的两个方法Thread.suspend和Thread.resume由于其自身存在的不可解决的竞争问题而被标记为Deprecated了。java.util.concurrent.locks包引入了LockSupport类,通过LockSupport.park阻塞线程,LockSupport.unpark取消阻塞线程,这两者都是JVM通过本地机制实现的。

维护队列

维护队列是先进先出的非阻塞队列,两个主要的候选者是MCS locks和CLH locks,juc采用的是CLH locks,CLH通常会做为自旋锁(spinlock),不过也可被同步框架所采用,因为其可以很容易的进行取消和超时操作。
CLH队列如下图所示:

入队是一个原子操作

do { pred = tail;} while(!tail.compareAndSet(pred, node));

每一个节点的释放状态保存在其前驱节点内,”自旋锁“的”自旋“表现为如下式

while (pred.status != RELEASED) ; // spin

经过“自旋”后,出队操作仅仅是将头节点指向刚刚获得锁的节点

head = node;

CLH locks的好处是出队和入队都很快,无锁,并且无阻碍。检测是否有线程在等待也很快。

AbstractQueuedSynchronizer队列原始的CLH locks做了改造:

  • 在节点内增加后继域
  • 每个Node里使用状态域控制阻塞,而非自旋
  • 节点的回收依赖GC

还有一些细节的变化,比如头节点的延迟初始化等,看AbstractQueuedSynchronizer的源码可以比较清楚的看到这些。

不考虑以上细节,acquire操作的通用形式如下:

if (!tryAcquire(arg)) {
  node = create and enqueue new node;
  pred = node's effective predecessor;
  while (pred is not head node || !tryAcquire(arg)) {
    if (pred's signal bit is set)
      park();
    else
      compareAndSet pred's signal bit to true;
      pred = node's effective predecessor;
  }
  head = node;
}

release操作的形式如下:

if (tryRelease(arg) && head node's signal bit is set) {
  compareAndSet head's signal bit to false;
  unpark head's successor, if one exists
}

AbstractQueuedSynchronizer框架的使用

一个Mutex的例子,使用状态0代表锁可用,状态1代表锁被占用:

class Mutex {
  class Sync extends AbstractQueuedSynchronizer {
    public boolean tryAcquire(int ignore) {
    return compareAndSetState(0, 1);
  }
  public boolean tryRelease(int ignore) {
    setState(0);
    return true;
  }
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(0); }
public void unlock() { sync.release(0); }
}

闲扯RSS Reader

最近google reader基本上是上不去了。墙的触角又伸长了。这对我来说很不适应,因为reader是我获取信息的主要手段。

除却被封的因素外,最近上reader的频率也不如以前高了。经常是看看好友的分享文,看看几个特别关注的网站和博客,其它的就全部标记已读了,或者就直接放在那里不管了。在繁忙的工作之余,很难在有时间阅读大量的大段长文了。

想起了09年中的一个话题:RSS 已死?TechCrouch的一篇文章Rest in Peace, RSS认为RSS将被twitter所取代,这引起了大家对于RSS命运的争论。WebLeOn的观点是:RSS不会死去,而是将获得重生。曹增辉则得出结论:阅读器大众化已死

现在看起来,将RSS Reader普及到大众的努力已经基本上失败了。而在一些小众的应用领域,RSS Reader仍然发挥着重要的作用。正向曹増辉所说的,“它依然是最高效的定向阅读工具”。每次登录google reader,都会看到好友们在积极的分享文章,flower我的人的数量也在逐步增加,我依然会在reader里记笔记,收藏和分享文章。

也许,RSS Reader的定位应该是:知识管理工具和知识分享工具。

基于GAE的导出农历生日ics文件的工具

很多中国人的习惯是按照农历过生日的,但是在Google Calendar里添加农历生日只能一年一年的添加,而不能一次添加多年,这样就很不方便。为了避免这个麻烦,在几个月前写了一个程序,用来生成按年重复的农历生日的ICS文件,导入Google Calendar里,避免一年一年的添加带来的麻烦。程序是一个Web应用,运行在GAE(Google App Endine)之上,地址在这里: http://spur.appspot.com

前几天有网友给我发信说他在用这个工具,问我能不能开源,因为appspot随时可能会被封掉,这样就用不了了。如果开源的话别人拿到源代码至少可以自己部署起来使用。我想这也是个不错的主意,本来这只是我个人写着玩的东西,能对人有帮助还是很高兴的。

于是在Google Code上建了个项目,把源代码放了上去,地址: http://code.google.com/p/lunar-cal-ics-gen/ 。

简单说明一下:

  • 程序使用python语言编写,运行于GAE平台, 要想在本机运行,需要下载GAE SDK。当然,也可以修改代码改造成不依于GAE的Web应用。
  • Web框架使用了从friendfeed开源出来的tornado,具体介绍可见这里
  • 生成日历使用的是icalendar 库,计算农历的代码取自pyzh 项目。

目前添加日历的方式是以ics文件做为中介,最初曾设想通过GData API直接将日历添加到Google Calendar,不过由于时间关系,一直没来得及做这件事,也不确定这个想法是否可行。

程序截图

截图

eLearning工具之RSS阅读器

网上分布着大量的不同种类型的知识,每天都有无数的知识被生产出来。作为知识消费者的学习者,往往面对着海量信息无所释从。RSS阅读器可以帮助他们解决这个问题。学习者可以通过订阅感兴趣的信息源,实现对于知识的集中阅读和管理。前提是,这些信息源提供了可供订阅的格式。

也就是说,RSS阅读器提供了由知识生产者到知识消费者之间的快捷通道。但是在RSS阅读器里,知识仍然是原有的知识,并不能通过知识消费者之间的互动而得到升华,因而RSS阅读器作为eLearning工具的作用也就甚为有限了。为什么这么说呢,先简单的扯一点理论。

社会建构主义学习观认为个人建构的、独有的主观意义和理论只有与社会、与物理世界“相适应”时,才有可能得到发展。因此,社会建构主义学习观强调意义的社会建构和学习的社会情境,强调社会互动、协作与活动等。从社会建构主义角度来看,知识不仅是个体在与物理环境的相互作用中建构起来的,而且社会性的相互作用对于知识的建构也同样重要,甚至更为重要。

基于上面的理论,只有当阅读器支持社会性互动,支持社会性知识建构,才能成为更有效的社会化学习工具。社会化也是目前大多数在线RSS阅读器的发展方向。
Google Reader 在社会化方面做了很多尝试,包括许多社会化方面的功能:

  • 分享:包括对文章的分享,目录的分享,标签的分享以及把文章邮寄给好友等功能
  • 评论:可对分享的文章进行评论
  • Like: 与分享不同的地方在于Like对于所有人是可见的,从而为学习者提供了一种找到兴趣可能相同的人的方法
  • Follow: 最初是订阅gtalk好友的分享,改成follow后范围大大扩大,而且是单向关系。

总的来说,这几个方面的功能都可归结为“分享”。其实,“分享”的过程可以看做是知识整理的过程,相当于无数的用户不断地在以人工的方式从Google Reader的巨大的内容库中挑选出有价值的知识。重要的是,这些知识是存在Google Reader里的,这本身就是一个财富。用户精挑细选互联网上的内容,加以评论和注释,帮助google构建了一个巨大的知识库。这个知识库又会吸引更多的人来使用Google Reader,他们之中又会有很多人参与“分享”活动,从而形成一个知识不断增长的“生态系统”。

从上面的分析来看,阅读器本身可以作为一个社会化的学习工具,且在社会化方面其实可以走得更远一些,比如围绕同种类型的知识形成学习圈子(因其小众,更容易形成同质的人群),更好地促进学习。

返回顶部