loop in codes

Kevin Lynx BLOG

使用ActionScript开发Ice Web客户端

我们目前的项目服务器端使用了Ice来构建。Ice有一套自己的网络协议,客户端和服务器端可以基于此协议来交互。由于Ice使用Slice这种中间语言来描述服务器和客户端的交互接口,所以它可以做到极大限度地屏蔽网络协议这个细节。也就是说,我们只要借助Ice和Slice,我们可以轻松地编写网络程序。

然后,我们的后端现在需要一个运行在Web浏览器上的客户端。要与Ice做交互,如果使用TCP协议的话,得保证是长连接的。但HTTP是短连接的。而另一方面,我们还需要选择一个Ice支持的和Web相关的语言来做这件事情。如果要在浏览器端直接与Ice服务建立连接,可供选择的语言/平台包括:

  • Flash
  • Silverlight

因为我之前用erlang简单写了个Ice的客户端库,所以我对Ice底层协议有一定了解,可以不一定使用Ice支持的语言,所以HTML5其实也是个选择。此外,如果在浏览器端使用Applet,Java可能也是个选择。

其实几个月前在这块的技术选择问题上我就做过简单的研究,当时确定的方案是使用Flash。但是,后来人员招聘上遇到了问题,看起来要招一个会ActionScript和前端页面技术的程序员来做我们这种项目,似乎大材小用,成本显高了。

那么,考虑到团队里有现成的Java程序员,而且看起来招一个会用Java写网站的程序员简单又便宜,似乎是排除技术原因的最好选择。但是,如果不在浏览器端直接连接服务器来做交互,而是让Web服务器端来做中转的话,会面临不少问题:

  • 浏览器端操作结果的获取问题,说白了就是非实时了,得用Ajax等等技术去模拟实时,代价就是不断轮训,也就是通常说的poll
  • Web服务器端需要编写大量代码:对用户操作的映射,结果缓存等等

如果能用Flash包装与服务器交互的部分,而把UI相关的东西留给HTML/JS/CSS去做,那是不是可行一点?如果只是用ActionScript编写与服务器端的交互逻辑代码,我就不需要花时间去系统学习ActionScript,甚至如何用Flash做界面,我甚至不用搞懂这些技术之间的关系。基本上看些Ice for ActionScript的例子代码,就可以完成这件事情。

以下记录一些主要的过程/方法:

Erlang编程技巧若干

guard

guard可以以逗号或者分号分隔,以逗号分隔表示最终的结果为各个guard的and结果,以分号则是只要任意一个guard为true则最终结果为true。

guard(X, Y) when not(X>Y), is_atom(X) ->
    X + Y.

guard在list comprehension中可以筛选元素:

NewNodes  = [Node || Node <- AllNodes, not gb_sets:is_member(Node, NewQueried)],

guard中不能使用自定义函数,因为guard应该保证没有副作用,但自定义函数无法保证这一点,所以erlang禁止在guard中使用自定义函数。

P2P中DHT网络爬虫

DHT网络爬虫基于DHT网络构建了一个P2P资源搜索引擎。这个搜索引擎不但可以用于构建DHT网络中活跃的资源索引(活跃的资源意味着该网络中肯定有人至少持有该资源的部分数据),还可以分析出该网络中的热门分享资源。小虾不久前发布了一个这样的搜索引擎:磁力搜索。他也写博客对此稍作了介绍:写了个磁力搜索的网页 - 收录最近热门分享的资源。网络上其实也有其他人做了类似的应用:DHT monitoringCrawling Bittorrent DHT

但是他的这篇文章仅介绍了DHT网络的大致工作原理,并且这个爬虫的具体工作原理也没有提到。对此我查阅了些文章及代码,虽然从原理上梳理出了整个实现方案,但很多细节还是不甚清楚。所以本文仅作概要介绍。

DHT/Magnet/Torrent

在P2P网络中,要通过种子文件下载一个资源,需要知道整个P2P网络中有哪些计算机正在下载/上传该资源。这里将这些提供某个资源下载的计算机定义为peer。传统的P2P网络中,存在一些tracker服务器,这些服务器的作用主要用于跟踪某个资源有哪些关联的peer。下载这个资源当然得首先取得这些peer。

DHT的出现用于解决当tracker服务器不可用时,P2P客户端依然可以取得某个资源的peer。DHT解决这个问题,是因为它将原来tracker上的资源peer信息分散到了整个网络中。这里将实现了DHT协议的计算机定义为节点(node)。通常一个P2P客户端程序既是peer也是节点。DHT网络有多种实现算法,例如Kademlia。

