XMPP Jabber即时通讯开发实践
百度首页 | 百度空间
 
文章列表
 
2008-07-24 00:00
Building Scalable Web Sites一书大家基本上都是冲着Flickr和Cal Henderson首席架构师的名头去的,最近看到有中文版了就订了一本。书中全部代码用PHP为例,但其实跟PHP关系不大。书中实用的章节不多,先看了远程服务一章。

1. 使用远程服务的第一原则是不能依赖远程服务,另外一方面远程服务要考虑冗余性。
在我理解,远程服务有几类:
1) 远程API,如XML-RPC, HTTP接口, Message Queue。
这个是业务相关的,Failure怎样做要程序自身考虑了。
2) DB存储类,如MySQL, NFS。
MySQL可用主从或MySQL proxy, NFS不知道,不稳定,关键业务用的场合不多。
3) Cache, 如Memcached,
Memcache默认可以分布多个,Failure怎样处理?我觉得如果cache失效对系统影响比较大的话可以写2份,否则就立即启用备用cache。

书中还提到一个TCO的概念,在某个场合,靠人力去优化还是加硬件,哪个成本更低?

2. 异步系统。
异步系统也是我感兴趣的地方,任何一大型的系统,都会有不少异步的设计。
书中提到一种Ticket模型:

图片来源:(books.google.com)

Ticket异步模型除了书中介绍的用来转换图片服务的场景之外,我举一个我理解的例子。
用在IM里面比如在群中发送图片功能。
1) 发送方发送/粘贴图片,立即显示发送完毕
2) 群中所有接收方立即显示接收到图片(只是个Ticket)
3) 系统实际上处于发送方上传图片中
4) 同时群中用户拿ticket去要图片(轮询or通知)。
5) 发送方上传完毕
6) 接收方凭Ticket获取到图片最终资源显示。
这个场景理论上就是和Ticket模型相似,但在实际操作上群图片还有更多细节考虑。

3. 使用轻量级协议
HTTP和XML被过量使用,某些高访问场合未必是最好的选择。
这就是Flickr为什么要使用自己的存储服务和协议。
但这个问题是双面的,首先作者强调,“大多数场合,自行开发协议是糟糕的主意”。Flickr也尝试过NFS和scp。(这也是为什么gtalk选用XMPP)

另外翻译版源代码排版比较差,空行缩进行距等细节未注意,跟我们开发人员写的Word文档排版有得一比。
 
2008-07-16 00:29
1. HiveDB是在2007年5月"Bay Area Community Meetup"首次出现,底层基于Hibernate shards基础实现。Hibernate shards 则是 Google 的开发工程师在"20%工作时间可以干别的有兴趣事情"环境下开发出来的一个 Hibernate extension,贡献给开源社区希望发扬光大;

2. HiveDB推出到现在也不算短,开发进度相对平缓。功能上已经处于一个相对稳定1.0状态,核心功能已经基本没大的问题。作者声称已经在一个每秒请求数达数千次的,包含海量数据的生产环境稳定运行;

3. HiveDB/Hibernate shards所适用的典型场合就是一个海量记录的表,可以根据某个规则分开存到多个相同表结构的数据库服务器上。和HSCALE功能差不多,但HSCALE当前版本实际上还不能跨服务器的;

4. 可以查询跨服务器数据,但不能做 order/join;

5. 具有类似mysql proxy之类多服务器容错功能,单独服务器发生故障不影响系统正常运行,通过类似ha-jdbc思想实现;

6. 目前只支持Java语言,有支持各种语言如php/python/perl/ruby hive client的计划,但是目前只有一个python hive client测试版可用。


图片:按字段(Partition Key)切分典型场合

(图片来源:hivedb.org)
 
2008-07-14 21:30
本文gtalk server架构介绍整理自视频 Seattle Conference on Scalability: Lessons In Building Scalable Systems 而得,加入Tim少量理解和补充。

演讲者 Reza Behforooz , 是 Google Talk servers的 team leader

1. Google talk server基于 java 平台实现,见视频23:50问答。

