記錄用戶行為的意義?
很多互聯(lián)網(wǎng)產(chǎn)品都會有數(shù)據(jù)分析的后臺,比如,本公眾號的一些數(shù)據(jù)分析:
通過后臺的一些數(shù)據(jù)分析,我可以知道本公眾號讀者的一些年齡分布、地域分布、對哪些文章比較感興趣等信息。
這些數(shù)據(jù)一定程度上對我之后生產(chǎn)內(nèi)容有一定的啟發(fā)。這些數(shù)據(jù)就是微信公眾號把我們的一些用戶信息、閱讀公眾號的一些行為給記錄下來,并形成圖表等形式展現(xiàn)出來。
特別是To C的消費(fèi)類電子產(chǎn)品,用戶數(shù)量較大,用戶對設(shè)備的使用習(xí)慣對產(chǎn)品經(jīng)理們之后的決策、工程師之后的優(yōu)化方向很有幫助。
線上的嵌入式設(shè)備能記錄用戶的行為,能夠幫助我們深入了解用戶的行為模式,進(jìn)而實(shí)現(xiàn)個(gè)性化推薦、故障預(yù)測、用戶體驗(yàn)優(yōu)化等目標(biāo)。
比如:
??通過分析大量的用戶使用功能A的頻次最多,那么功能A的bug能修復(fù)就盡量修復(fù)好,哪怕是一些比較偏門的路徑,因?yàn)檫@個(gè)功能好用與否可能關(guān)乎到用戶對于這個(gè)產(chǎn)品地評價(jià)。
??通過監(jiān)控用戶在執(zhí)行哪些操作時(shí),觸發(fā)了一些異常,這對于之后地優(yōu)化起到了指導(dǎo)的方向。
??用戶可能在夜間的時(shí)候沒有使用設(shè)備的習(xí)慣,那對于夜間的一些有聲音的預(yù)約操作是不是可以通過各種策略提前預(yù)防到這種情況,防止打擾用戶休息。
具體到各個(gè)行業(yè):
??智能家居行業(yè)。通過智能門鎖、智能照明等設(shè)備的埋點(diǎn)數(shù)據(jù),分析用戶的日常行為習(xí)慣,以優(yōu)化家居環(huán)境的智能化管理。
??工業(yè)自動(dòng)化。通過埋點(diǎn)數(shù)據(jù)收集生產(chǎn)過程中的關(guān)鍵參數(shù),進(jìn)行質(zhì)量控制和數(shù)據(jù)分析,確保產(chǎn)品質(zhì)量的穩(wěn)定性。
??醫(yī)療健康。通過分析用戶的日常健康數(shù)據(jù)(如步數(shù)、心率、睡眠質(zhì)量等),提供個(gè)性化的健康管理建議。
??智能交通。在智能交通信號燈中嵌入埋點(diǎn),根據(jù)實(shí)時(shí)交通流量調(diào)整信號燈配時(shí),提高道路通行效率。
??物聯(lián)網(wǎng)。通過對物聯(lián)網(wǎng)設(shè)備收集的海量數(shù)據(jù)進(jìn)行分析,預(yù)測設(shè)備的運(yùn)行趨勢和潛在故障,提前采取措施進(jìn)行預(yù)防和維護(hù)。
記錄用戶的行為,有個(gè)專業(yè)一點(diǎn)的詞,叫做埋點(diǎn)。
嵌入式埋點(diǎn)就是在嵌入式設(shè)備中預(yù)設(shè)一些數(shù)據(jù)采集點(diǎn)(即“埋點(diǎn)”),當(dāng)特定事件發(fā)生時(shí)(如用戶點(diǎn)擊某個(gè)按鈕、觀看某個(gè)節(jié)目),這些埋點(diǎn)會自動(dòng)記錄并上傳相關(guān)數(shù)據(jù)到服務(wù)器進(jìn)行分析。
如何進(jìn)行數(shù)據(jù)埋點(diǎn)?
整個(gè)數(shù)據(jù)分析的步驟大致如下:
??事件定義與管理:首先,在嵌入式設(shè)備中定義和管理數(shù)據(jù)采集點(diǎn),即“埋點(diǎn)”。這些埋點(diǎn)可以配置為在用戶點(diǎn)擊、交互等事件發(fā)生時(shí)觸發(fā)數(shù)據(jù)采集。
??數(shù)據(jù)采集與傳輸:當(dāng)事件發(fā)生時(shí),嵌入式設(shè)備將相關(guān)數(shù)據(jù)存儲起來并通過網(wǎng)絡(luò)傳輸?shù)綌?shù)據(jù)采集服務(wù)器。這里,可以使用HTTP請求、WebSocket、MQTT等協(xié)議實(shí)現(xiàn)數(shù)據(jù)的實(shí)時(shí)傳輸。
??數(shù)據(jù)處理與分析:在服務(wù)器端,使用大數(shù)據(jù)處理工具對收集到的數(shù)據(jù)進(jìn)行實(shí)時(shí)處理和分析。通過分析用戶的點(diǎn)擊行為、觀看習(xí)慣等,可以建立用戶行為模型,實(shí)現(xiàn)個(gè)性化推薦和安全監(jiān)控等應(yīng)用。
這里我們著重分享事件定義與管理的例子:
我們基于Linux C,使用POSIX線程(pthread)來創(chuàng)建單獨(dú)的線程,并使用POSIX消息隊(duì)列來接收來自其他線程的開機(jī)次數(shù)及按鍵埋點(diǎn)事件。同時(shí),我們將使用cJSON
庫來處理JSON數(shù)據(jù),以及標(biāo)準(zhǔn)文件操作來記錄數(shù)據(jù)到tracking.log文件中。
本例子源碼可以在本公眾號回復(fù)關(guān)鍵詞:埋點(diǎn)例子,進(jìn)行獲取。
本例子源碼可以在本公眾號回復(fù)關(guān)鍵詞:埋點(diǎn)例子,進(jìn)行獲取。
本例子源碼可以在本公眾號回復(fù)關(guān)鍵詞:埋點(diǎn)例子,進(jìn)行獲取。
1、相關(guān)頭文件
#include?<stdio.h>??
#include?<stdlib.h>??
#include?<string.h>??
#include?<unistd.h>
#include?<pthread.h>??
#include?<mqueue.h>??
#include?<sys/stat.h>??
#include?<fcntl.h>??
#include?<time.h>??
#include?"cJSON.h"
2、埋點(diǎn)事件數(shù)據(jù)結(jié)構(gòu)
????//?埋點(diǎn)類型
enum?track_event_type
{
????TRACK_EVENT_TYPE_BOOT,
????TRACK_EVENT_TYPE_BUTTON,
????TRACK_EVENT_TYPE_MAX,
};
//?公共埋點(diǎn)信息
struct?track_event_common_info
{
? ? char?dev_name[32];//?設(shè)備名稱
? ? char?serial_num[32];//?設(shè)備序列號
? ? char?timestamp[64];//?時(shí)間戳
};
//?啟動(dòng)事件信息
struct?track_event_info_boot
{
? ? unsignedint?cnt;//?開機(jī)次數(shù)
};
//?按鍵事件信息
struct?track_event_info_button
{
? ? unsignedchar?button_num;//?按鍵號
? ? unsignedchar?button_type;//?按鍵類型,長按?or?短按
};
//?當(dāng)前的埋點(diǎn)事件信息
union?track_event_info
{
????struct?track_event_info_boot?track_boot;
????struct?track_event_info_button?track_button;
};
//?埋點(diǎn)事件體
struct?tracking_event
{
????enum?track_event_type?event_type;
????union?track_event_info?event_info;
????struct?track_event_common_info?*event_common_info;
};??
3、消息隊(duì)列初始化、清除接口
#define?QUEUE_NAME??"/mq0"?
mqd_t?g_mqd;
intinit_mq(void)
{
????struct?mq_attr?attr;
????attr.mq_flags?=?0;
????attr.mq_maxmsg?=?10;//?最大消息數(shù)??
????attr.mq_msgsize?=?sizeof(struct?tracking_event);//?消息最大大小?
????attr.mq_curmsgs?=?0;//?當(dāng)前隊(duì)列中的消息數(shù)(由系統(tǒng)維護(hù))??
????g_mqd?=?mq_open(QUEUE_NAME,?O_CREAT?|?O_RDWR,0777,&attr);
????if(g_mqd?== (mqd_t)-1)
????{
????????perror("mq_open");
? ? ? ? exit(EXIT_FAILURE);
????}
????return?0;
}
voidcleanup_mq(void)
{
????mq_close(g_mqd);
????mq_unlink(QUEUE_NAME);
}
4、通過消息隊(duì)列發(fā)送埋點(diǎn)事件
//?微信公眾號:嵌入式大雜燴
struct?track_event_common_info?*get_track_event_common_info(void)
{
????staticstruct?track_event_common_info?common_info?={0};
????time_t?rawtime;
????struct?tm?*?timeinfo;
????strncpy(common_info.dev_name,"board?xxx",sizeof(common_info.dev_name)-1);
????strncpy(common_info.serial_num,"1234ABCD567",sizeof(common_info.serial_num)-1);
????time(&rawtime);
????timeinfo?=?localtime(&rawtime);
????strftime(common_info.timestamp,sizeof(common_info.timestamp),"%Y-%m-%d?%H:%M:%S",?timeinfo);
????return?&common_info;
}
voidsend_event(enum?track_event_type?event_type,?union?track_event_info?event_info)
{
????struct?track_event_common_info?*common_info?=?get_track_event_common_info();
????struct?tracking_event?event?={0};
????event.event_type?=?event_type;
????event.event_info?=?event_info;
????event.event_common_info?=?common_info;
????if(mq_send(g_mqd,(constchar*)&event,sizeof(event),1)==-1)
????{
????????perror("mq_send");
????}
}
5、埋點(diǎn)線程實(shí)現(xiàn)
//?微信公眾號:嵌入式大雜燴
void*tracking_thread(void?*arg)
{
????#define?STR(x)??#x?
????FILE*fp?=?fopen("tracking.log","a");
????if(!fp)
????{
????????perror("fopen");
????????return?NULL;
????}
????struct?tracking_event?event?={0};
????while(1)
????{
????????if(mq_receive(g_mqd,(char*)&event,sizeof(event),NULL)==-1)
????????{
? ? ? ? ????perror("mq_receive");
????????????continue;
????????}
????????cJSON?*root?=?cJSON_CreateObject();
????????printf("event.event_type?=?%dn",?event.event_type);
????????switch(event.event_type)
????????{
????????????case?TRACK_EVENT_TYPE_BOOT:
????????????{
????????????????cJSON_AddStringToObject(root,"even_type",?STR(TRACK_EVENT_TYPE_BOOT));
????????????????cJSON_AddNumberToObject(root,"boot_cnt",?event.event_info.track_boot.cnt);
????????????????break;
????????????}
????????????case?TRACK_EVENT_TYPE_BUTTON:
????????????{
????????????????cJSON_AddStringToObject(root,"even_type",?STR(TRACK_EVENT_TYPE_BUTTON));
????????????????cJSON_AddNumberToObject(root,"button_num",?event.event_info.track_button.button_num);
????????????????cJSON_AddNumberToObject(root,"button_type",?event.event_info.track_button.button_type);
????????????????break;
????????????}
????????????default:
????????????break;
????????}
????????cJSON_AddStringToObject(root,"dev_name",?event.event_common_info->dev_name);
????????cJSON_AddStringToObject(root,"serial_num",?event.event_common_info->serial_num);
????????cJSON_AddStringToObject(root,"timestamp",?event.event_common_info->timestamp);
????????char*json_str?=?cJSON_Print(root);
????????fprintf(fp,"%sn",?json_str);
????????printf("json_str?=?%sn",?json_str);
????????free(json_str);
????????cJSON_Delete(root);
????????fflush(fp);
????????usleep(100*1000);
????}
????fclose(fp);
????pthread_exit(NULL);
}
6、主函數(shù)實(shí)現(xiàn)
//?微信公眾號:嵌入式大雜燴
intmain(void)
{
????pthread_t?thread_id;
????init_mq();
????//?創(chuàng)建跟蹤線程??
????if(pthread_create(&thread_id,NULL,?tracking_thread,NULL)!=0)
????{
????????perror("pthread_create");
????????return?1;
????}
????while(1)
????{
????????int?ch?=?getchar();
????????switch(ch)
????????{
????????????case'1':
????????????{
????????????????//?模擬發(fā)送TRACK_EVENT_TYPE_BOOT事件??
????????????????printf("TRACK_EVENT_TYPE_BOOTn");
????????????????enum?track_event_type?event_type?={0};
????????????????union?track_event_info?event_info?={0};
????????????????event_type?=?TRACK_EVENT_TYPE_BOOT;
????????????????event_info.track_boot.cnt?+=1;
????????????????send_event(event_type,?event_info);
????????????????break;
????????????}
????????????case'2':
????????????{
????????????????//?模擬發(fā)送button_1_pressed事件??
????????????????printf("TRACK_EVENT_TYPE_BUTTONn");
????????????????enum?track_event_type?event_type?={0};
????????????????union?track_event_info?event_info?={0};
????????????????event_type?=?TRACK_EVENT_TYPE_BUTTON;
????????????????event_info.track_button.button_num?=1;
????????????????event_info.track_button.button_type?=1;
????????????????send_event(event_type,?event_info);
????????????????break;
????????????}
????????????default:
????????????break;
????????}
????????usleep(100);
????}
????//?清理??
????pthread_join(thread_id,NULL);
????cleanup_mq();
????return0;
}
編譯運(yùn)行:
gcc?cJSON.c?test.c?-o?test?-lpthread?-lrt
埋點(diǎn)文件把相關(guān)埋點(diǎn)事件給記錄了下來,實(shí)際埋點(diǎn)信息可以根據(jù)需要進(jìn)行增刪。
埋點(diǎn)注意事項(xiàng)
??保護(hù)用戶隱私:確保收集的數(shù)據(jù)符合隱私政策,避免泄露敏感信息。
??合理控制埋點(diǎn)數(shù)量:過多埋點(diǎn)會影響系統(tǒng)性能和用戶體驗(yàn)。
??數(shù)據(jù)準(zhǔn)確性:確保收集到的數(shù)據(jù)準(zhǔn)確無誤,避免誤導(dǎo)決策。
??是否有必要進(jìn)行埋點(diǎn)?埋點(diǎn)的缺點(diǎn):
??開發(fā)成本高:嵌入式埋點(diǎn)需要在每個(gè)監(jiān)測點(diǎn)都編寫單獨(dú)的事件監(jiān)測代碼,增加了開發(fā)工作量。
??存在延遲上報(bào)和漏報(bào)情況:由于網(wǎng)絡(luò)延遲、設(shè)備性能等因素,可能存在數(shù)據(jù)上報(bào)的延遲或漏報(bào)情況。這會影響數(shù)據(jù)的完整性和準(zhǔn)確性。進(jìn)而會影響數(shù)據(jù)分析。
??無歷史記錄:嵌入式埋點(diǎn)只能采集到植入代碼之后的數(shù)據(jù),無法回溯到之前的歷史數(shù)據(jù)。這可能導(dǎo)致在分析用戶行為或系統(tǒng)變化時(shí)缺乏全面的數(shù)據(jù)支持。
??影響系統(tǒng)性能:埋點(diǎn)或多或少會帶來一些性能開銷。
埋點(diǎn)作為一種數(shù)據(jù)收集和分析的技術(shù)手段,具有其獨(dú)特的優(yōu)點(diǎn)和缺點(diǎn)。在實(shí)際應(yīng)用中,需要根據(jù)具體需求和場景進(jìn)行權(quán)衡和選擇。