当某个P2P客户端通过种子文件下载资源时,如果没有tracker服务器,它就会向DHT网络查询这个资源对应的peer列表。资源的标识在DHT网络中称为infohash,是一个20字节长的字符串,一般通过sha1算法获得,也就是一个类似UUID的东西。

实际上,种子文件本身就对应着一个infohash,这个infohash是通过种子文件的文件描述信息动态计算得到。一个种子文件包含了对应资源的描述信息,例如文件名、文件大小等。Magnet,这里指的是磁力链接,它是一个类似URL的字符串地址。P2P软件通过磁力链接,会下载到一个种子文件,然后根据该种子文件继续真实资源的下载。

磁力链接中包含的最重要的信息就是infohash。这个infohash一般为40字节或32字节,它其实只是资源infohash(20字节)的一种编码形式。

Erlang使用感受

用erlang也算写了些代码了,主要包括使用RabbitMQ的练习,以及最近写的kl_tservericerl。其中icerl是一个实现了Ice的erlang库。

erlang的书较少,我主要读过<Programming Erlang>和<Erlang/OTP in Action>。其实erlang本身就语言来说的话比较简单,同ruby一样,类似这种本身目标是应用于实际软件项目的语言都比较简单,对应的语法书很快可以翻完。

这里我仅谈谈自己在编写erlang代码过程中的一些感受。

语法

erlang语法很简单,接触过函数式语言的程序员上手会很快。它没有类似common lisp里宏这种较复杂的语言特性。其语法元素很紧凑,不存在一些用处不大的特性。在这之前,我学习过ruby和common lisp。ruby代码写的比common lisp多。但是在学习erlang的过程中我的脑海里却不断出现common lisp里的语法特性。这大概是因为common lisp的语法相对ruby来说,更接近erlang。

编程模式

erlang不是一个面向对象的语言,它也不同common lisp提供多种编程模式。它的代码就是靠一个个函数组织出来的。面向对象语言在语法上有一点让我很爽的是,其函数调用更自然。erlang的接口调用就像C语言里接口的调用一样:

func(Obj, args)
Obj->func(args)

即需要在函数第一个参数传递操作对象。但是面向对象语言也会带来一些语法的复杂性。如果一门语言可以用很少的语法元素表达很多信息,那么我觉得这门语言就是门优秀的语言。

表达式/语句

erlang里没有语句,全部是表达式,意思是所有语法元素都是有返回值的。这实在太好了,全世界都有返回值可以让代码写起来简单多了:

    Flag = case func() of 1 -> true; 0 -> false end, 

erlang和RabbitMQ学习总结

AMQP和RabbitMQ概述

AMQP(Advanced Message Queue Protocol)定义了一种消息系统规范。这个规范描述了在一个分布式的系统中各个子系统如何通过消息交互。而RabbitMQ则是AMQP的一种基于erlang的实现。

AMQP将分布式系统中各个子系统隔离开来,子系统之间不再有依赖。子系统仅依赖于消息。子系统不关心消息的发送者,也不关心消息的接受者。

AMQP中有一些概念,用于定义与应用层的交互。这些概念包括:message、queue、exchange、channel, connection, broker、vhost。

注:到目前为止我并没有打算使用AMQP,所以没有做更深入的学习,仅为了找个机会写写erlang代码,以下信息仅供参考。

  • message,即消息,简单来说就是应用层需要发送的数据
  • queue,即队列,用于存储消息
  • exchange,有翻译为“路由”,它用于投递消息,应用程序在发送消息时并不是指定消息被发送到哪个队列,而是将消息投递给路由,由路由投递到队列
  • channel,几乎所有操作都在channel中进行,有点类似一个沟通通道
  • connection,应用程序与broker的网络连接
  • broker,可简单理解为实现AMQP的服务,例如RabbitMQ服务

关于AMQP可以通过一篇很有名的文章了解更多:RabbitMQ+Python入门经典 兔子和兔子窝

RabbitMQ的运行需要erlang的支持,erlang和RabbitMQ在windows下都可以直接使用安装程序,非常简单。RabbitMQ还支持网页端的管理,这需要开启一些RabbitMQ的插件,可以参考官方文档

RabbitMQ本质上其实是一个服务器,与这个服务器做交互则是通过AMQP定义的协议,应用可以使用一个实现了AMQP协议的库来与服务器交互。这里我使用erlang的一个客户端,对应着RabbitMQ的tutorial,使用erlang实现了一遍。基于这个过程我将一些关键实现罗列出来以供记忆: