IM/XMPP服务器选型

一、IM协议选型


IM协议按照是否公开可以分为私有协议(腾讯QQ)和开放协议(GTalk)。私有IM协议需要从零开始设计和搭建,时间和财力成本极高。而开放协议:1)经过业界的长期研究和验证,在安全性、完备性容、容错性等诸多方面都有保障。2)由于其开放的特性,业界已经有很多优秀的开源IM Server和IM Client,直接基于这些开源组件进行开发,可以在相对短的时间内快速搭建高质量的Chat基础服务。3)开放协议一般具有较高的可扩展性,可以进行协议的扩展和改造,以适应特殊的应用场景和需求。


目前主流的IM协议有XMPP (Extensible Messaging and Presence Protocol), SIMPLE(session initiation protocol for instant messaging and presence leveraging extensions), IMPP (Instant Messaging and Presence Protocol).


IMPP主要定义了必要的协议和数据格式,用来构建一个具有空间接收、发布能力的即时信息系统。到目前为止,这个组织已经出版了三个草案RFC,但主要的有两个:一个是针对站点空间和即时通讯模型的(RFC 2778);另一个是针对即时通讯/空间协议需求条件的(RFC2779)。RFC2778是一个资料性质的草案,定义了所有presence和IM服务的原理。RFC2779定义了IMPP的最小需求条件。另外,这个草案还就presence服务定义了一些条款,如运行的命令、信息的格式,以及presence服务器如何把presence的状态变化通知给客户。


SIMPLE计划利用SIP来发送presence信息。SIP是IETF中为终端制定的协议。SIP一般考虑用在建立语音通话中,一旦连接以后,依靠如实时协议(RTP)来进行实际上的语音发送。但SIP不仅仅能被用在语音中,也可以用于视频。SIMPLE被定义为建立一个IM进程的方法。SIMPLE在2002年夏季得到额外的信任,目前,微软和IBM都致力于在它们的即时通讯系统中实现这个协议。 SIMPLE小组致力于进程模式的操作,这将提升运行效率,使基于SIP的机制能够进行会议和三方电话交谈控制,也考虑到能和未来提供的许多新特性实现兼容并提升表现能力。有了进程模式,SIMPLE使用SIP来建立一次进程,再利用SDP(进程描述协议)来实际传输IM数据。


XMPP是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个服务器进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。


XMPP应用广泛,国内外已有众多大规模的应用案例,参见表1。基本上,除了商业公司私有化的聊天协议,XMPP已经成为事实上的标准IM协议。


表1 XMPP应用一览表

公司

产品

Google

Hangouts (前身是GTalk)

Apple

Apple Push Notification Service

iMessage

WhatsApp Inc

WhatsApp (customized)

腾讯

微信 (参考XMPP+ActiveSync)

小米

米聊

深圳市和讯华谷

极光推送

环信

环信


基于XMPP的方案拥有大量的Server端、Client端以及类库实现,可以快速搭建满足业务需求的应用。


XMPP去中心化的设计具有天生的scale out扩展性,同时支持server-2-server通信,可以与其他XMPP服务器进行连接,实现帐号的跨域聊天。XMPP的这种特性非常适合应用于提供IM基础服务的平台。


综上所诉,XMPP是以上几个协议中最完善、可扩展性最好、应用最广、组件支持最多的一个协议。因此,Chat Service选用XMPP作为Chat Service的IM协议。


二、XMPP Server选型


XMPP Server拥有众多的实现方案,参见表2.


表2 XMPP Server实现方案


Name

Platform(s)

License

Details

Latest Release

Apache Vysper

Windows / Linux

Apache License Version 2.0

mina.apache.org

2011-02-23

Citadel

Linux

GPL3

citadel.org

2013-08-14

CommuniGate Pro

Linux / Mac OS X / Windows

Commercial

communigate.com

2013-09-10

Coversant SoapBox Server

Windows

Commercial

coversant.com

unknown

djabberd

Linux

GPL3

danga.com

2011-06-13

ejabberd

Linux / Mac OS X / Solaris / Windows

GPL2

process-one.net

2013-06-28

IceWarp

Linux / Windows

Commercial

icewarp.com

2012-12-11

iChat Server

Mac OS X

Commercial

apple.com

2012-07-25

in.jabberd

Linux

GPL2

inetdxtra.sourceforge.net

2013-05-16

Isode M-Link

Linux / Solaris / Windows

Commercial

isode.com

2013-06-24

jabberd 1.x

Linux

GPL2

jabberd.org

2012-06-28

jabberd 2.x

Linux / Solaris / Windows

GPL2

jabberd2.org

2012-08-26

Jabber XCP

Linux / Solaris / Windows

Commercial

cisco.com

2008-10-31

Jerry Messenger

Linux / Windows

Commercial

j-livesupport.com

unknown

Kwickserver

Windows

GPL

kwickserver.info

2010-10-15

Metronome IM

Linux / Mac OS X

ISC/MIT

lightwitch.org/metronome

2013-10-02

MongooseIM

Linux / Mac OS X

GPL2

erlang-solutions.com

2013-05-23

Openfire

Linux / Mac OS X / Solaris / Windows

Apache

igniterealtime.org

2013-05-28

