tsungでjabberのmulti user chatに対応するためのpatch

tsungはerlangで書かれたベンチマーク。MUCに対応していなかったので、練習がてら作ってみた。

前提

ユーザ名が"prefix(xxx)"になってること。(xxxは数字)

追加するもの

追加したoptionは以下の二つ。

<option type="ts_jabber" name="muc:domain" value="conference"></option>
<option type="ts_jabber" name="muc:member" value="2"></option>
[]muc
domain[]:mucサーバのドメイン。(同じoptionのname="domain"の前にドットでつなげるだけ。)
[]muc
member[]:ひとつのroomに何人入るか。

追加したrequestは以下の四つ。

<request> <jabber type="muc:enter" ack="no_ack"></jabber> </request>
<request> <jabber type="muc:initial" ack="local"></jabber> </request>
<request> <jabber type="muc:chat" show="xa" status="status" ack="no_ack" size="100"/> </request>
<request> <jabber type="muc:exit" ack="no_ack"></jabber> </request>
[]muc
enter[]:roomに入室
[]muc
initial[]:(roomがまだ無ければ)roomをつくる。
[]muc
chat[]:roomにメッセージを送る。
[]muc
exit[]:roomから退室する。

使い方

こんな感じで利用する。

 <options>
  <option type="ts_jabber" name="global_number" value="100"></option>
  <option type="ts_jabber" name="userid_max" value="10000"></option>
  <option type="ts_jabber" name="domain" value="localhost"></option>
  <option type="ts_jabber" name="username" value="user"></option>
  <option type="ts_jabber" name="passwd" value="password"></option>
  <option type="ts_jabber" name="muc:domain" value="conference"></option>
  <option type="ts_jabber" name="muc:member" value="10"></option>
 </options>

  <sessions>
   <session probability="100" name="jabber-example" type="ts_jabber">

    <request> <jabber type="connect" ack="no_ack"></jabber> </request>
    <thinktime value="2"></thinktime>
    <transaction name="auth_plain">
      <request> <jabber type="auth_get" ack="local"></jabber> </request>
      <request> <jabber type="auth_set_plain" ack="local"></jabber> </request>
      <request> <jabber type="muc:enter" ack="local"></jabber> </request>
      <request> <jabber type="muc:initial" ack="local"></jabber> </request>
    </transaction>

    <request> <jabber type="presence:initial" ack="no_ack"/> </request>
    <thinktime value="2"></thinktime>

    <request> <jabber type="muc:chat" show="xa" status="I may never come back..." ack="no_ack" size="100"/> </request>
    <thinktime value="2"></thinktime>

    <transaction name="close">
      <request> <jabber type="muc:exit" ack="local"></jabber> </request>
      <request> <jabber type="close" ack="no_ack"></jabber> </request>
    </transaction>

patch

diff -crN tsung-1.2.1.old/include/ts_jabber.hrl tsung-1.2.1/include/ts_jabber.hrl
*** tsung-1.2.1.old/include/ts_jabber.hrl	2006-09-20 19:52:53.000000000 +0900
--- tsung-1.2.1/include/ts_jabber.hrl	2007-05-16 18:34:10.000000000 +0900
***************
*** 34,39 ****
--- 34,41 ----
				  cle,
				  id = 0,
				  domain,   %% jabber domain
+ 				 muc_domain, %% jabber muc domain
+ 				 muc_member, %% muc room per member
				  username, %% first chars of username (will append id dynamically)
				  passwd,   %% first chars of passwd (will append id dynamically)
				  nonce,    %% used to generate sip-digest passwd
diff -crN tsung-1.2.1.old/src/tsung/ts_jabber_common.erl tsung-1.2.1/src/tsung/ts_jabber_common.erl
*** tsung-1.2.1.old/src/tsung/ts_jabber_common.erl	2006-09-20 19:52:53.000000000 +0900
--- tsung-1.2.1/src/tsung/ts_jabber_common.erl	2007-05-16 18:34:43.000000000 +0900
***************
*** 183,188 ****
--- 183,197 ----
	       << >>
      end;

