問題分析:
- Step 1. 首先將這天訪問百度的IP從log中讀取出來,并寫入到一個大文件中,這個過程可以通過程序生成大量隨機的IP來模擬;
- Step 2. 以IPV4為例,IP是32位的(因為IP格式為a.b.c.d,其中a/b/c/d都是0-255的整數(shù)),因此可能有2^32個不同的IP,占據(jù)的空間為4 * 4G = 16GB,對于32位的操作系統(tǒng)而言無法完全加載到內(nèi)存中處理;
解決方案:
分而治之 + hash
- 分而治之:既然所有的IP數(shù)量太大,那么可以將這些IP分在多個不同的小文件中,這樣每次處理的小文件所需要的內(nèi)存空間就小多了;
- hash:對IP取hash值并取余數(shù)(比如1000,就有1000個小文件),這樣的話相同的IP可以分配在同一個小文件中;當(dāng)然,不同的IP也可能會有同樣的hash值;
- 然后依次讀取每個小文件,對于單個小文件遍歷其中的IP,統(tǒng)計出每個小文件中出現(xiàn)次數(shù)最多的IP(放在Hashmap中,IP為key,次數(shù)為value);
- 從Hashmap中找出value最大的key-value對;
java代碼:
public class IpUtil {
private List<String> keyList = new LinkedList<String>(); //保存每個小文件中次數(shù)出現(xiàn)最多的IP
private int ipMaxNum = 0; //次數(shù)出現(xiàn)最多的值
private int callNum = 0;
/**
* 模擬生成大量的IP(1億個)并批量寫入到同一個大文件中
*/
public void genIP2BigFile(File ipFile, long numberOfLine){
BufferedWriter bw = null;
FileWriter fw = null;
long startTime = System.currentTimeMillis();
try{
fw = new FileWriter(ipFile,true);
bw = new BufferedWriter(fw);
SecureRandom random = new SecureRandom();
for (int i = 0; i < numberOfLine; i++) {
bw.write("10."+random.nextInt(255)+"."+random.nextInt(255)+"."+random.nextInt(255)+"\n");
if((i+1) % 1000 == 0) {
// 每1000條批量寫入文件中,提高效率
bw.flush();
}
}
bw.flush();
long endTime = System.currentTimeMillis();
System.err.println((endTime - startTime) / 1000);
}catch (Exception e) {
e.printStackTrace();
}finally{
try{
if(fw != null){
fw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
try{
if(bw != null){
bw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* check這個大文件是否已經(jīng)存在
*/
public void checkFileExists(File ipFile) {
if (!ipFile.exists()) {
try {
ipFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
gernBigFile(ipFile, 100000000);
System.out.println(">>>>>> Ip file generated.");
} else {
System.out.println(">>>>>> Ip file already existed.");
}
}
/**
* 對IP求取hash值并取余數(shù)(比如1000),將hash值相同的IP放入到同一個小文件中,得到1000個小文件
*/
public void splitFile(File ipFile,int numberOfFile){
Map<Integer,BufferedWriter> bwMap = new HashMap<Integer,BufferedWriter>();//保存每個文件的流對象
Map<Integer,List<String>> dataMap = new HashMap<Integer,List<String>>();//分隔文件用
BufferedReader br = null;
FileReader fr = null;
BufferedWriter bw = null;
FileWriter fw = null;
long startTime = System.currentTimeMillis();
try{
fr = new FileReader(ipFile);
br = new BufferedReader(fr);
String ipLine = br.readLine();
//先創(chuàng)建文件及流對象方便使用
for(int i=0;i<numberOfFile;i++){
File file = new File("/Users/ycaha/Desktop/tmpIps/"+ i + ".log");
bwMap.put(i, new BufferedWriter(new FileWriter(file,true)));
dataMap.put(i, new LinkedList<String>());
}
while(ipLine != null){
// 對每個ip求取hash
int hashCode = ipLine.hashCode();
hashCode = hashCode < 0 ? -hashCode : hashCode;
// 對hash值取余數(shù),根據(jù)余數(shù)分配文件地址
int fileNum = hashCode % numberOfFile;
List<String> list = dataMap.get(fileNum);
list.add(ipLine + "\n");
if(list.size() % 1000 == 0){
BufferedWriter writer = bwMap.get(fileNum);
for(String line : list){
writer.write(line);
}
writer.flush();
list.clear();
}
ipLine = br.readLine();
}
for(int fn : bwMap.keySet()){
List<String> list = dataMap.get(fn);
BufferedWriter writer = bwMap.get(fn);
for(String line : list){
writer.write(line);
}
list.clear();
writer.flush();
writer.close();
}
bwMap.clear();
long endTime = System.currentTimeMillis();
System.err.println((endTime - startTime) / 1000);
}catch (Exception e) {
e.printStackTrace();
}finally{
try{
if(fr != null){
fr.close();
}
}catch (Exception e) {
e.printStackTrace();
}
try{
if(br != null){
br.close();
}
}catch (Exception e) {
e.printStackTrace();
}
try{
if(fw != null){
fw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
try{
if(bw != null){
bw.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 讀取單個小文件split,并獲取小文件中的出現(xiàn)次數(shù)最大的IP
*/
public void read(File split) {
Map<String,Integer> ipNumMap = new HashMap<String, Integer>(); //保存每個文件中的每個IP出現(xiàn)的次數(shù)
//使用局部變量,不要使用全局變量,以免OOM
callNum ++;
BufferedReader br = null;
FileReader fr = null;
long startTime = System.currentTimeMillis();
try{
fr = new FileReader(split);
br = new BufferedReader(fr);
String ipLine = br.readLine();
while(ipLine != null) {
ipLine = ipLine.trim();
if (ipNumMap.containsKey(ipLine)) {
Integer count = ipNumMap.get(ipLine);
count ++;
ipNumMap.replace(ipLine, count);
} else {
ipNumMap.put(ipLine, 1);
}
ipLine = br.readLine();
}
Set<String> keys = ipNumMap.keySet();
for (String key: keys) {
int value = ipNumMap.get(key);
if (value > ipMaxNum) {
ipMaxNum = value;
keyList.add(key);
}
}
long endTime = System.currentTimeMillis();
totalTime += (endTime - startTime);
} catch (Exception e) {
e.printStackTrace();
} finally{
try{
if(fr != null){
fr.close();
}
}catch (Exception e) {
e.printStackTrace();
}
try{
if(br != null){
br.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(">>>>>> FileOrder: " + callNum + ", ipMaxNum: " + ipMaxNum + ", key: " + keyList.get(keyList.size()-1) );
}
}
// 主類
public class TestMain {
public static void main(String[] args) throws UnsupportedEncodingException {
File ipFile = new File("/Users/ycaha/Desktop/ipAddr.log");
IpUtil genIp = new IpUtil();
genIp.checkFileExists(ipFile);
long start = System.currentTimeMillis();
genIp.splitFile(ipFile, 1000);
File files = new File("/Users/ycaha/Desktop/tmpIps/");
for (File split : files.listFiles()) {
genIp.read(split);
}
long end = System.currentTimeMillis();
System.out.println(">>>>>> The whole consumed time in seconds: " + (end - start) / 1000);
}
}
結(jié)論:
- 生成的大文件ipAddr.log的大小為1.38G;
- 單個小文件的大小為2.1M,共有1000個;
- 出現(xiàn)次數(shù)最多的IP鍵值對為(10.52.99.80,52);