MQTT併發測試(Linux嵌入式環境)

發佈者代碼:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "MQTTClient.h"

#define ADDRESS     "tcp://localhost:1883"
#define CLIENTID    "ExampleClientPub"
#define TOPIC       "MQTT Examples"
#define PAYLOAD     "Hello World!"
#define QOS         1
#define TIMEOUT     10000L


int GetSec(void)
{
	struct timeval tv;

	gettimeofday(&tv,NULL);

	//printf("GetSec: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);

	//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
	//	tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);	

	return tv.tv_sec;

}

int GetMillSec(void)
{
	struct timeval tv;

	gettimeofday(&tv,NULL);

	//printf("GetMillSec: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);

	//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
	//	tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);	

	return tv.tv_sec*1000 + tv.tv_usec/1000;
}

int GetMirc(void)
{
	struct timeval tv;

	gettimeofday(&tv,NULL);

	//printf("GetMirc: sec=%d, usec=%d\n", tv.tv_sec, tv.tv_usec);

	//printf("start sec=%ld, mill_sec=%ld, micr_sec=%ld\n", \
	//	tvBef.tv_sec, tvBef.tv_sec*1000 + tvBef.tv_usec/1000, tvBef.tv_sec*1000000 + tvBef.tv_usec);	

	return tv.tv_sec*1000000 + tv.tv_usec;

}

typedef struct{
	int iThrdId; //線程ID
	int iTestCnt; //測試次數
}ThrdPara;

int main_thread(void* argv)
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc, iCnt;
	int iBf, iAf;
	char szClientId[32];
	char payload[128];
	ThrdPara *pThrdPara;


	pThrdPara = (ThrdPara*)argv;
	printf("main_thread: pThrdPara->iThrdId=%d, pThrdPara->iTestCnt=%d.\n", pThrdPara->iThrdId, pThrdPara->iTestCnt);

	//用戶ID,每個發佈消息的客戶端都不一樣!
	memset(szClientId, 0, sizeof(szClientId));
	sprintf(szClientId, "%c", pThrdPara->iThrdId);

	//待發布的負載數據,通過線程號來區分
	memset(payload, 0, sizeof(payload));
	sprintf(payload, "hello world! thrd-%d", pThrdPara->iThrdId);
	printf("payload: %s\n", payload);

	//創建MQTT客戶端,綁定服務器地址、客戶端ID
//    MQTTClient_create(&client, ADDRESS, CLIENTID,
	  MQTTClient_create(&client, ADDRESS, szClientId,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

	//connect服務端
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
	iCnt = 0;
	printf("--------------------pThrdPara->iThrdId=%d, iCnt=%d----------------------\n", pThrdPara->iThrdId, pThrdPara->iTestCnt);
	iBf = GetMirc();
	while (iCnt++ < pThrdPara->iTestCnt)
	{
		//發佈消息
	    //pubmsg.payload = PAYLOAD;
	    //pubmsg.payloadlen = strlen(PAYLOAD);
	    pubmsg.payload = payload;
		pubmsg.payloadlen = strlen(payload);
	    pubmsg.qos = QOS;
	    pubmsg.retained = 0;
	    MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
	    //printf("Waiting for up to %d seconds for publication of %s\n"
	    //        "on topic %s for client with ClientID: %s\n",
	    //        (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID);

		//等待消息發佈完成
	    rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
	    //printf("Message with delivery token %d delivered\n", token);
		//printf("--------------------iCnt=%d----------------------\n", iCnt);
	}
	//計算消息發佈所有的時間,單位微妙
	iAf = GetMirc();
	printf("thrd-%d, Interv time MircSec=%d.\n", pThrdPara->iThrdId, iAf-iBf);

    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}

//描述:主函數
//參數:@argc argv參數個數
//		@argv:
//				argv[1] 線程個數
//				argv[2] 單個線程通信的次數
//Example: ./MQTTClient_publish 3 100 
//			其中3表示運行3個線程,100表示每個線程發佈100次消息
int main(int argc, char *argv[])
{
	ThrdPara *pThrdPara;
	pthread_t *pthrd;
	int iThrdNum;
	int iTestNum;
	int iThrdIdx;
	char szTestNumStr[16];
	char ch;

	if (argc != 3)
	{

		printf("main: Failed to argc, argc=%d\n", argc);
		exit(0);
	}

	//通過主函數的參數控制創建線程的個數、每個線程下的消息發佈條數
	iThrdNum = atoi(argv[1]);
	iTestNum = atoi(argv[2]);
	printf("main: iThrdNum=%d, iTestNum=%d\n", iThrdNum, iTestNum);
	pthrd = malloc(sizeof(pthread_t)*iThrdNum);
	pThrdPara = malloc(sizeof(ThrdPara)*iThrdNum);
	for (iThrdIdx=0; iThrdIdx<iThrdNum; iThrdIdx++)
	{
		pThrdPara[iThrdIdx].iThrdId = iThrdIdx;
		pThrdPara[iThrdIdx].iTestCnt = iTestNum;
		pthread_create(&pthrd[iThrdIdx], NULL, &main_thread, (void *)&pThrdPara[iThrdIdx]);
//		sleep(5);
	}

	do  
	{ 
		ch = getchar(); 
	} while(ch!='Q' && ch != 'q'); 


	return NULL;
}

