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()