日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java XMPP负载测试工具

發布時間:2023/12/3 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java XMPP负载测试工具 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在本文中,我們將開發用Java編寫的XMPP負載測試工具。

目錄

1.簡介 2. XMPP負載測試工具 3.先決條件 4. LoadXmppTest Java程序
4.1。 創建一個新的Maven項目 4.2。 創建主類 4.3。 XmppManager類 4.4。 建立 4.5。 負載測試
5.總結 6.參考 7.下載Maven項目

1.簡介

可擴展消息傳遞和狀態協議XMPP )是基于XML(可擴展標記語言)的面向消息的中間件的通信協議。 它是由Internet工程任務組 (IETF) 標準化并由XMPP標準基金會 (XSF)支持和擴展的開放協議。 XMPP在開放標準中定義,并使用開放系統的開發和應用方法。 因此,許多服務器,客戶端和庫的實現都以自由和開源軟件的形式分發。 XMPP擴展協議 (XEP)中還定義了許多擴展。

IgniteRealtime發行的一種免費的開源發行版提供了以下實現:

  • Openfire聊天服務器
  • Spark聊天客戶端
  • XMPP協議的Smack Java庫

Spark是類似于Messenger,What's app,Viber或Google Talk的聊天客戶端應用程序(實際上后者使用XMPP協議)。 一個人可以發送聊天消息,文件作為附件等。這些消息被發送到Openfire服務器,然后由其負責將它們傳遞到目的地,該服務器可以是直接與其連接的另一個Spark(或其他)聊天客戶端,也可以是另一個Openfire。實例(聯盟),直到他們到達最終目的地。

但是,服務器和客戶端在負載下的性能如何,即當它們必須處理許多聊天消息或許多文件傳輸時?

2. XMPP負載測試工具

存在許多解決方案來對XMPP服務器(例如Openfire)進行負載/壓力測試(列表并不詳盡):

  • 帶有XMPP協議支持插件的Apache JMeter(請參閱[1,2])
  • iksemel XMPP C庫
  • Tsung ,一種開源的多協議分布式負載測試工具

在本文中,我們將使用Smack XMPP庫編寫Java XMPP負載測試工具。

3.先決條件

您需要在系統上下載并安裝Openfire 。 要嘗試我們的負載測試工具,使用嵌入式數據庫就足夠了,即使您需要記住嵌入式數據庫(HSQLDB)將在一段時間后填滿。 當然,推薦使用真實的RDBMS。

您必須創建許多用戶來模擬用戶消息交換負載。 在我們的示例中,我們將創建50個用戶名user001到user050用戶, user050所有用戶的密碼都相同,即a 。 如果您不知道該怎么做,請登錄管理控制臺(例如http:// localhost:9090或https:// localhost:9091 ),然后單擊“ 用戶/組”標簽; 在那里,您可以單擊創建新用戶來創建用戶。

由于創建大量用戶非常繁瑣,因此有幾個插件可以節省您的時間。 單擊Openfire管理控制臺的“ 插件”標簽,然后單擊“ 可用插件”并安裝“ 用戶創建”和/或“ 用戶導入/導出”插件。 如果現在單擊返回到“ 用戶/組”選項卡,您將看到已創建新鏈接; 用戶創建 (由于用戶創建插件)和導入和導出 (由于用戶導入/導出插件)。 剩下的練習是找出它們如何工作。

但是,這些并不是唯一需要做的更改。 在最新版本的Openfire中,安全機制已更改,因此,要使我們的程序正常運行,我們需要定義兩個屬性。 單擊服務器選項卡, 服務器管理器->系統屬性,然后在頁面底部輸入以下屬性名稱/值對:

sasl.mechs.00001 PLAIN
sasl.mechs.00002 DIGEST-MD5

4. LoadXmppTest Java程序

我們將創建的工具是一個使用smack庫的Java程序。 它提供了命令行界面(CLI),但是如果發現有用,則可以為其編寫圖形用戶界面(GUI)。

$ java -Djava.util.logging.config.file=logging.properties -jar loadxmpptest.jar Required options: s, d, p, n usage: java -Djava.util.logging.config.file=logging.properties –jar loadxmpptest.jar -a,--attachment Test attachments -b,--big Test big attachments or messages -d,--domain Domain -n,--number Number of users -o,--observer Observer -p,--password Password -s,--server Server Usage : java -Djava.util.logging.config.file=logging.properties -jar loadxmpptest.jar -s -d -p -n [-o ] [-a] [-b]jabber id : userXXX@chatroom : roomXXX@conference.observer : userXXX@/Spark (just to test)10 users per chatroom5 chatrooms Use:-a to test small attachments (file transfers) or-a -b to test big attachments (file transfers) or:-b to test long messages

此外, loadxmpptest.properties允許進一步配置測試應用程序:

SHORT_MESSAGES_DELAY_SECONDS = 100 LONG_MESSAGES_DELAY_SECONDS = 60 SMALL_ATTACHMENTS_DELAY_MINUTES = 1 BIG_ATTACHMENTS_DELAY_MINUTES = 5 DELAY_TO_SEND_MESSAGES_MILLISECONDS = 1000 BIG_FILE_NAME_PATH=blob.txt SMALL_FILE_NAME_PATH=test.txt

日志存儲在log/loadxmpptest.log ,可以通過編輯logging.properties進行配置。

這是一個執行示例,其中服務器為localhost ,域為localhost (可以是其他名稱),使用相同的密碼模擬了50個用戶a :

java -Djava.util.logging.config.file=logging.properties -jar loadxmpptest.jar -s localhost -d localhost -p a -n 50

另一個例子,這次發送大型附件:

java -Djava.util.logging.config.file=logging.properties -jar loadxmpptest.jar -s localhost -d localhost -p a -n 50 -ba

如上所述,要在loadxmpptest.properties中配置要發送的文件。

4.1創建一個新的Maven項目

跳轉到您喜歡的IDE并創建一個新的Maven項目。 將以下依賴項添加到pom.xml :

<dependencies><dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-core</artifactId><version>4.3.4</version></dependency><dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-tcp</artifactId><version>4.3.4</version></dependency><dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-im</artifactId><version>4.3.4</version></dependency> <dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-extensions</artifactId><version>4.3.4</version></dependency><dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-java7</artifactId><version>4.3.4</version></dependency><dependency><groupId>org.igniterealtime.smack</groupId><artifactId>smack-debug</artifactId><version>4.3.4</version></dependency><dependency><groupId>commons-cli</groupId><artifactId>commons-cli</artifactId><version>1.4</version></dependency> </dependencies>

這些是撰寫本文時的最新版本,但是您可以使用在Maven Central中可能找到的最新版本。

4.2創建主類

該程序由基于[10]的兩個類組成。 XmppLoadTest包含main()方法,并委托XmppManager來完成工作(與現實相反,因為規范是經理委托而不是實際進行工作:))。