訂閱者代碼:

#include "stdio.h" 
#include "stdlib.h" 
#include "string.h" 
#include "MQTTClient.h" 

 
#define ADDRESS     "tcp://localhost:1883" 
#define CLIENTID    "ExampleClientSub" 
#define TOPIC       "MQTT Examples" 
#define PAYLOAD     "Hello World!" 
#define QOS         1 
#define TIMEOUT     10000L 

 
volatile MQTTClient_deliveryToken deliveredtoken; 

 
void delivered(void *context, MQTTClient_deliveryToken dt) 
{ 
    printf("Message with token value %d delivery confirmed\n", dt); 
    deliveredtoken = dt; 
} 
 
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) 
{ 
	int i; 
	char* payloadptr; 
 
	printf("Message arrived\n"); 
	printf("     topic: %s\n", topicName); 
	printf("   message: ");  

	//按照我們的需求,需要在payload負載數據中攜帶對應數據的屬性
	payloadptr = message->payload; 
	for(i=0; i<message->payloadlen; i++) 
	{ 
		putchar(*payloadptr++); 
	} 
	putchar('\n'); 
	MQTTClient_freeMessage(&message); 
	MQTTClient_free(topicName); 
	return 1; 
} 
 
void connlost(void *context, char *cause) 
{ 
	printf("\nConnection lost\n"); 
	printf("     cause: %s\n", cause); 
} 

int main(int argc, char* argv[]) 
{ 
	MQTTClient client; 
	MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; 
	int rc; 
	int ch; 

	//創建MQTT訂閱客戶端,綁定服務端地址、以及當前訂閱者的用戶ID(每個客戶端的用戶ID務必保證唯一性)
	MQTTClient_create(&client, ADDRESS, CLIENTID, 
	MQTTCLIENT_PERSISTENCE_NONE, NULL); 
	conn_opts.keepAliveInterval = 20; 
	conn_opts.cleansession = 1; 

	//綁定MQTT訂閱者客戶端的消息到達、消息發送的回調函數 
	MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered); 
	 
	//MQTT訂閱者客戶端連接服務端 
	if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) 
	{ 
		printf("Failed to connect, return code %d\n", rc); 
		exit(EXIT_FAILURE); 
	} 

	//提交訂閱者給服務端
	printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n" 
		"Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS); 
	MQTTClient_subscribe(client, TOPIC, QOS); 

	 
	do  
	{ 
		ch = getchar(); 
	} while(ch!='Q' && ch != 'q'); 
	 
	 
	MQTTClient_disconnect(client, 10000); 
	MQTTClient_destroy(&client); 
	return rc; 
} 

makfile:

arm-linux-gnueabi-gcc MQTTClient_publish.c -o MQTTClient_publish -L./linux_mqtt_c -lpaho-mqtt3c -lpthread

arm-linux-gnueabi-gcc MQTTClient_subscribe.c -o MQTTClient_subscribe -L./linux_mqtt_c -lpaho-mqtt3c -lpthread

 

方案驗證結果:由於發佈者和訂閱者的消息是同步的,以下消耗時間均指發佈者和訂閱者的時間;

驗證方案1

進程

發送次數

發送消耗時間(us)

發佈者進程1

100

10998389

發佈者進程1

200

21997781

發佈者進程1

500

55018353

結論:同一個進程發送100、200、500次所耗時間,平均每次發送時間爲100ms;

驗證方案2

進程

發送次數

發送消耗時間(us)

發佈者進程1

100

10997878

發佈者進程2

100

10999326

發佈者進程1

200

22017567

發佈者進程2

200

21997405

發佈者進程1

500

54998293

發佈者進程2

500

55007166

結論:不同進程同時發送100、200、500次所耗時間,平均每次發送時間爲100ms;

總結:綜合方案1和方案2,多個進程與單個進程的負載通信所耗時間相同,所以單個進程或多個進程對MQTT服務器的負載沒有影響,但每次發佈一次負載消息所耗時間爲100ms,系統庫無法用這種通信機制滿足;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章