2. 关于系统设计与实现难点
和任何xmpp server一样,难点是处理Presence峰值流量,而不是用户数或并发用户数
峰值 total QPS > 10万
Presence = ConnectedUsers * BuddyListSize * OnlineStateChanges
系统在一夜之间通过 gmail/orkut用户突然增大,没有一个缓慢适应的过程。

3. 关于压力测试
1) 实验室式的压力测试只是一个开端;
2) 在正式上线之前,通过真实环境的试验平台,选取大约10%的用户,来做实际环境测试,在不显示UI的前提下,将google talk代码嵌入用户页面
这样在gtalk正式上线时候,所有功能已经是比较有把握的。

4. 数据和应用服务分布到多服务器
可以动态扩容,升级和更新,无须停机
但是 google talk server服务并不考虑跨 IDC, 因为作者认为在海量流量处理下,跨数据中心不利于系统处理。
分布包括数据分布,可参看以前文章MySQL 分表分数据库服务器的一种方案HSCALE, 基于MySQL proxy 另外还有请求和逻辑服务分布

5. abstraction, 抽象与分离
不将问题带入别的系统, gmail/gtalk单独出问题不影响另外一方使用。
gmail, orkut 与 google talk server 完全没有关系
如 gmail 无须关心 google talk 有哪些服务器,有多少,在哪里,怎样分布。
不同系统之间通过 gateway(logic name) 来访问, gateway 再映射到物理的服务器。

6. 避免客户端或服务器自动重连造成DoS问题,服务器瞬时访问过大而瘫痪

7. 在服务器增加profile/monitor机制,包括配置文件,资源状况, 日志, 可以做离线分析。
这样某个服务器发现问题,首先去看 monitor console
profile/monitor不是侵入式的,就是不是通过在服务器程序嵌入代码实现。

8. Google Talk Server其他一些开发经验
程序可以先前和向后兼容(协议/API兼容),可以逐个或批量升级,新老系统可以共存
有良好的平台来试验新功能,就是上文中提到可以用gmail来试验gtalk,这个是很多公司不具备的
开发人员可以直接访问真实生产环境(production environment),观察系统实际运行情况来调优和改进。
 
2008-06-30 22:45
Presence处理是IM Server的核心,也是一个IM Server最复杂的部分。一个用户的状态发生变化,需要通过服务器自动投递给他所有在线的好友,因此Presence模块实际上等同一个消息处理服务器,可参看以前消息服务器相关文章ActiveMQ性能研究及与memcacheq比较

Presence的复杂性体现在:

1. 由于每个用户都有1到多个好友,服务器的处理量被放大。
2. 分布式处理的复杂度,你的好友可能同时分布在n个服务器上,而且同时上线的好友没有规律。
3. 请求量不均衡,可能瞬时非常大。比如你服务器刚重启所有的客户几乎同时自动重连过来。比如Twitter宕机都是在一些热点事件时,大家活跃度突然同时增大。所以系统必须按峰值的处理量设计。
4. 缓存cache设计困难。每个用户的在线好友都不同,而且随时在变。
5. 隐身同黑名单的业务逻辑很难高效处理。

Openfire Server处理presence的流程如下,以3.6.0为准。


1. ConnectionHandler.messageReceived();
mina 层面处理。

2. StanzaHander.process() => processPresence
xmpp 层面。处理所有xmpp包的方法,实际上只有login相关包在这里处理。其他类型的包交由相关逻辑类来处理。 由于是个presence包,交由下面presence逻辑处理模块进行。(also add from to packet)

3. PacketRouteImpl.route() // route presence
4. PresenceRoute.route() => handle() // route presence
由于presence是一个需要路由的包,路由主要区分目标是本机还是远程,是component/server还是普通用户。

5. PresenceUpdateHandler.process() => broadcastUpdate
// process() update db and update cache,
calls PresenceManager.userAvaliable(); session.setPresence()...

6. Roster.boradcastPresence();
检查privacy list(隐身及黑名单用户)然后路由给所有在线好友。

7. RoutingTable.routePacket, routeTable.getRoutes()
真正的工作在这里,较慢。

8. session.process(), session.deliver
已经分发到相关用户了,调用该用户的session投递给此用户

9. nioconnection().deliver, deliver to the end users
再回到MINA