public static void main(String[] args) throws Exception {parseCLIArguments(args);final XmppLoadTest loadXmppTest = new XmppLoadTest();loadProperties(PROPERTIES_FILE);init(loadXmppTest);performLoad(loadXmppTest); }

我將跳過parseCLIArguments()方法的描述。 它使用Apache Commons CLI庫來解析命令行參數(請參見[4])。 您可以根據需要選擇其他任何CLI庫或創建GUI。

我們的測試模擬了50個用戶和5個聊天室(您可以模擬自己的方案來滿足您的需求)。 這些存儲在:

private static final List<User> users = new ArrayList< >(numberOfUsers); private static final List<ChatRoom> chatRooms = new ArrayList< >(numberOfRooms);

類User和ChatRoom的定義如下:

/*** User (e.g. {@code user001}). Functionality delegated to @{see* XmppManager}.*/ final class User {private final String username;private final String password;private final String domain;private final XmppManager xmppManager; // delegate to itprivate MultiUserChat joinedChatRoom;public User(String username, String password, String domain, XmppManager xmppManager) {this.username = username;this.password = password;this.domain = domain;this.xmppManager = xmppManager;}public String getUsername() { return username; }public String getPassword() { return password; }public String getJabberID() { return username + "@" + domain; }public void connect() {xmppManager.connect();}public void disconnect() {xmppManager.destroy();LOG.info("User " + username + " disconnected.");}public void login() {xmppManager.login(username, password);}public void setStatus(boolean available, String status) {xmppManager.setStatus(available, status);}public void sendMessage(String toJID, String message) {xmppManager.sendMessage(toJID, message);}public void receiveMessage() {xmppManager.receiveMessage();}public void sendAttachment(String toJID, String path) {xmppManager.sendAttachment(toJID, "Smack", path);}public void receiveAttachment() {xmppManager.receiveAttachment(username);}public void joinChatRoom(String roomName, String nickname) {joinedChatRoom = xmppManager.joinChatRoom(roomName, nickname);}public void leaveChatRoom() {try {joinedChatRoom.leave();} catch (SmackException.NotConnectedException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());}}public void sendMessageToChatRoom(String message) {xmppManager.sendMessageToChatRoom(joinedChatRoom, message);}public String getJoinedChatRoom() {return joinedChatRoom.getRoom().toString();}public void addRosterListener() {xmppManager.rosterChanged();}}/*** Chat room, e.g. {@code room001}*/final class ChatRoom {private final String name;private final String domain;public ChatRoom(String name, String domain) {this.name = name;this.domain = domain;}public String getName() {return name + "@conference." + domain;}}

ChatRoom類很簡單。 聊天室被標識為例如room001@conference.localhost ,其中conference是您在單擊Group Chat- > Group Chat Settings時在Openfire管理員控制臺中定義的子域,而localhost是我們通過命令行參數-d傳遞的域。 getName()返回的String是房間的裸JID ,我們將在后面看到。

User類更復雜。 它需要一個username ,一個password和一個domain并委托給XmppManager ,我們將很快看到。

XMPP客戶端的地址格式為user@server.com ,其中user是用戶名 , server.com是域 。 XMPP中的節點地址稱為Jabber ID,縮寫為JID 。 JID也可以具有資源 ( user@server.com/resource ),這意味著用戶可以從多個設備連接。 格式為user@server.com JID稱為裸JID ,而格式為user@server.com/resource的JID稱為完整JID 。

用戶可以setStatus() connect()到Openfire服務器,然后再login() ,然后用戶可以setStatus() , sendMessage()/receiveMesage(), sendAttachment()/receiveAttachment(), joinChatRoom()/leaveChatRoom()和sendMessageToChatRoom() 。

init()方法初始化XmppManager()并創建50個用戶,每個用戶連接,登錄并將其狀態設置為available 。 如果要測試文件傳輸,則每個用戶都開始收聽文件傳輸。 也創建了五個聊天室。 50個用戶中的每個用戶都分配到一個聊天室,因此最后,每個聊天室都包含10個用戶。

private static void init(XmppLoadTest loadXmppTest) {XmppManager xmppManager = new XmppManager(server, domain, port);for (int i = 1; i <= numberOfUsers; i++) {User user = loadXmppTest.new User("user" + String.format("%03d", i), password, domain, xmppManager);user.connect();user.login();user.setStatus(true, "Hello from " + user.getUsername());users.add(user);if (testAttachments || testBigAttachments) {user.receiveAttachment();}}for (int i = 0; i < numberOfRooms; i++) {chatRooms.add(loadXmppTest.new ChatRoom("room" + String.format("%03d", i + 1), domain));}if (!testAttachments && !testBigAttachments) {// join chatroomsfor (int i = 1; i <= numberOfUsers; i++) {ChatRoom chatRoom = chatRooms.get((i - 1) % numberOfRooms);User user = users.get(i - 1);user.joinChatRoom(chatRoom.getName(), user.getJabberID());}} }

一種方案是讓每個user連接到五個聊天室之一并發送消息。 任務被創建( chatRoomMessageTask )在performLoad()和每執行every取決于消息的類型秒( 長或短 )作為配置loadxmpptest.properties 。

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); ....} else { // send messages to chat roomsfinal Runnable task = () -> {while (true) {if (Thread.currentThread().isInterrupted()) {return;}loadXmppTest.chatRoomMessageTask();}};int every = testLongMessages ? longMessagesDelayInSeconds : shortMessagesDelayInSeconds;scheduler.scheduleWithFixedDelay(task, 0, every, SECONDS); // every x seconds }

另一種情況是將附件發送給另一個用戶,而不是將消息發送到聊天室:

if (testAttachments || testBigAttachments) { // send attachmentsString filePath = testBigAttachments ? bigFileNamePath : smallFileNamePath;int delay = testBigAttachments ? bigAttachmentsDelayInMinutes : smallAttachmentsDelayInMinutes;final Runnable task = () -> {while (true) {if (Thread.currentThread().isInterrupted()) {return;}loadXmppTest.fileTransferTask(filePath);}};scheduler.scheduleWithFixedDelay(task, 0, delay, MINUTES);

您當然可以將兩種情況結合起來,但是您需要確保不會溢出Openfire的緩存。

/** Each user sends a message to a chat room. */ private synchronized void chatRoomMessageTask() {for (int i = 1; i <= numberOfUsers; i++) {String message = testLongMessages ? LONG_MESSAGE : MESSAGE;User user = users.get(i - 1);try {Thread.currentThread().sleep(delayToSendMessagesInMillis); // sleep 1"user.sendMessageToChatRoom(message);LOG.info(user.getJabberID() + " sent " + (testLongMessages ? "long" : "short") + " message to " + user.getJoinedChatRoom());} catch (InterruptedException ie) {Thread.currentThread().interrupt(); // reset the flag}} }

在上述方法(稱為第一種情況)中,每個用戶向該用戶加入的聊天室發送一條消息(短消息或長消息)。

在fileTransferTask() ,每個用戶將附件發送給另一用戶(避免將附件發送給自己)。 請注意此方法和先前方法中的synchronized關鍵字,以避免代碼中出現死鎖。

/*** Exchange file attachments between users.** @param path path of the file to send* @see #transferFile(int, java.lang.String)*/ private void fileTransferTask(String path) {for (int i = 1; i <= numberOfUsers; i++) {transferFile(i, path);} } /*** Transfer the file to all other users.** @param i i-th user* @param path path of the file to be sent*/ private synchronized void transferFile(int i, String path) {int j;for (j = 1; j <= numberOfUsers; j++) {if (i != j) {try {int delay = testBigAttachments ? bigAttachmentsDelayInMinutes : smallAttachmentsDelayInMinutes;Thread.currentThread().sleep(delay); if (users.get(i - 1).sendAttachment(users.get(j - 1).getJabberID(), path)) {LOG.info("Attachment " + path + " sent from " + users.get(i - 1).getJabberID() + " to " + users.get(j - 1).getJabberID());} else {LOG.severe("Attachment " + path + " from " + users.get(i - 1).getJabberID() + " to " + users.get(j - 1).getJabberID() + " was not sent!");}} catch (InterruptedException ie) {Thread.currentThread().interrupt(); // reset the flag}}} }

這樣就完成了XmppLoadTest類的描述。

4.3 XmppManager類

XmppManager類使用smack庫[6,7]與Openfire服務器進行通信。 Smack是用于與XMPP服務器通信以執行實時通信(包括即時消息傳遞和群聊)的庫。

XmppManager與[10]中的類似,但是直到那時一切都在發展,API也發生了變化。 如前所述, User委托給XmppManager 。

4.3.1連接到Openfire

要連接到Openfire服務器,您需要托管Openfire 的服務器名稱 , 域和端口 (已固定: 5222 )。 XMPPTCPConnection類用于創建與XMPP服務器的連接。 可以使用XMPPTCPConnectionConfiguration.Builder配置其他連接參數:

private String resource = "Smack"; ... XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder(); try {builder.setXmppDomain(JidCreate.domainBareFrom(domain)).setHost(server).setPort(port).setResource(resource).setSecurityMode(SecurityMode.disabled).setHostnameVerifier((String hostname, SSLSession session) -> true); } catch (XmppStringprepException ex) {LOG.severe(ex.getLocalizedMessage()); } try {builder = TLSUtils.acceptAllCertificates(builder); } catch (KeyManagementException | NoSuchAlgorithmException ex) {LOG.log(Level.SEVERE, null, ex); } XMPPTCPConnection.setUseStreamManagementDefault(true); XMPPTCPConnectionConfiguration config = builder.build();

resource String對于文件傳輸很重要。 如果您使用的是smack,則可以是"Smack"或"Resource" 。 如果使用其他客戶端,例如Spark,則可以將其設置為"Spark ”。它可以確定要將文件發送到的資源。

//SASLMechanism mechanism = new SASLDigestMD5Mechanism(); SASLMechanism mechanism = new SASLPlainMechanism(); SASLAuthentication.registerSASLMechanism(mechanism); SASLAuthentication.unBlacklistSASLMechanism("PLAIN"); SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1"); SASLAuthentication.unBlacklistSASLMechanism("DIGEST-MD5"); try {builder = TLSUtils.acceptAllCertificates(builder); } catch (KeyManagementException | NoSuchAlgorithmException ex) {LOG.severe(ex.getLocalizedMessage()); } XMPPTCPConnection.setUseStreamManagementDefault(true); XMPPTCPConnectionConfiguration config = builder.build();

TLSUtils.acceptAllCertificates(builder); 由于安全模型在最新版本的Openfire中已更改,因此這一點非常重要。 因此,我們在Openfire的管理控制臺中添加了sasl.mechs.00001和sasl.mechs.00002 。 如果他仍然遇到連接/身份驗證問題,則此鏈接可能有幫助。

4.3.2登錄

配置與Openfire的連接后,就可以連接到它了:

private AbstractXMPPConnection connection; ... connection = new XMPPTCPConnection(config); connection.setReplyTimeout(1000L); try {connection.connect(); } catch (SmackException | IOException | XMPPException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage()); }

默認情況下,如果突然斷開連接,Smack將嘗試重新連接。 重新連接管理器將嘗試立即重新連接到服務器,并增加嘗試之間的延遲,因為連續的重新連接持續失敗。 創建連接后,用戶應使用其憑據使用XMPPConnection.login()方法登錄:

public void login(String username, String password) {if (connection != null && connection.isConnected()) {try {connection.login(username, password);} catch (XMPPException | SmackException | IOException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());}}LOG.info(username + " authenticated? " + connection.isAuthenticated()); }

4.3.3在場和名冊

用戶登錄后,可以通過創建新的Chat或MultiUserChat對象開始與其他用戶Chat 。 用戶還可以將其狀態設置為可用 :

public void setStatus(boolean available, String status) {Presence.Type type = available ? Type.available : Type.unavailable;Presence presence = new Presence(type);presence.setStatus(status);try {connection.sendStanza(presence);} catch (SmackException.NotConnectedException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());} }

從客戶端到XMPP服務器的每個消息稱為數據包或節,并以XML的形式發送。 節是客戶端可以在一個程序包中發送給服務器的最小XML數據段,反之亦然。 所述org.jivesoftware.smack.packet Java包中包含封裝由XMPP( 消息 , 存在和IQ)所允許的三種不同的基本分組類型的類。 XMPP服務器和客戶端對每個節的處理方式不同。 節具有類型屬性 ,這些屬性可用于進一步區分節[3]。

消息 節旨在用于在XMPP實體之間發送數據。 實在是忘了,也就是說,接收方不承認節。 通常,當您從客戶端發送消息節并且未生成任何類型的錯誤時,您可以假定消息已成功發送。 消息節的類型可以是“聊天”,“ groupchar”,“錯誤”等。

狀態節會通告其他實體的在線狀態(網絡可用性)。 在線狀態的工作方式類似于XMPP中的訂閱。 當您對某些JID的存在感興趣時,您就訂閱它們的存在,即,您告訴XMPP服務器“每次該JID向您發送狀態更新時,我都希望得到通知”。 當然,服務器會詢問JID持有者是否接受向您透露其在線信息。 當他們接受時,服務器會記住他們的決定,并在更改在線狀態時更新訂閱該狀態的任何人。 術語存在還表示用戶是否在線。

最后, IQ (信息/查詢)節用于從服務器獲取一些信息(例如,有關服務器或其注冊客戶端的信息)或將某些設置應用于服務器。

在XMPP中,術語名冊用于指代聯系人列表。 用戶的聯系人列表通常存儲在服務器上。 該名冊使您可以跟蹤其他用戶的可用性(狀態)。 可以將用戶分為“朋友”和“同事”之類的組,然后您會發現每個用戶是在線還是離線。 Roster類允許您查找所有名單條目,它們所屬的組以及每個條目的當前狀態。

名冊中的每個用戶都由RosterEntry表示,該成員包括:

  • XMPP地址(例如john@example.com )。
  • 您分配給用戶的名稱(例如"John" )。
  • 條目所屬的名冊中的組的列表。 如果名冊條目不屬于任何組,則稱為“未歸檔條目”。

在名單中的每個條目都有一個與之關聯的存在 。 Roster.getPresence(String user)方法將返回一個具有用戶狀態的Presence對象;如果用戶不在線或您未訂閱該用戶的狀態,則返回null 。 用戶要么在線要么離線 。 當用戶在線時,他們的存在可能包含擴展信息,例如他們當前正在做什么,是否希望受到打擾等。

public Roster createRosterFor(String user, String name) throws Exception {LOG.info(String.format("Creating roster for buddy '%1$s' with name %2$s", user, name));Roster roster = Roster.getInstanceFor(connection);roster.createEntry(JidCreate.bareFrom(user), name, null);return roster; } public void printRosters() throws Exception {Roster roster = Roster.getInstanceFor(connection);Collection entries = roster.getEntries();for (RosterEntry entry : entries) {LOG.info(String.format("Buddy: %s", entry.getName()));} } public void rosterChanged() {Roster roster = Roster.getInstanceFor(connection);roster.addRosterListener(new RosterListener() {@Overridepublic void presenceChanged(Presence presence) {LOG.info("Presence changed: " + presence.getFrom() + " " + presence);resource = presence.getFrom().getResourceOrEmpty().toString();}@Overridepublic void entriesAdded(Collection clctn) { }@Overridepublic void entriesUpdated(Collection clctn) { }@Overridepublic void entriesDeleted(Collection clctn) { }}); }

在場信息可能會經常更改,并且名冊條目也可能會更改或刪除。 要偵聽變化的花名冊和狀態數據,請使用RosterListener 。 為了通知有關名冊的所有更改,應在登錄XMPP服務器之前注冊RosterListener 。 文件傳輸知道,如果收件人的資源發生了變化,所描述的是很重要的位置 。

4.3.4聊天和多聊

您可以在ChatManager的幫助下發送和接收聊天消息。 盡管可以將單個消息作為數據包發送和接收,但是使用org.jivesoftware.smack.chat2.Chat類將消息字符串視為聊天通常會更容易。 聊天會在兩個用戶之間創建新的消息線程。 Chat.send(String)方法是一種便捷方法,它創建一個Message對象,使用String參數設置正文,然后發送消息。

/*** Send message to another user.** @param buddyJID recipient* @param message to send*/ public void sendMessage(String buddyJID, String message) {LOG.info(String.format("Sending message '%1$s' to user %2$s", message, buddyJID));try {Chat chat = ChatManager.getInstanceFor(connection).chatWith(JidCreate.entityBareFrom(buddyJID));chat.send(message);} catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());} } public void receiveMessage() {ChatManager.getInstanceFor(connection).addIncomingListener((EntityBareJid from, Message message, Chat chat) -> {LOG.info("New message from " + from + ": " + message.getBody());}); }

要加入聊天室( MultiUserChat )并向其中發送消息:

public MultiUserChat joinChatRoom(String roomName, String nick) {try {MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection);MultiUserChat muc = manager.getMultiUserChat(JidCreate.entityBareFrom(roomName));Resourcepart nickname = Resourcepart.from(nick);muc.join(nickname);LOG.info(muc.getNickname() + "joined chat room " + muc.getRoom());return muc;} catch (XmppStringprepException | SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | XMPPException.XMPPErrorException | MultiUserChatException.NotAMucServiceException ex) {LOG.severe(ex.getLocalizedMessage());}return null; }public void sendMessageToChatRoom(MultiUserChat muc, String message) {try {muc.sendMessage(message);LOG.fine("Message '" + message + "' was sent to room '" + muc.getRoom() + "' by '" + muc.getNickname() + "'");} catch (InterruptedException | SmackException.NotConnectedException ex) {LOG.severe(ex.getLocalizedMessage());} }

您可以在加入聊天室時定義昵稱。

4.3.5文件傳輸

要發送/接收附件,它比較復雜(請參閱此處 ):

/*** File transfer.** @param buddyJID recipient* @param res e.g. "Spark-2.8.3", default "Smack" (cannot be empty or null)* @param path path of the file attachment to send* @return {@code true} if file transfer was successful*/ public boolean sendAttachment(String buddyJID, String res, String path) {LOG.info(String.format("Sending attachment '%1$s' to user %2$s", path, buddyJID));FileTransferManager fileTransferManager = FileTransferManager.getInstanceFor(connection);FileTransferNegotiator.IBB_ONLY = true;OutgoingFileTransfer fileTransfer = null;try {fileTransfer = fileTransferManager.createOutgoingFileTransfer(JidCreate.entityFullFrom(buddyJID + "/Spark-2.8.3"));} catch (XmppStringprepException ex) {LOG.log(Level.SEVERE, null, ex);return false;}if (fileTransfer != null) {OutgoingFileTransfer.setResponseTimeout(15 * 60 * 1000);LOG.info("status is:" + fileTransfer.getStatus());File file = Paths.get(path).toFile();if (file.exists()) {try {fileTransfer.sendFile(file, "sending attachment...");} catch (SmackException ex) {LOG.severe(ex.getLocalizedMessage());return false;}LOG.info("status is:" + fileTransfer.getStatus());if (hasError(fileTransfer)) {LOG.severe(getErrorMessage(fileTransfer));return false;} else {return monitorFileTransfer(fileTransfer, buddyJID);}} else try {throw new FileNotFoundException("File " + path + " not found!");} catch (FileNotFoundException ex) {LOG.severe(ex.getLocalizedMessage());return false;}}}return true; }/*** Monitor file transfer.** @param fileTransfer* @param buddyJID* @return {@code false} if file transfer failed.*/ private boolean monitorFileTransfer(FileTransfer fileTransfer, String buddyJID) {while (!fileTransfer.isDone()) {if (isRejected(fileTransfer) || isCancelled(fileTransfer)|| negotiationFailed(fileTransfer) || hasError(fileTransfer)) {LOG.severe("Could not send/receive the file to/from " + buddyJID + "." + fileTransfer.getError());LOG.severe(getErrorMessage(fileTransfer));return false;} else if (inProgress(fileTransfer)) {LOG.info("File transfer status: " + fileTransfer.getStatus() + ", progress: " + fileTransfer.getProgress());}try {Thread.sleep(1000);} catch (InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());}}if (isComplete(fileTransfer)) {LOG.info(fileTransfer.getFileName() + " has been successfully transferred.");LOG.info("The file transfer is " + (fileTransfer.isDone() ? "done." : "not done."));return true;}return true; }public void receiveAttachment(String username) {final FileTransferManager manager = FileTransferManager.getInstanceFor(connection);manager.addFileTransferListener((FileTransferRequest request) -> {// Check to see if the request should be acceptedif (request.getFileName() != null) {StringBuilder sb = new StringBuilder(BUFFER_SIZE);try {// Accept itIncomingFileTransfer transfer = request.accept();String filename = transfer.getFileName() + "_" + username;transfer.receiveFile(new File(filename));while (!transfer.isDone()) {try {Thread.sleep(1000);LOG.info("STATUS: " + transfer.getStatus()+ " SIZE: " + sb.toString().length()+ " Stream ID : " + transfer.getStreamID());} catch (Exception e) {LOG.severe(e.getMessage());}if (transfer.getStatus().equals(FileTransfer.Status.error)) {LOG.severe(transfer.getStatus().name());}if (transfer.getException() != null) {LOG.severe(transfer.getException().getLocalizedMessage());}}LOG.info("File received " + request.getFileName());} catch (SmackException | IOException ex) {LOG.severe(ex.getLocalizedMessage());}} else {try {// Reject itrequest.reject();LOG.warning("File rejected " + request.getFileName());} catch (SmackException.NotConnectedException | InterruptedException ex) {LOG.severe(ex.getLocalizedMessage());}}}); }

Openfire中定義了3種類型的文件傳輸 :

  • 帶內 ( FileTransferNegotiator.IBB_ONLY ),其中消息被分解為多個塊并作為編碼消息發送。 它速度較慢,但??始終有效。 此外,由于交換的消息存儲在Openfire數據庫中,因此備份起來更容易。
  • 當兩個用戶都在同一網絡上時, 對等 (p2p)效果很好,但是當一個用戶位于防火墻后或使用NAT時,對等網絡將失敗。 它速度更快,除了上述問題之外,您無法控制要交換的內容。
  • 代理服務器 (SOCKS5,請參閱XEP-0096或更新的XEP-0234 )使用文件傳輸代理,但需要打開端口7777。它比p2p慢,但比帶內快。

在我們的測試工具中,正在使用帶內文件傳輸。

一旦成功發送文件,就需要監視其狀態( monitorFileTransfer() )。 可能存在網絡錯誤,或者收件人可能只是拒絕文件傳輸。 實際上,其他用戶可以選擇接受,拒絕或忽略文件傳輸請求。

發送附件是OutgoingFileTransfer ,而接收是IncomingFileTransfer 。 這是通過向FileTransferManager添加偵聽器來實現的。 如前所述,接收者需要在發送者發送文件之前開始偵聽。 此外,在我們的負載測試中,正在發送和接收相同的文件。 為了避免覆蓋相同的文件,源文件以不同的名稱存儲,在文件名中添加"_"和收件人的名稱。 當然,這些文件名在負載測試工具運行時會一次又一次地寫入。

建立

為了能夠執行負載測試工具,您需要創建一個可執行文件XmppLoadTest-1.0.jar 。 一種方法是將以下內容添加到pom.xml中:

<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><configuration><archive><manifest><addClasspath>true</addClasspath><mainClass>test.xmpp.xmpploadtest.XmppLoadTest</mainClass></manifest></archive></configuration></plugin></plugins> </build>

并且還需要將依賴項包括到classpath中 。 或者,您可以使用依賴項插件來創建一個單個jar ,該jar會創建此處所述的所有內容。

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.2.0</version><configuration><archive><manifest><mainClass>test.xmpp.xmpploadtest.XmppLoadTest</mainClass></manifest></archive><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><executions><execution><id>make-assembly</id> <!-- this is used for inheritance merges --><phase>package</phase> <!-- bind to the packaging phase --><goals><goal>single</goal></goals></execution></executions> </plugin>

您也可以使用maven命令代替執行它:

mvn exec:java -Dexec.mainClass=test.xmpp.xmpploadtest.XmppLoadTest "-Dexec.args=-s localhost -d localhost -p a -n 50"

4.5負載測試

一旦執行了負載測試工具,您將看到許多發送到Openfire服務器的消息。 根據您選擇的場景(群聊或文件傳輸),如果您使用第50個用戶(例如Spark)之類的聊天客戶端進行連接并加入聊天室,您將看到他們被重復發送的相同消息所填充其他49個模擬用戶。

Apr 25, 2020 11:55:16 PM test.xmpp.xmpploadtest.XmppManager connect INFO: Initializing connection to server localhost port 5222 Apr 25, 2020 11:55:18 PM test.xmpp.xmpploadtest.XmppManager connect INFO: Connected: true Apr 25, 2020 11:55:18 PM test.xmpp.xmpploadtest.XmppManager login INFO: user001 authenticated? True ... Apr 25, 2020 11:55:21 PM test.xmpp.xmpploadtest.XmppManager joinChatRoom INFO: user001@localhost joined chat room room001@conference.localhost Apr 25, 2020 11:55:21 PM test.xmpp.xmpploadtest.XmppManager joinChatRoom INFO: user002@localhost joined chat room room002@conference.localhost ... Apr 25, 2020 11:55:24 PM test.xmpp.xmpploadtest.XmppLoadTest chatRoomMessageTask INFO: user001@localhost sent short message to room001@conference.localhost Apr 25, 2020 11:55:25 PM test.xmpp.xmpploadtest.XmppLoadTest chatRoomMessageTask INFO: user002@localhost sent short message to room002@conference.localhost ...

當您在user050情況下運行該工具時,您沒有在Spark或以user050連接的聊天客戶端中看到任何附件。

INFO: Sending attachment 'test.txt' to user user003@localhost [Sun May 10 17:55:15 CEST 2020] INFO: status is:Initial [Sun May 10 17:55:15 CEST 2020] INFO: status is:Initial [Sun May 10 17:55:15 CEST 2020] INFO: STATUS: Complete SIZE: 0 Stream ID : jsi_2604404248040129956 [Sun May 10 17:55:15 CEST 2020] INFO: File received test.txt [Sun May 10 17:55:15 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_4005559316676416776 [Sun May 10 17:55:16 CEST 2020] WARNING: Closing input stream [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_6098909703710301467 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_2348439600749627884 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_8708250841661514027 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_2119745768373873364 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_6583436044582265363 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_3738252107587424431 [Sun May 10 17:55:16 CEST 2020] INFO: STATUS: In Progress SIZE: 0 Stream ID : jsi_4941117510857455094 [Sun May 10 17:55:16 CEST 2020] INFO: test.txt has been successfully transferred. [Sun May 10 17:55:16 CEST 2020] INFO: The file transfer is done. [Sun May 10 17:55:16 CEST 2020]

一旦運行了加載/壓力工具,就可以搜索XMPP服務器或客戶端的內存泄漏或CPU高使用率。 您可以使用VisualVM之類的工具來監視內存和CPU,或者甚至可以根據需要使用YourKit或Java Flight Recorder之類的工具進行概要分析。

5.總結

在本教程中,我們學習了如何編寫自己的負載測試工具來對XMPP服務器(如Openfire)進行負載/壓力測試。 負載/壓力工具還可以用于測試XMPP客戶端(例如Spark)。 如果您編寫了自己的XMPP客戶端或服務器,那么它也可以用于測試它們。 該工具使用Smack XMPP庫以Java編寫。 它可以在兩種模式或場景下運行,既可以將消息發送到聊天室,也可以在用戶之間發送文件傳輸。 XMPP服務器需要使用模擬用戶和聊天室進行預配置。

您可以根據需要自定義進一步擴展源代碼,例如用戶數量,消息或文件附件的大小,消息之間的延遲,發送消息和文件傳輸的組合或測試XMPP的其他方面協議。

6.參考

  • Aladev R.(2017a),“ XMPP負載測試–最終指南 ”。
  • Aladev R.(2017b),“ XMPP負載測試–高級方案 ”。
  • Gakwaya D.(2016),“ XMPP的友好介紹 ”。
  • Marx D.(2017),“ Java命令行界面(第1部分):Apache Commons CLI ”,JavaCodeGeeks。
  • Saint-Andre P.,Smith K.,Troncon R.(2009年), XMPP:權威指南 ,O'Reilly。
  • Smack API
  • 打擊文件
  • Tsagklis I.(2010a),“ Openfire服務器安裝-即時消息基礎結構 ”,JavaCodeGeeks。
  • Tsagklis I.(2010b),“ Openfire服務器配置-即時消息基礎結構 ”,JavaCodeGeeks。
  • Tsagklis I.(2010c),“ 帶有適用于Java應用程序的Smack的XMPP IM-即時消息基礎結構 ”,JavaCodeGeeks。
  • 7.下載Maven項目

    那是一篇有關Java XMPP負載測試工具的文章。

    下載
    您可以在此處下載完整的源代碼: Java XMPP負載測試工具

    翻譯自: https://www.javacodegeeks.com/java-xmpp-load-test-tool.html

    總結

    以上是生活随笔為你收集整理的Java XMPP负载测试工具的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 国产av一区二区三区精品 | 亚洲乱码视频在线观看 | 精品精品精品 | 天堂俺去俺来也www久久婷婷 | 在厨房拨开内裤进入毛片 | jizz在线看| 亚洲美女久久 | 中文字幕美女 | 激情六月天 | 午夜精品区| 亚洲午夜免费视频 | 老司机久久 | 美女穴穴| 在线观看无码精品 | 国产www性| 久久天天操 | china国产乱xxxxx绿帽 | 国产情侣自拍小视频 | 污片在线免费观看 | 大尺度做爰啪啪床戏 | 日韩国产欧美精品 | 性久久久| 中文字幕欧美亚洲 | 成人综合网站 | 在线观看日韩 | 狠狠操狠狠插 | 久久久久久久久综合 | 中文字幕一区2区3区 | 99免费在线观看视频 | 综合视频在线观看 | 999精品在线观看 | 亚洲精品在线免费播放 | 国产精品九九 | 91成人短视频 | 丰满的人妻hd高清日本 | 亚洲成人国产精品 | 韩国一区二区在线观看 | 天天综合人人 | 中文字幕日韩三级 | 欧美激情aaa | 91啪国产 | 久久中文字幕国产 | 中文字幕二 | 成人靠逼视频 | 18禁裸乳无遮挡啪啪无码免费 | 国产二三区| 亚日韩欧美 | 无码人妻一区二区三区在线视频 | 日韩性高潮 | 免费网站在线观看视频 | 亚洲先锋影音 | 日本少妇做爰全过程毛片 | 欧美女优一区二区 | 午夜91视频| 欧美在线www | 不卡一二三 | 免费中文视频 | 成人性生交大片免费看 | 亚洲精品亚洲人成人网 | 欧美亚洲激情 | 久精品在线观看 | 九九人人 | 国产高清在线观看 | 色婷婷九月 | 黄色片播放器 | 五月婷婷深爱 | 久久噜噜噜精品国产亚洲综合 | 久久国产网站 | 亚洲视频免费观看 | 婷婷av在线 | 亚洲狼人干 | caobi视频| 国产视频久久 | 中文字幕久久精品 | 美女张开腿让男人操 | 丝袜美腿一区二区三区 | 激情视频久久 | 天堂国产 | 俺啪也 | 香蕉国产在线视频 | 欧美精品99久久 | 国产精品成人久久久久 | 成人 黄 色 免费播放 | 涩涩屋污 | 假日游船法国满天星 | 樱花视频在线观看 | 老牛影视av老牛影视av | 国产一级片免费观看 | 男女黄床上色视频免费的软件 | 国产一区二区av在线 | 夜夜嗨av色一区二区不卡 | 亚洲激情自拍偷拍 | 国产精品二区三区 | 在线免费看av | 嫩模被强到高潮呻吟不断 | 怡红院一区二区三区 | 亚洲精品一区二区三区四区 | 久久久久久九九九九 | 亚洲精品福利视频 |