+ get_message(Jabber=#jabber{type = 'muc:enter'}) ->
+     muc_get_message(Jabber);
+ get_message(Jabber=#jabber{type = 'muc:initial'}) ->
+     muc_get_message(Jabber);
+ get_message(Jabber=#jabber{type = 'muc:chat'}) ->
+     muc_get_message(Jabber);
+ get_message(Jabber=#jabber{type = 'muc:exit'}) ->
+     muc_get_message(Jabber);
+ 
  get_message(Jabber=#jabber{username=Name, passwd=Passwd, id=Id}) ->
      FullName = Name ++ Id,
      FullPasswd = Passwd ++ Id,
***************
*** 190,195 ****
--- 199,259 ----


  %%----------------------------------------------------------------------
+ %% Func: muc_get_message/1
+ %%----------------------------------------------------------------------
+ muc_get_message(Jabber=#jabber{username=Name, id=Id, domain=Domain, muc_domain = MucDomain, muc_member=MucMember}) ->
+     Room = integer_to_list(list_to_integer(Id) div MucMember),
+     Nick = integer_to_list(list_to_integer(Id) rem MucMember),
+     FullName = Name ++ Id ++ "@" ++ Domain ++ "/" ++ Nick,
+     RoomName = Room ++ "@" ++ MucDomain ++ "." ++ Domain,
+     muc_get_message(Jabber#jabber{username=FullName}, RoomName, Nick).
+ %%----------------------------------------------------------------------
+ %% Func: muc_get_message/3
+ %%----------------------------------------------------------------------
+ muc_get_message(#jabber{type = 'muc:enter', username=UserName}, Room, Nick) ->
+     list_to_binary(
+       ["<presence id='", ts_msg_server:get_id(list),
+        "' from='", UserName,
+        "' to='", Room , "/", Nick,
+        "'> <x xmlns='http://jabber.org/protocol/muc#user'/> </presence>"
+       ]);
+ muc_get_message(#jabber{type = 'muc:initial', username=UserName}, Room, Nick) ->
+     list_to_binary(
+       ["<iq id='", ts_msg_server:get_id(list),
+        "' from='",  UserName,
+        "' to='", Room, "/", Nick,
+        "' type='set'>",
+        "<query xmlns='http://jabber.org/protocol/muc'>",
+        "<x xmlns='jabber:x:data' type='submit'/>",
+        "</query></iq>"
+       ]);
+ muc_get_message(#jabber{type = 'muc:chat', size=Size, data=undefined, username=UserName}, Room, _Nick) ->
+     list_to_binary(
+       ["<message id='", ts_msg_server:get_id(list),
+        "' from='", UserName,
+        "' to='", Room,
+        "' type='groupchat'",
+        "> <body>", garbage(Size),"</body></message>"       
+       ]);
+ muc_get_message(#jabber{type = 'muc:chat', data=Data, username=UserName}, Room, _Nick) ->
+     list_to_binary(
+       ["<message id='", ts_msg_server:get_id(list),
+        "' from='", UserName,
+        "' to='", Room,
+        "' type='groupchat'",
+        "> <body>", Data, "</body></message>"       
+       ]);
+ muc_get_message(#jabber{type = 'muc:exit', username=UserName}, Room, Nick) ->
+     list_to_binary(
+       ["<presence id='", ts_msg_server:get_id(list),
+        "' from='", UserName,
+        "' to='", Room, "/", Nick,
+        "' type='unavailable'/>"
+       ]).
+ 
+ 
+ 
+ %%----------------------------------------------------------------------
  %% Func: get_message2/1
  %%----------------------------------------------------------------------
  get_message2(Jabber=#jabber{type = 'register'}) ->
diff -crN tsung-1.2.1.old/src/tsung_controller/ts_config_jabber.erl tsung-1.2.1/src/tsung_controller/ts_config_jabber.erl
*** tsung-1.2.1.old/src/tsung_controller/ts_config_jabber.erl	2006-09-20 19:52:53.000000000 +0900
--- tsung-1.2.1/src/tsung_controller/ts_config_jabber.erl	2007-05-16 18:35:38.000000000 +0900
***************
*** 62,67 ****
--- 62,69 ----
	 Domain  =ts_config:get_default(Tab, jabber_domain_name, jabber_domain),
	 UserName=ts_config:get_default(Tab, jabber_username, jabber_username),
	 Passwd  =ts_config:get_default(Tab, jabber_passwd, jabber_passwd),
+ 	MucDomain  =ts_config:get_default(Tab, jabber_muc_domain_name, jabber_muc_domain_name),
+ 	MucMember  =ts_config:get_default(Tab, jabber_muc_room_per_member, jabber_muc_room_per_member),

	 Msg=#ts_request{ack   = Ack,
		       dynvar_specs= DynVar,
***************
*** 69,74 ****
--- 71,78 ----
		       subst   = SubstFlag,
		       match   = MatchRegExp,
		       param = #jabber{domain = Domain,
+ 									muc_domain = MucDomain,
+ 									muc_member = MucMember,
				       username = UserName,
				       passwd = Passwd,
				       data   = Data,
***************
*** 96,101 ****
--- 100,111 ----
	   "domain" ->
	       Val = ts_config:getAttr(string,Element#xmlElement.attributes, value,"erlang-projects.org"),
	       ets:insert(Tab,{{jabber_domain_name,value}, Val});
+         "muc:domain" ->
+             Val = ts_config:getAttr(string,Element#xmlElement.attributes, value,"conference"),
+             ets:insert(Tab,{{jabber_muc_domain_name,value}, Val});
+         "muc:member" ->
+             N = ts_config:getAttr(integer,Element#xmlElement.attributes, value,10),
+             ets:insert(Tab,{{jabber_muc_room_per_member,value}, N});
	   "global_number" ->
	       N = ts_config:getAttr(integer,Element#xmlElement.attributes, value, 100),
	       ts_timer:config(N),
diff -crN tsung-1.2.1.old/src/tsung_controller/ts_user_server.erl tsung-1.2.1/src/tsung_controller/ts_user_server.erl
*** tsung-1.2.1.old/src/tsung_controller/ts_user_server.erl	2006-09-20 19:52:53.000000000 +0900
--- tsung-1.2.1/src/tsung_controller/ts_user_server.erl	2007-05-16 18:35:26.000000000 +0900
***************
*** 199,205 ****
      {reply, State#state.first_client, State};

  handle_call({reset, NFin}, _From, _State) ->
!     Offline = ets:new(offline,[set, private]),
      Online  = ets:new(online, [set, private]),
      Connected  = ets:new(connected, [set, private]),

--- 199,206 ----
      {reply, State#state.first_client, State};

  handle_call({reset, NFin}, _From, _State) ->
!     %%Offline = ets:new(offline,[set, private]),
!     Offline = ets:new(offline,[ordered_set, private]),
      Online  = ets:new(online, [set, private]),
      Connected  = ets:new(connected, [set, private]),

問題点

  • etsの変更

ランダムなIDが割り当てられてしまうのを避けるためにts_user_server.erlで生成しているofflineのユーザの集合をordered_setにしている。これが問題かどうかは追っていないけど。

  • mucのベンチを正しく取れない?

ts_client.erlにあるhandle_next_requestやhandle_infoがそれぞれメッセージの生成と送信、受信を行っている。
erlang:displayで送受信を出力してみると一目瞭然なのだが、mucを使うと受信が爆発的に増える。これのおかげで、サーバに負荷をかけたいのに逆にアタックを受けるような状態に。。。
roomにN人いて、それぞれが1回メッセージを送ると(N-1)Nの送信が発生するからなぁ。

あ、一台でやるのがいけないのか!Erlangなんだし二台三台でやれば問題無いのかも。

コメントください