海量日志数据,提取出访问百度次数最多的TOPK个IP
-
hash映射:将所有的IP提取出来到一个大文件,将大文件映射成小文件。比如%1000,就会把大文件映射为1000个小文件(这个1000根据实际情况定,设置成每个小文件都小于内存为止)
-
map存储:对每一个小文件进行IP频次的统计,利用map数据结构,遍历到一个就对应加一,找出该小文件的次数最多的那个IP。
-
堆排序:对1000个IP次数大小进行堆排序(此时map假设内存装的下的,如果一次性装不下,得使用多路外部排序对这1000数进行排序),取TopK个IP即可()
为什么这种从文件中取最大的IP是可行的呢?
因为相同IP经过Hash函数后,只会被分到其中一个小文件之中,不会出现在多个文件。
实际实现的代码:
主要难度是文件的读写操作,还有IP字符串的hash操作(利用每一个字符的Ascll值进行操作)
fileToHash("./test.txt")
sort := topKSort(precess(), 2)
fmt.Println(sort)
package BigData
import (
"bufio"
"os"
"sort"
"strconv"
"strings"
)
const filePrefix string = "tmp_"
const hashValue int = 1000
type ipAndCount struct {
ip string
count int
}
// 读取那个大的文件
func fileToHash(file string) {
open, err := os.Open(file)
if err != nil {
}
defer open.Close()
scanner := bufio.NewScanner(open)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
cur := scanner.Text()
//对当前IP进行hash
writeIpToFile(cur)
}
}
// 将字符串的ASCLL值相加,再取模
func stringToIntMod(input string, mod int) int {
sum := 0
for _, char := range input {
sum += int(char)
}
return sum % mod
}
// 将IP追加写到文件中
func writeIpToFile(IP string) {
mod := stringToIntMod(IP, hashValue)
file, err := os.OpenFile(filePrefix+strconv.Itoa(mod), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
}
defer file.Close()
file.Write([]byte(IP + "\n"))
}
// 处理小文件列表 返回每个文件的top1的IP
func precess() []ipAndCount {
files := getSmallFiles("./", filePrefix)
res := make([]ipAndCount, 0, hashValue)
for _, entry := range files {
ipAndCountNode := processOneFile(entry)
res = append(res, ipAndCountNode)
}
return res
}
// 对map进行排序,输出topK个IP
func topKSort(res []ipAndCount, k int) []string {
sort.Slice(res, func(i, j int) bool {
return res[i].count > res[j].count
})
var ans []string
if k > len(res) {
k = len(res)
}
for i := 0; i < k; i++ {
ans = append(ans, res[i].ip)
}
return ans
}
// 获取指定目录下的指定前缀的文件列表
func getSmallFiles(path, prefix string) []string {
var file []string
dir, err := os.ReadDir(path)
if err != nil {
return nil
}
for _, entry := range dir {
if !entry.IsDir() && strings.HasPrefix(entry.Name(), prefix) {
file = append(file, entry.Name())
}
}
return file
}
// 处理每一个小文件,小文件可以全部载入内存,返回次数最多的一个IP
func processOneFile(file string) ipAndCount {
oneFileIp2Count := make(map[string]int, 0)
maxIp := ""
open, err := os.Open(file)
if err != nil {
}
defer open.Close()
scanner := bufio.NewScanner(open)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
cur := scanner.Text()
//对当前IP进行hash
oneFileIp2Count[cur]++
if oneFileIp2Count[cur] > oneFileIp2Count[maxIp] {
maxIp = cur
}
}
return ipAndCount{ip: maxIp, count: oneFileIp2Count[maxIp]}
}