因此Presence投递工作的核心是在6~7,不过其他的步骤也有不少细节的处理。Openfire中6~7的实现比较精简和优雅,但如果想作为一个大型的高效消息投递系统还是有改进的空间。
 
2008-06-14 01:34

在大型的应用中,我们经常碰到MySQL的表数据需要无限扩充的情形。我们通常有以下一些解决方案,但是现成的方案都不是完美的。

比如,
MySQL master/slave: 只适合大量读的情形,未必适合海量数据。
MySQL cluster: 提供的可能不是大家想要那种功能。
MySQL proxy: MySQL master/slave配合
MySQL 5.1 partition: 只是将一个表存储上逻辑分开,部分改善了性能,但是可扩展性仍然是问题。
MySQL 按应用逻辑分表和分数据库,通过程序来决定数据存放的表,目前很多公司都是这么做的。它的主要问题是跨区查询,可参考Tim以前的文章MySQL分表实现上百万上千万记录分布存储的批量查询设计模式

使用程序来分表分服务器最大的问题是比较繁琐,需要程序做很多特殊处理,需要程序员了解数据存放在哪个服务器哪个表,这样,几乎所有的程序员都牵涉了进来, 也容易出错。那如果我们把分表的逻辑放到中间层则上层的应用就简单很多,而且可以单点控制分表的逻辑,方便调整与扩展。

  • HSCALE分表分数据库的思路


    HSCALE就是这样一个产品,它是在MySQL proxy的 基础上,在MySQL proxy的层面将上层的请求分配到实际的表上。实际的原理是通过拦截SQL进行替换和服务器重定向再将SQL传递到目标服务器上。它的分表算法可以由自定义的Lua脚本来实现,非常灵活。目前已经能支持同数据库分表,跨数据库的实现也将增加,因为在MySQL proxy的框架下,这并不是很困难的事情。现在的版本或许不是很成熟,但是在原理上我觉得是基本上没多大障碍,发展下去将是一个不错的选择。

    HSCALE具体的性能测试简单介绍如下。
    • 测试
    使 用HSCALE有2个开销,一是网络层面的,下面的测试环境大约MySQL proxy对每个SQL会增加0.02ms的网络延迟,如果增加了HSCALE, 则会增加到0.3ms,第2个开销则是MySQL proxy, Lua, SQL解析,HSCALE算法等造成,可看下面数据。

    (图片来源:pero.blogs.aprilmayjune.org)

    结论是最极端的情况下,在10个线程的情况下,使用MySQL proxy会需要大约3倍时间,HSCALE则是10倍。
    注意结论是MySQL方面最优化的情况,查找一个三条记录的表。在实际环境中的latency和这个没有直接比例关系(比如1:3)。测试结果不太令人满意,幸好后面新版本MySQL proxy的测试数据得到了改善。

    使用了MySQL proxy 的 svn版本,性能提升很大。MySQL/MySQL proxy从1:3提升到1:2, HSCALE同样也提升比较大。具体结果见连接。但是仍然迫切希望作者再有提升。

    今天说到大部分技术Blog都以介绍国外技术与产品的文章为主,没有深度,当然我这篇也不例外。:)
  •  
    2008-06-06 22:52
    IM中,通常有需要保存一些用户的私有数据到服务器,比如用户的本地配置信息。此功能可以通过 XEP-0049: Private XML Storage实现。

    如:

    Example 1. 客户端保存私有信息
    CLIENT:
    <iq type="set" id="1001">
    <query xmlns="jabber:iq:private">
    <exodus xmlns="exodus:prefs">
    <defaultnick>Hamlet</defaultnick>
    </exodus>
    </query>
    </iq>

    SERVER:
    <iq type="result"
    from="hamlet@shakespeare.lit/denmark"
    to="hamlet@shakespeare.lit/denmark"
    id="1001"/>

    Example 2. 客户端获取私有信息
    CLIENT:
    <iq type="get" id="1002">
    <query xmlns="jabber:iq:private">
    <exodus xmlns="exodus:prefs"/>
    </query>
    </iq>

    SERVER:
    <iq type="result"
    from="hamlet@shakespeare.lit/denmark"
    to="hamlet@shakespeare.lit/denmark"
    id="1002">
    <query xmlns="jabber:iq:private">
    <exodus xmlns="exodus:prefs">
    <defaultnick>Hamlet</defaultnick>
    </exodus>
    </query>
    </iq>

    用户可以保存多个不同的信息到服务器,多个信息可以设置不同的 namespace, 类似 key:value 中key的作用。

    Openfire 支持XEP-0049, 其中的表结构为:
    +-----------+--------------+------+-----+---------+-------+
    | Field     | Type         | Null | Key | Default | Extra |
    +-----------+--------------+------+-----+---------+-------+
    | username | varchar(64) | NO   | PRI |         |       |
    | name      | varchar(100) | NO   | PRI |         |       |
    | namespace | varchar(200) | NO   | PRI |         |       |
    | value     | text         | NO   |     |         |       |
    +-----------+--------------+------+-----+---------+-------+


    根据表结构可以看到,实际服务器实现时候把name和namespace加起来作为区分不同key标志。name就是example1中的exodus,namespace为exodus:prefs
     
    2008-05-22 23:47
    最近看到了不少Facebook chat技术架构的介绍,如 InfoQInside Facebook。总结如下,公开的资料以概念为主,没有什么新的或特殊的亮点。

    Presence notification
    • Facebook Chat 与一般的IM系统一样。最复杂之处不是消息发送,而是在线状态通知。而facebook为了提高用户体验,考虑增加的功能更增大这些处理,比如好友可以看到某个用户“idle-for-10-minutes”,用户即使不操作也有巨大负荷。
    • 处理量是:O(平均好友数 * 在线用户峰值 * 变化频率) msg /sec ?
    Comet
    • Facebook 网页上是一种特殊的comet, 每打开一个页面调用一个iframe, iframe直至有消息才会输出。Polling + push?
    • Comet不适合用Apache来跑, 可参看comet server之java实现:asyncweb,jetty,tomcat
    • Facebook使用基于epoll技术的erlang web server
    Erlang
    • Why Erlang? 因为erlang有天生的并发支持,不需要one thread per connection,轻量级的 "processes"
    • Erlang跟其他语言交互式是选用Erlang的最大问题,Facebook为了解决这个问题,自己开发了open source的Thrift,实现远程RPC
    因暂时没计划用Erlang, 借鉴的地方不多,想试下Java7 Doug Lea 的 Fork/Join


    (图片来源为 blog.rd2inc.com)

     
    2008-05-14 23:46
    XMPP Server(IM Server)不像Web Server有十分标准的性能评测工具, 一个新的IM服务器拿到之后,通常并没有十分方便的方式来进行测试性能。

    通常我们只能依赖网上的数据来衡量某个服务器产品的性能。但是网上的数据通常并不准确,因为测试的环境, 测试的指标不同,测试的方法有差异,得到的结果并没有直接的可比性。我们去验证某些测试结果,得出的数据有2~3倍的差异并不奇怪。

    因此最好通过自己的环境去验证这些产品的性能指标,所以构建一套简单的性能测试工具十分重要。目前想到的测试一个XMPP Server的几种方案。
    • 使用 Smack API, 它是一个 Java 的XMPP Client Library,也是由Jive Software开发。
    • 使用 Java Socket异步NIO select方式, 模拟XMPP登录。XML可直接用string构建,可直接根据XML返回结果的特征字符判断调用是否成功。如使用XML library来解析可能会造成部分自身瓶颈。
      • 优点:可以模拟更多的客户连接。1台客户机可以模拟数万个用户同时操作。
      • 编程稍复杂,需要熟悉Java NIO async socket,或相关框架,如Mina
      • 代码示例,使用MINA模拟客户端连接到服务器

    /**
    * Use mina client to connect xmpp server
    * @author Tim
    *
    */
    public class XmppClient {
    public static void main(String[] args) {
    String hostname = "server";
    int port = 5222;

    SocketConnectorConfig cfg = new SocketConnectorConfig();
    cfg.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));

    for (int i = 1; i <= 2; i++) {
    SocketConnector connector = new SocketConnector();
    connector.connect(new InetSocketAddress(hostname, port),
    new XmppProtocolHandler("tim" + i, "tim", "mina"), cfg);
    }
    }
    }

    class XmppProtocolHandler extends IoHandlerAdapter {
    String username = "tim";
    String password = "tim";
    String server = "server";
    String bareJid = username + "@" + server;
    String resource = "mina";

    public XmppProtocolHandler(String username, String password, String resource) {
    this.username = username;
    this.password = password;
    this.resource = resource;
    this.bareJid = username + "@" + server;
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
    System.err.println("Closed. Total " + session.getReadBytes() + " byte(s)");
    }

    @Override
    public void messageReceived(IoSession session, Object message)
    throws Exception {
    ByteBuffer buf = (ByteBuffer) message;
    while (buf.hasRemaining()) {
    System.out.print((char) buf.get());
    }
    System.out.flush();
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
    sendPacket(session, sendStream(true));
    Thread.sleep(50);
    sendPacket(session, sendAuth(username, bareJid, password));
    Thread.sleep(200);
    sendPacket(session, sendStream(false));
    Thread.sleep(50);
    sendPacket(session, sendResource(username, resource));
    Thread.sleep(50);
    sendPacket(session, sendPresence(username));
    }

    private String genPassword(String bareJid, String username, String password) {
    String str = bareJid + "\0" + username + "\0" + password;
    BASE64Encoder base64 = new BASE64Encoder();
    return base64.encode(str.getBytes());
    }

    private StringBuilder sendStream(boolean addHeader) throws IOException {
    StringBuilder sb = new StringBuilder();
    if (addHeader)
    sb.append("<?xml version='1.0' encoding='UTF-8'?>");
    sb.append("<stream:stream to=\"").append(server)
    .append("\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">");
    return sb;
    }

    private StringBuilder sendAuth(String username, String bareJid,
    String password) throws IOException {
    String pwd = genPassword(bareJid, username, password);
    StringBuilder sb = new StringBuilder();
    sb.append("<auth mechanism=\"PLAIN\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">")
    .append(pwd).append("</auth>");
    return sb;
    }

    private StringBuilder sendResource(String username,
    String resource) throws IOException {
    StringBuilder sb = new StringBuilder();
    sb.append("<iq id=\"").append(username)
    .append("-0\" type=\"set\"><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"><resource>")
    .append(resource).append("</resource></bind></iq>");
    return sb;
    }

    private StringBuilder sendPresence(String username)
    throws IOException {
    StringBuilder sb = new StringBuilder("<presence id=\"")
    .append(username).append("-2\" />");
    return sb;
    }

    private void sendPacket(IoSession session, StringBuilder sb) throws Exception {
    sb.append("\r\n");
    System.out.println(sb.toString());
    session.write(sb.toString());
    }
    }
    • 使用 tsung, tsung使用erlang开发,利用了erlang并发编程的优势,可以从一到多个客户机向发起xmpp请求。
      • 优点:利用现成的工具,无需开发。测试过程使用XML来配置,不需要改动源代码。
      • 缺点:配置和扩展复杂,学习成本,适应成本及扩展成本大。
    • 使用 Python 等动态语言开发,如PyXMPP
      • 优点:开发及重构效率比Java稍快,可以更多关注测试模型及指标本身。而不是测试程序怎么实现。
      • 缺点:需要开发人员熟悉相关语言及特性。另外同样有 one thread per client 的问题,占用客户端资源过高。可能要几台客户机才能搞定一台服务器。
      • 代码示例,下面例子可以对实现一个Python的client有个基本了解,详细可参考 PyXMPP examples 下面的例子。
    class MyClient(Client):
    def session_started(self):
    self.stream.send(Presence())

    def idle(self):
    print "idle"
    Client.idle(self)
    if self.session_established:
    target=JID("tim2",s.jid.domain)
    self.stream.send(Message(to_jid=target,body=unicode("测试","utf-8")))

    def post_disconnect(self):
    print "Disconnected"
    raise Disconnected

    logger=logging.getLogger()
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)

    libxml2.debugMemory(1)

    print "creating stream..."
    s=MyClient(jid=JID("tim@server/Test"),password=u"tim",auth_methods=["sasl:DIGEST-MD5","digest"])

    print "connecting..."
    s.connect()

    print "processing..."
    try:
    try:
    s.loop(1)
    finally:
    s.disconnect()
    except KeyboardInterrupt:
    traceback.print_exc(file=sys.stderr)
    except (StreamError,Disconnected),e:
    raise

    libxml2.cleanupParser()
    if libxml2.debugMemory(1) == 0:
    print "OK"
    else:
    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
    libxml2.dumpMemory()
     
    2008-05-12 18:50
    Twitter的手机访问界面m.twitter.com每次访问都需要输入密码,很不方便。看了一下Twitter API发文比较简单,原理为:

    Post a status update, authenticated: curl -u email:password -d status="your message here" http://twitter.com/statuses/update.xml

    因此就可利用curl写个脚本,把自己用户名、密码设进去以后就不用每次登录了。

    <?php
    header ('Content-type: text/html; charset=utf-8');
    echo '<?xml version="1.0" encoding="UTF-8"?>';

    /*
    POST Status update to Twitter USING PHP AND CURL
    Author, Tim, iso1600 at gmail dot com
    */

    // Your Twitter username/password
    $my_username="username"; //change this to your login username
    $my_password="password"; // change this to your login password

    // What are you doing?
    $status = $_POST['status'];
    if ($status != '') {
    $content = "status=".urlencode($status);
    $headers = array( "Content-type: application/x-www-form-urlencoded" );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/update.xml");

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 4);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_USERPWD, $my_username.':'.$my_password);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $content);

    $data = curl_exec($ch);

    if (curl_errno($ch)) {
    print curl_error($ch);
    } else {
    curl_close($ch);
    }
    }
    ?>
    <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head><title>Twitter mobile post</title></head><body>
    <form method="post" action="twitter.php"> <input type="text" name="status"> <input type="submit" > </form>
    <?php

    // Get your twitter homepage.
    $myt = curl_init();
    curl_setopt($myt, CURLOPT_URL, "http://m.twitter.com/".$my_username);
    $html = curl_exec($myt);
    $p1 = strpos($html, "<body ");
    $p2 = strpos($html, ">", p1) + 1;
    $p3 = strpos($html, "</body>");
    echo substr($html, $p2, $p3);
    ?>
    </body></html>


    写完后传到某虚拟主机测试,发现调用失败,因为大部分虚拟主机不支持curl。
    那只得改用socket, php4, 按最低配置写,经过PC和手机测试都可以成功更新状态。
    有PHP虚拟主机的朋友可以上传到某个隐藏路径,方便自己用,改下用户和密码就行了。

    <?php
    header ('Content-type: text/html; charset=utf-8');
    echo '<?xml version="1.0" encoding="UTF-8"?>';
    /*
    POST Status update to Twitter USING PHP4 AND socket
    Author, Tim, iso1600 at gmail dot com
    */

    // Your Twitter username/password
    $my_username="username"; //change this to your login username
    $my_password="password"; // change this to your login password

    // What are you doing?
    $status = $_POST['status'];
    if ($status != '') {
    $content = "status=".urlencode($status);
    $headers = "Authorization: Basic " . base64_encode("$my_username:$my_password");
    do_post_request("http://twitter.com/statuses/update.xml", $content, $headers);
    }
    ?>
    <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head><title>Twitter mobile post</title></head><body>
    <form method="post" action="twitter.php"> <input type="text" name="status"> <input type="submit" > </form>
    <?php

    // Get your twitter homepage
    $html = do_get_request("http://m.twitter.com/".$my_username);
    $p1 = strpos($html, "<body ");
    $p2 = strpos($html, ">", p1) + 1;
    $p3 = strpos($html, "</body>");
    echo substr($html, $p2, $p3);

    function do_post_request($url, $data, $optional_headers = null) {
    $start = strpos($url,'//')+2;
    $end = strpos($url,'/',$start);
    $host = substr($url, $start, $end-$start);
    $domain = substr($url,$end);
    $fp = pfsockopen($host, 80);
    if(!$fp) return null;
    fputs ($fp,"POST $domain HTTP/1.1\n");
    fputs ($fp,"Host: $host\n");

    if ($optional_headers) {
    fputs($fp, $optional_headers);
    }
    fputs ($fp,"Content-type: application/x-www-form-urlencoded\n");
    fputs ($fp,"Content-length: ".strlen($data)."\n\n");
    fputs ($fp,"$data\n\n");

    $response = "";
    while(!feof($fp)) {
    $response .= fgets($fp, 1024);
    }
    fclose ($fp);
    return $response;
    }

    function do_get_request($url) {
    $start = strpos($url,'//')+2;
    $end = strpos($url,'/',$start);
    $host = substr($url, $start, $end-$start);
    $domain = substr($url,$end);
    $fp = pfsockopen($host, 80);
    if(!$fp) return null;
    fputs ($fp,"GET $domain HTTP/1.0\n");
    fputs ($fp,"Host: $host\n\n");

    $response = "";
    while(!feof($fp)) {
    $response .= fgets($fp, 1024);
    }
    fclose ($fp);
    $start = strpos($response, "\r\n\r\n");
    return substr($response, $start);
    }
    ?>
    </body></html>
     
    2008-05-07 15:07
    前面简单测试了memcacheq一个国内开发的message queue之性能研究,所以想了解下目前流行的Apache ActiveMQ在相同环境的表现,先声明这个比较没有广泛的借鉴意义,因为ActiveMQ是一全面的产品,因为本人只用到部分单一功能,所以做以下测试,测试环境和上文相同,简单说明如下
    • 版本:ActiveMQ 5.1.0, Java 6
    • ActiveMQ使用默认配置,
    • producer 和 consumer 100万条信息,每条100字节
    • 使用默认Examples下面ProducerTool.java & Consumer.java 加入几行统计时间速度代码,使用单线程。
    • ActiveMQ支持 async publisher (non-persistent) 和 sync publisher, 区别比较大,persistent msg client会阻塞到服务器存入成功并返回结果才完成。
    • 存储使用默认的 AMQ Message Store
    • 修改了 conf/activemq/activemq.xml中内存的配置,如下


    <destinationPolicy>
    <policyMap>
    <policyEntries>
    <policyEntry queue="&gt;" memorylimit="1500mb">
    <policyEntry topic="&gt;" memorylimit="5mb">
    <dispatchPolicy>
    <strictOrderDispatchPolicy>
    </strictOrderDispatchPolicy>
    <subscriptionRecoveryPolicy>
    <lastImageSubscriptionRecoveryPolicy>
    </lastImageSubscriptionRecoveryPolicy>
    </subscriptionRecoveryPolicy>
    </dispatchPolicy>
    </policyEntry>
    </policyEntry>
    </policyEntries></policyMap></destinationPolicy>

    测试结果:
    1. 单独 producer
    10,027 async non-persistent msgs / sec
    4,462 persistent msgs / sec,单位下同

    2. 单独 consumer, 使用上步的数据
    8,195
    848

    3. 同时producer 和 consumer, 当producer到10万条的时候开始 consumer
    producer avg:
    5,593
    960
    consumer avg:
    4,462
    1,002

    结果分析:
    • 同时producer&consumer偶尔有卡住3~5s的现象(可能硬盘慢造成?),但会自动恢复
    • 使用 async non-persistent 方式速度极快,对于偶尔丢失少量数据不敏感的应用极为适合。
    • 使用 persistent 和 memcacheq 的速度区别不大。
    • ActiveMQ 可以 tuning,因为相关配置复杂,没有进一步去研究。
     
         
     
     
    个人档案
     
    iso1600

    广东 
    上次登录:
    1天前
    加为好友
     
       
     
    文章分类
     
     
    Jep(11)
     
    Xmpp(12)
     
     
     
     
     
     
     
     
     
     
     
     
     
    Xep(1)
     
         
     
    RSS订阅
     
       
     
    其它
     
    已有人次访问本空间
     
    订阅RSS  什么是RSS?

    您也想拥有这样的空间?请点此申请。
         
     
    最新评论
       
         
     
    最近访客
     
     

    dragonlady

    movinghorse

    xsharecn

    xf47

    16199

    mccheresy

    yatou_2006

    mr_ziqiang
         


    ©2008 Baidu