一.問題描述:海量日誌資料,提取出某日訪問網頁YY,http://yy.com/次數最多的那個IP,如下:
,可以在右下角輸入一個頻道號,如2080進入相關頻道。
二.問題類比:
1.產生海量IP,並把這些IP儲存在一個檔案中,如下:
void ConstructBigData::constructIps(std::string fileName){
std::ofstream outfile(fileName.c_str(), std::ios::out);
std::stringstream ip("");
unsigned short num = 0;
srand((unsigned) time(NULL));
for (int i = 0; i < 9000000; ++i) {
for (int j = 0; j < 4; ++j) {
num = (rand() % 256);//產生IPv4地址
ip << num;
if (j < 3)
ip << '.';
else
ip << '\n';
}
outfile << ip.str();
ip.str("");
outfile.flush();
}
outfile.close();
}
說明:這裡產生了9000000個IP,也許離海量還有點距離,但是海量是相對的,處理方法是相通的,需要根據具體情況具體產生,這裡考慮的是一般情況。fileName即我們儲存IP的檔案名稱。
2.這9000000個IP可以一次性裝入記憶體,進行相關計算統計,但是我們講的是這類問題的處理方法,比如一次性沒法裝載時,就可以這樣處理。這裡可以把這些IP讀出來,根據模數把它們劃分成幾個更小的檔案,前提是要保證相同的IP被劃分在相同的小檔案中,如下:
void ConstructBigData::filePartition(std::string fileName){
std::ifstream infile(fileName.c_str(),std::ios::in);
std::ofstream outfile0("outfile0.txt",std::ios::out);
std::ofstream outfile1("outfile1.txt",std::ios::out);
std::ofstream outfile2("outfile2.txt",std::ios::out);
std::ofstream outfile3("outfile3.txt",std::ios::out);
std::ofstream outfile4("outfile4.txt",std::ios::out);
if(!infile){
return;
}
unsigned short val1,val2,val3,val4;
unsigned char ch1,ch2,ch3;
unsigned long ipval = 0;
int modval = 0;
std::stringstream ss;
std::string buffer;
std::stringstream ssbuf("");
while (!infile.eof()) {
getline(infile,buffer);
ssbuf<<buffer;
if(!infile.eof()){
ssbuf >> val1 >> ch1 >> val2 >> ch2 >> val3 >> ch3 >> val4;
ipval = (((((val1<<8) + val2)<<8)+val3)<<8)+val4;
modval = ipval % 5;
switch(modval){
case 0:
outfile0 << ssbuf.str() << '\n';
break;
case 1:
outfile1 << ssbuf.str() << '\n';
break;
case 2:
outfile2 << ssbuf.str() << '\n';
break;
case 3:
outfile3 << ssbuf.str() << '\n';
break;
case 4:
outfile4 << ssbuf.str() << '\n';
break;
default:
std::cout<<"sb"<<std::endl;
break;
}
ipval = 0;
}
ssbuf.clear();
ssbuf.str("");
}
outfile0.flush();
outfile1.flush();
outfile2.flush();
outfile3.flush();
outfile4.flush();
outfile0.close();
outfile1.close();
outfile2.close();
outfile3.close();
outfile4.close();
infile.close();
}
說明:這裡取值為mod5,也只是僅僅樣本而已,需要根據具體情況具體處理,這裡講的是一般方法。
3.依次讀取每個檔案,取出每個檔案訪問次數最多的那個IP,再比較這些IP中的最大值即可,這裡可以使用暴雪的雜湊演算法,因為雜湊演算法是最快的hash演算法,如下:
void ConstructBigData::findMax(){
int fileNum = 5;
std::stringstream ss("");
for(int i=0;i<fileNum;++i){
std::string fileName = "outfile";
std::string suffix = ".txt";
ss<<fileName<<i<<suffix;
std::ifstream infile(ss.str().c_str(),std::ios::in);
std::string buffer;
while (!infile.eof()) {
getline(infile,buffer);
if(!infile.eof()){
mpq.SetHashTable(buffer);//雜湊,其中mpq是暴雪hash演算法實現的一個執行個體。
}
}
infile.close();
printf("from %s->",ss.str().c_str());
Max();
ss.clear();
ss.str("");
}
}
void ConstructBigData::Max(){//尋找每個檔案中的訪問最多的IP的值
int index = 0;
for (int i = 0; i < hashMpqLen; ++i) {
if (mpq.m_HashIndexTable[i].bExists) {
if(mpq.m_HashIndexTable[i].count > mpq.m_HashIndexTable[index].count){
index = i;
}
}
}
if(mpq.m_HashIndexTable[index].bExists){
printf("%s,%d\n",mpq.m_HashIndexTable[index].test_filename,mpq.m_HashIndexTable[index].count);
}
mpq.reset(hashMpqLen);//重設hash結構
}
三.暴雪hash演算法實現的執行個體講解,如下:
1.定義hash表資料結構,如下:
typedef struct
{
long nHashA;//用於hash演算法
long nHashB;//用於hash演算法
bool bExists;//用於hash演算法
char test_filename[MAXFILENAME];//儲存IP地址
int count;//用於儲存訪問的次數
} MPQHASHTABLE;
2.hash表的初始化以及重設,如下:
void HashMpq::reset(const long nTableLength) {//nTableLength為hash表的長度
for (int i = 0; i < nTableLength; i++) {
m_HashIndexTable[i].nHashA = -1;
m_HashIndexTable[i].nHashB = -1;
m_HashIndexTable[i].bExists = false;
m_HashIndexTable[i].test_filename[0] = '\0';
m_HashIndexTable[i].count = 0;
}
}
3.對IP地址進行hash,如下:
bool HashMpq::SetHashTable(std::string lpszString )
{
const unsigned long HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
unsigned long nHash = HashString(lpszString, HASH_OFFSET);
unsigned long nHashA = HashString(lpszString, HASH_A);
unsigned long nHashB = HashString(lpszString, HASH_B);
unsigned long nHashStart = nHash % m_tablelength, nHashPos = nHashStart;
while (m_HashIndexTable[nHashPos].bExists) {
/*
TODO,判斷該IP地址是否已經存在,存在的話,只需在原來的基礎上加1即可,這裡略去
*/
nHashPos = (nHashPos + 1) % m_tablelength;//處理衝突的方法,即順延
if (nHashPos == nHashStart) {
return false;
}
}
//否則表示該IP在hash表中還木有。
m_HashIndexTable[nHashPos].bExists = true;
m_HashIndexTable[nHashPos].nHashA = nHashA;
m_HashIndexTable[nHashPos].nHashB = nHashB;
strcpy( m_HashIndexTable[nHashPos].test_filename, lpszString.c_str());//表示該IP地址
m_HashIndexTable[nHashPos].count = 1;//表示第一次訪問,所以設定count為1
return true;
}
四.main函數,如下:
#include "ConstructBigData.h"
#include <iostream>
#include <stdio.h>
int main(int argc,char** argv){
ConstructBigData bd(10000000);//參數表示hash表的長度
std::string fileName = "bigdata.txt";//表示儲存的海量IP的檔案名稱
bd.constructIps(fileName);//構造海量IP
bd.filePartition(fileName);//partition
bd.findMax();//尋找每個partition中的最大值
return 0;
}
未完待續~
PS:初寫文章,文筆生澀之處,各位請見諒,若有疑問或者交流的,可加本人YY號:301558660
轉載請註明出處:山水間部落格,http://blog.csdn.net/linyanwen99/article/details/8182814