1:關於smack與tigase的用法跟做用請你們本身去網上查看相關資料,這裏就不作描述了。java
PS:這篇文章主要是說明在客戶端jvm建立的最大線程數的大小。多線程
以前公司要求作一個客戶端用於測試剛剛部署的tigase的性能,因此項目經理就安排了一個事情就是本身動手在客戶端寫一個基於smack長鏈接的壓力測試工具。併發
初期的要求是這樣的:jvm
一、併發註冊10000個用戶ide
二、用戶之間相互收發消息工具
三、能夠調整併發數量性能
用smack 作客戶端鏈接測試
因爲以前對多線程這塊懂的不是很深,在這個過程當中碰了走了不少彎路(總覺得是代碼的問題,實際上不是),個人機子的配置是:32位,xp,4G內存。本身寫了個程序不管怎麼跑在線數都不能突破2000,離要求還差一大截呢!本身反反覆覆檢查程序,沒啥不對啊!後來使用jconsole工具觀察發現線程數6k一直是最高峯,難怪用戶量上不去,要知道要保證10000個用戶同時在線至少客戶端要啓動10000個線程吧!問題總算找到了,因而百度了一下,發現了一個jvm啓動線程數的公式:this
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory 指的是一個進程的最大內存
JVMMemory JVM內存
ReservedOsMemory 保留的操做系統內存
ThreadStackSize 線程棧的大小spa
原來:在java語言裏, 當你建立一個線程的時候,虛擬機會在JVM內存建立一個Thread對象同時建立一個操做系統線程,而這個系統線程的內存用的不是JVMMemory,而是系統中剩下的內存(MaxProcessMemory - JVMMemory - ReservedOsMemory)
OK問題找到了有了解決問題的思路:根據本身的測試發現當給jvm設置的內存數爲512m時建立的線程數是最多的,以下:
2*1024*1024-1024*64-512*1024)/128=11776
又經過觀察發現要保持一個長鏈接本地至少了啓動四個線程因而支持的在線用戶數爲:11776/4
OK:測試結果想以下:
看圖說話,哈哈 問題解決:
代碼以下:
public class SmackConf {
public static String server="XXXX";
public static int port=5222;
private SmackConf(){};
//初始化配置
private static class ConnectionConfig{
private static ConnectionConfiguration config;
static{
config = new ConnectionConfiguration(server, 5222, "XXX");
config.setSASLAuthenticationEnabled(false);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
//config.setSendPresence(false);
System.out.println("加載配置文件類完畢");
}
}
public static ConnectionConfiguration getConnectionConfiguration(){
return ConnectionConfig.config;
}
}
public class SmackUtils {
private static Logger log = Logger.getLogger(SmackUtils.class);
private static int sleepTime = 1000;
public static void chart(User user) {
Connection connection = MapsUtils.getConnection(user.getUsername());
try {
if(connection==null||!connection.isConnected()){
initConnection(user);
}
if (!connection.isAuthenticated()) {
initLogin(user);
}
synchronized (connection) {
PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
connection.addPacketListener(new PacketListener() {
@Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) {
String fromName = (String) message
.getProperty("fromId");
System.out.println("Got text [" + message.getBody()
+ "] from [" + fromName + "]");
}
}
}, filter);
}
} catch (Exception e) {
System.out.println("chart===="+e.getMessage());
log.info("chart===="+e.getMessage());
}
}
/*
* 得到鏈接
*/
private static void getConnection(User user, int i) throws Exception {
Connection connection = null;
try {
log.info("第" + i + "次開始申請鏈接,用戶:" + user.getUsername());
connection = MapsUtils.getConnection(user.getUsername());
if (connection == null) {
connection = new XMPPConnection(SmackConf.getConnectionConfiguration());
MapsUtils.put(user.getUsername(), connection);
}
if(connection.isConnected()){
connection.disconnect();
}
connection.connect();
user.setConnectionFa(true);
log.info("第" + i + "次申請鏈接,用戶:" + user.getUsername() + "成功");
} catch (Exception e) {
log.info("第" + i + "次申請鏈接,用戶:" + user.getUsername() + "失敗");
System.out.println("得到鏈接的err:"+e.getMessage());
log.info("得到鏈接的err:"+e.getMessage());
throw e;
}
}
public static void getConnection(User user) {
/** 創建鏈接 */
try {
getConnection(user, 0);
} catch (Exception e) {
while (user.getiConnent() < MapsUtils.count
&& !user.isConnectionFa()) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e1) {
}
user.setiConnent(user.getiConnent() + 1);
try {
getConnection(user, user.getiConnent());
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
private static void initConnection(User user) throws Exception {
try {
log.info("開始初始化建立鏈接:" + user.getUsername());
user.setConnectionFa(false);
user.setiConnent(0);
getConnection(user);
} catch (Exception e) {
throw e;
}
}
/**
* 註冊新用戶
*
* @param i
* @throws Exception
*/
private static void createAccount(User user, int i) throws Exception {
Connection connection = MapsUtils.getConnection(user.getUsername());
try {
if (connection == null || !connection.isConnected()) {
initConnection(user);
}
synchronized (connection) {
AccountManager am = null;
log.info("第" + i + "次開始註冊用戶:" + user.getUsername());
am = connection.getAccountManager();
am.createAccount(user.getUsername(), user.getPassword());
user.setCreateFa(true);
log.info("第" + i + "次開始註冊用戶:" + user.getUsername() + "成功");
}
} catch (Exception e) {
log.info("第" + i + "次開始註冊用戶:" + user.getUsername() + "失敗");
String message=e.getMessage();
log.info("註冊用戶的err:"+e.getMessage());
reInit(message, user);
throw e;
}
}
public static void createAccount(User user) {
try {
createAccount(user, 0);
} catch (Exception e) {
while (user.getiCreate() < MapsUtils.count && !user.isCreateFa()) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e2) {
}
user.setiCreate(user.getiCreate() + 1);
try {
createAccount(user, user.getiCreate());
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
// 登錄
public static void login(User user) {
try {
login(user, 0);
} catch (Exception e) {
while (user.getiLogin() < MapsUtils.count && !user.isLoginFa()) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e1) {
}
user.setiLogin(user.getiLogin() + 1);
try {
login(user, user.getiLogin());
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
private static void initSignUser(User user) throws Exception {
try {
log.info("開始初始化註冊:" + user.getUsername());
user.setiCreate(0);
user.setCreateFa(false);
createAccount(user);
} catch (Exception e) {
throw e;
}
}
// 登錄
private static void login(User user, int i) throws Exception {
Connection connection = MapsUtils.getConnection(user.getUsername());
try {
if(connection==null){
initConnection(user);
}
if(!connection.isConnected()){
initConnection(user);
}
if (!user.isCreateFa()) {
initSignUser(user);
}
if(connection.isAuthenticated()){
return;
}
synchronized (connection) {
log.info("第" + i + "次開始登錄,用戶:" + user.getUsername());
connection.login(user.getUsername(), user.getPassword());
user.setLoginFa(true);
/** 設置狀態 */
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Q我吧");
connection.sendPacket(presence);
log.info("第" + i + "次開始登錄,用戶:" + user.getUsername() + "成功");
}
} catch (Exception e) {
log.info("第" + i + "次開始登錄,用戶:" + user.getUsername() + "失敗");
String message = e.getMessage();
log.error("登錄異常信息:" + message);
System.out.println("用戶登錄的err:"+e.getMessage());
log.info("用戶登錄的err:"+e.getMessage());
reInit(message, user);
throw e;
}
}
private static void initLogin(User user) throws Exception {
try {
log.info("開始初始化登錄:" + user.getUsername());
user.setiLogin(0);
user.setLoginFa(false);
login(user);
} catch (Exception e) {
throw e;
}
}
public static void sendMessage(String from, String to, String message) {
/** 獲取當前登錄用戶的聊天管理器 */
int messageIndex = 0;
try {
sendMessage(from, to, message, messageIndex);
} catch (Exception e) {
e.printStackTrace();
while (messageIndex < MapsUtils.count) {
System.err.println("消息發送次數:" + messageIndex + "消息來源:" + from
+ "\t消息目的地:" + to + "\t消息內容:" + message);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
messageIndex++;
try {
sendMessage(from, to, message, messageIndex);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}
private static void sendMessage(String from, String to, String message,
int count) throws Exception {
/** 獲取當前登錄用戶的聊天管理器 */
Connection connection = MapsUtils.getConnection(from);
try {
if (!connection.isAuthenticated()) {
log.info(from + "==沒有登錄");
return;
}
synchronized (connection) {
/** 發送消息 */
ChatManager chatManager = connection.getChatManager();
Chat chat = chatManager.createChat(to + "@tt.com", null);
chat.sendMessage("消息來源:" + from + "\t消息目的地:" + to + "\t消息內容:"
+ message);
System.err.println("消息來源:" + from + "\t消息目的地:" + to + "\t消息內容:"
+ message);
}
} catch (Exception e) {
throw e;
}
}
private static void reInit(String message,User user) throws Exception{
if(message==null){
throw new IllegalArgumentException("message參數不能爲空");
}
try {
if(message.indexOf("Not connected")>-1){
//沒有鏈接
initConnection(user);
}else if(message.indexOf("not-authorized(401)")>-1){
//沒有註冊
initSignUser(user);
}else if(message.indexOf("conflict(409)")>-1){
//重複註冊
user.setCreateFa(true);
}else if(message.indexOf("No response")>-1){
//沒有返回
initConnection(user);
}
} catch (Exception e) {
throw e;
}
}
}
private ExecutorService executorService=null;
public ThreadSysUilt(ExecutorService executorService) {
super();
this.executorService = executorService;
}
/**
* 執行線程任務
* @param rs
* @param fa true(表明所有線程任務須要執行完才能返回),false(與true相反)
* @return
*/
public void execute(List<Runnable> rs){
for(Runnable r:rs){
executorService.execute(r);
}
executorService.shutdown();
}
/**
* 線程執行任務
* @param cs
* @return
*/
public <V> List<Future<V>> submit(List<Callable<V>> cs){
List<Future<V>> fLists=null;
try {
fLists = executorService.invokeAll(cs);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
executorService.shutdown();
return fLists;
}
public class ConnThreadMore implements Callable<MoreUser> {
private Logger log = Logger.getLogger(ConnThreadSpe.class.getName());
private MoreUser moreUser = null;
public ConnThreadMore(MoreUser moreUser) {
super();
this.moreUser = moreUser;
}
@Override
public MoreUser call() {
// TODO Auto-generated method stub
List<User> users=moreUser.getUsers();
for(User user:users){
log.info("開始新建線程:"+Thread.currentThread().getName()+"==執行:"+user.getUsername()+"==建立鏈接");
try {
SmackUtils.getConnection(user);
Thread.sleep(1000);
} catch (Exception e) {
log.error("申請鏈接異常", e);
}
try {
SmackUtils.createAccount(user);
Thread.sleep(1000);
} catch (Exception e) {
log.error("申請鏈接異常", e);
}
}
return moreUser;
}
}
public class LoginThreadMore implements Runnable {
private Logger log = Logger.getLogger(LoginThreadSpe.class.getName());
private MoreUser moreUser = null;
public LoginThreadMore(MoreUser moreUser) {
super();
this.moreUser = moreUser;
}
@Override
public void run() {
// TODO Auto-generated method stub
List<User> users=moreUser.getUsers();
for(User user:users){
log.info("開始新建線程:"+Thread.currentThread().getName()+"==執行:"+user.getUsername()+"==註冊並登陸");
try {
SmackUtils.login(user);//登陸
Thread.sleep(100);
} catch (InterruptedException e) {
log.info("登錄異常", e);
e.printStackTrace();
}
try {
SmackUtils.chart(user);//啓動消息
Thread.sleep(100);
} catch (InterruptedException e) {
log.info("啓動消息", e);
e.printStackTrace();
}
}
}
}
總結:這個實現上是比較簡單的,關鍵的問題是培養本身解決問題的能力,有不對之處但願你們多多指正。
本文出自 「陳硯羲」 博客,轉載請與做者聯繫!