Oracle Communications Instant Messaging Server

Linux / Solaris / Windows

Commercial

oracle.com

2013-05-07

Prosody IM

Linux / Mac OS X / Windows

MIT/X11

prosody.im

2013-09-10

psyced

Linux / Mac OS X / Windows

GPL2

psyced.org

2011-11-22

Siemens OpenScape

Linux

Commercial

siemens-enterprise.com

2011-12-15

Tigase

Linux / Solaris / Mac OS X / Windows

AGPL

tigase.org

2013-04-24

Vines

Linux / Mac OS X

MIT

getvines.com

2013-06-22

Wokkel

Linux / Solaris / Mac OS X

MIT

wokkel.ik.nu

2013-01-12

其中,较为成熟,且使用广泛的是ejabberd, jabberd 2.x, Openfire, Tigase(蓝底标出). 这几个Server的详细比较可以参考这两篇文章:Choosing An XMPP Server, Android Push 开源方案解析. 以上两篇文章的结论如下:

  1. Jabberd 2.x 使用C语言实现,但是,存在着数据库事务的滥用、内存泄露、不一致的非阻塞设计等问题,最重要的是该server已经很长时间没有人维护;因此,chesspark在使用jabberd 2.x三年后,转用ejabberd。无独有偶,Jabber.org也在2010年淘汰Jabberd, 转为使用ejabberd.

  2. Openfire以及Tigase都是基于JAVA的解决方案。但是极光推送团队认为,Openfire单机并发很有限,集群方案不成熟,代码古老而缺乏及时更新,因此不适合应用在生产环境中。因此,极光团队在初期使用Tigase解决方案。但是在使用中发现,Tigase其集群方案实现复杂,单节点容量有限,后期转为自己开发server.


从编程语言角度看,主流的XMPP Server主要是JAVA和Erlang。JAVA语言的优势是类库完备,容易招人。Erlang的优势是hot code swap, live console, 高并发. ejabberd与Openfire/Tigase比较而言,最大的优势是相对优雅的集群方案以及更高的并发性能。在没有语言偏向性的情况下,一般在开发初期都会选用ejabberd, 如WhatsApp, Facebook Chat, 米聊


从各种XMPP Server支持的特性看,ejabberd是对XMPP协议支持最好、实现最为全面的server.


从开源协议看,Tigase采用AGPL开源协议,因此只要有代码修改,就必须对代码进行开源。Openfire采用Apache开源协议,修改代码后可以闭源。ejabberd采用GPL v2开源协议,如果只在服务端提供服务,而不进行代码二次分发,修改代码以后也可以「闭源」,参考


综合以上各因素,Chat Service采用ejabberd作为初期的XMPP Server.

WhatsApp 公开源代码: http://www.whatsapp.com/opensource/

Chat Secure: https://chatsecure.org/blog/

Public XMPP Server列表:https://xmpp.net/directory.php

可以查看这些server的状态及基本信息(实现方式、认证方式等)。

基于httpd的Erlang Web Server

Background

  1. 有一堆数据需要用Golang来处理,数据是使用Erlang的term_to_binary保存的。
  2. 数据是热数据,因此不能一次性对数据进行binary_to_term预处理。
  3. Erlang的Ports是不太合适的,因为Ports的设计更侧重于Erlang主动与外部进程(一般是其他语言编写的)进行通信。
  4. 基于通用协议使得Golang与Erlang直接可以通信是唯一的出路,通用协议如http, thrift, protobuf等都可以,因此这条出路还挺宽。BERT-RPC强大而笨重,但是没有Golang的RPC Client. 由于需求非常简单,考虑基于Erlang httpd写一个简单的Web Server完成这个任务。

如果不是Erlang, 这个任务应该是分分钟搞定,但是Erlang的官方文档缺乏example, 相关的文章又太少。难怪有人会吐槽:

When I first started using Erlang it took a fair bit of trial and error to create a server that generated dynamic HTTP content. The main problem is the lack of good tutorials out there for doing these kinds of things. The documentation that comes with Erlang is good for reference, but is not that helpful if you’re just learning the language.

How

下面是将用户输入的内容返回给用户的echo server代码:

  1. httpd实现了一套处理http request的behavior, 通过配置可以设置如何路由这些请求。这里使用esi的方式来处理请求。erl_script_alias依赖mod_esi. 因此{modules,[mod_esi]}不能省去。上例将/esi/echoserver:func交给echo_server:func处理。因此,可以通过 http://localhost:8080/esi/echo_server:echo?abcd你好321 进行测试。
  2. httpd定义了一堆mod_*来处理请求,modules之间的顺序有限制的,启用多个模块需要注意。
  3. mod_esi:deliver()第一段内容必须设置Header:

    Use this callback function to dynamically generate dynamic web content. When a part of the page is generated send the data back to the client through deliver/2. Note that the first chunk of data sent to the client must at least contain all HTTP header fields that the response will generate. If the first chunk does not contain the End of HTTP header, that is “\r\n\r\n”, the server will assume that no HTTP header fields will be generated.

  4. 如果返回的内容有unicode字符,需要使用unicode模块处理,否则浏览器会乱码。

References

[1] Working example for Erlang Server Interface

[2] “Hello World” Webapp in Erlang