校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識庫 > golang如何實現mapreduce單進程版本詳解

golang如何實現mapreduce單進程版本詳解

熱門標簽:阿里云ai電話機器人 黃岡人工智能電銷機器人哪個好 浙江高頻外呼系統(tǒng)多少錢一個月 濱州自動電銷機器人排名 建造者2地圖標注 鄭州亮點科技用的什么外呼系統(tǒng) 汕頭小型外呼系統(tǒng) 惠州電銷防封電話卡 釘釘有地圖標注功能嗎

前言

  MapReduce作為hadoop的編程框架,是工程師最常接觸的部分,也是除去了網絡環(huán)境和集群配 置之外對整個Job執(zhí)行效率影響很大的部分,所以很有必要深入了解整個過程。元旦放假的第一天,在家沒事干,用golang實現了一下mapreduce的單進程版本,github地址。處理對大文件統(tǒng)計最高頻的10個單詞,因為功能比較簡單,所以設計沒有解耦合。

  本文先對mapreduce大體概念進行介紹,然后結合代碼介紹一下,如果接下來幾天有空,我會實現一下分布式高可用的mapreduce版本。下面話不多說了,來一起看看詳細的介紹吧。

1. Mapreduce大體架構

  上圖是論文中mapreduce的大體架構。總的來說Mapreduce的思想就是分治思想:對數據進行分片,然后用mapper進行處理,以key-value形式輸出中間文件;然后用reducer進行對mapper輸出的中間文件進行合并:將key一致的合到一塊,并輸出結果文件;如果有需要,采用Combiner進行最后的合并。

  歸納來說主要分為5部分:用戶程序、Master、Mapper、Reducer、Combiner(上圖未給出)。

  • 用戶程序。用戶程序主要對輸入數據進行分割,制定Mapper、Reducer、Combiner的代碼。
  • Master:中控系統(tǒng)。控制分發(fā)Mapper、Reduer的個數,比如生成m個進程處理Mapper,n個進程處理Reducer。其實對Master來說,Mapper和Reduer都屬于worker,只不過跑的程序不一樣,Mapper跑用戶輸入的map代碼,Reduer跑用戶輸入的reduce代碼。Master還作為管道負責中間路徑傳遞,比如將Mapper生成的中間文件傳遞給Reduer,將Reduer生成的結果文件返回,或者傳遞給Combiner(如果有需要的話)。由于Master是單點,性能瓶頸,所以可以做集群:主備模式或者分布式模式??梢杂脄ookeeper進行選主,用一些消息中間件進行數據同步。Master還可以進行一些策略處理:比如某個Worker執(zhí)行時間特別長,很有可能卡住了,對分配給該Worker的數據重新分配給別的Worker執(zhí)行,當然需要對多份數據返回去重處理。
  • Mapper:負責將輸入數據切成key-value格式。Mapper處理完后,將中間文件的路徑告知Master,Master獲悉后傳遞給Reduer進行后續(xù)處理。如果Mapper未處理完,或者已經處理完但是Reduer未讀完其中間輸出文件,分配給該Mapper的輸入將重新被別的Mapper執(zhí)行。
  • Reducer: 接受Master發(fā)送的Mapper輸出文件的消息,RPC讀取文件并處理,并輸出結果文件。n個Reduer將產生n個輸出文件。
  • Combiner: 做最后的歸并處理,通常不需要。

  總的來說,架構不復雜。組件間通信用啥都可以,比如RPC、HTTP或者私有協議等。

2. 實現代碼介紹

  該版本代碼實現了單機單進程版本,Mapper、Reducer和Combiner的實現用協程goroutine實現,通信采用channel。代碼寫的比較隨意,沒有解耦合。

  • 功能:統(tǒng)計給定文件中出現的最高頻的10個單詞
  • 輸入:大文件
  • 輸出:最高頻的10個單詞
  • 實現:5個Mapper協程、2個Reducer、1個Combiner。

  為了方便起見,Combiner對最高頻的10個單詞進行堆排序處理,按規(guī)范來說應該放在用戶程序處理。

  文件目錄如下,其中bin文件夾下的big_input_file.txt為輸入文件,可以調用generate下的main文件生成,caller文件為入口的用戶程序,master目錄下分別存放master、mapper、reducer、combiner代碼:

.
├── README.md
├── bin
│ └── file-store
│  └── big_input_file.txt
└── src
 ├── caller
 │ └── main.go
 ├── generate
 │ └── main.go
 └── master
  ├── combiner.go
  ├── mapper.go
  ├── master.go
  └── reducer.go

6 directories, 8 files 

2.1 caller

  用戶程序,讀入文件并按固定行數進行劃分;然后調用master.Handle進行處理。

package main
import ( 
 "os"
 "path"
 "path/filepath"
 "bufio"
 "strconv"
 "master"
 "github.com/vinllen/go-logger/logger"
)
const ( 
 LIMIT int = 10000 // the limit line of every file
)
func main() { 
 curDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
 if err != nil {
  logger.Error("Read path error: ", err.Error())
  return
 }
 fileDir := path.Join(curDir, "file-store")
 _ = os.Mkdir(fileDir, os.ModePerm)
 // 1. read file
 filename := "big_input_file.txt"
 inputFile, err := os.Open(path.Join(fileDir, filename))
 if err != nil {
  logger.Error("Read inputFile error: ", err.Error())
  return
 }
 defer inputFile.Close()
 // 2. split inputFile into several pieces that every piece hold 100,000 lines
 filePieceArr := []string{}
 scanner := bufio.NewScanner(inputFile)
 piece := 1
Outter: 
 for {
  outputFilename := "input_piece_" + strconv.Itoa(piece)
  outputFilePos := path.Join(fileDir, outputFilename)
  filePieceArr = append(filePieceArr, outputFilePos)
  outputFile, err := os.Create(outputFilePos)
  if err != nil {
   logger.Error("Split inputFile error: ", err.Error())
   continue
  }
  defer outputFile.Close()
  for cnt := 0; cnt  LIMIT; cnt++ {
   if !scanner.Scan() {
    break Outter
   }
   _, err := outputFile.WriteString(scanner.Text() + "\n")
   if err != nil {
    logger.Error("Split inputFile writting error: ", err.Error())
    return
   }
  }
  piece++
 }
 // 3. pass to master
 res := master.Handle(filePieceArr, fileDir)
 logger.Warn(res)
}

2.2 master

  Master程序,依次生成Combiner、Reducer、Mapper,處理消息中轉,輸出最后結果。

package master
import (
 "github.com/vinllen/go-logger/logger"
)
var ( 
 MapChanIn chan MapInput // channel produced by master while consumed by mapper
 MapChanOut chan string // channel produced by mapper while consumed by master
 ReduceChanIn chan string // channel produced by master while consumed by reducer
 ReduceChanOut chan string // channel produced by reducer while consumed by master
 CombineChanIn chan string // channel produced by master while consumed by combiner
 CombineChanOut chan []Item // channel produced by combiner while consumed by master
)
func Handle(inputArr []string, fileDir string) []Item { 
 logger.Info("handle called")
 const(
  mapperNumber int = 5
  reducerNumber int = 2
 )
 MapChanIn = make(chan MapInput)
 MapChanOut = make(chan string)
 ReduceChanIn = make(chan string)
 ReduceChanOut = make(chan string)
 CombineChanIn = make(chan string)
 CombineChanOut = make(chan []Item)
 reduceJobNum := len(inputArr)
 combineJobNum := reducerNumber
 // start combiner
 go combiner()
 // start reducer
 for i := 1; i = reducerNumber; i++ {
  go reducer(i, fileDir)
 }
 // start mapper
 for i := 1; i = mapperNumber; i++ {
  go mapper(i, fileDir)
 }
 go func() {
  for i, v := range(inputArr) {
   MapChanIn - MapInput{
    Filename: v,
    Nr: i + 1,
   } // pass job to mapper
  }
  close(MapChanIn) // close map input channel when no more job
 }()
 var res []Item
outter: 
 for {
  select {
   case v := - MapChanOut:
    go func() {
     ReduceChanIn - v
     reduceJobNum--
     if reduceJobNum = 0 {
      close(ReduceChanIn)
     }
    }()
   case v := - ReduceChanOut:
    go func() {
     CombineChanIn - v
     combineJobNum--
     if combineJobNum = 0 {
      close(CombineChanIn)
     }
    }()
   case v := - CombineChanOut:
    res = v
    break outter
  }
 }
 close(MapChanOut)
 close(ReduceChanOut)
 close(CombineChanOut)
 return res
}

2.3 mapper

  Mapper程序,讀入并按key-value格式生成中間文件,告知Master。

package master
import ( 
 "fmt"
 "path"
 "os"
 "bufio"
 "strconv"

 "github.com/vinllen/go-logger/logger"
)
type MapInput struct { 
 Filename string
 Nr int
}
func mapper(nr int, fileDir string) { 
 for {
  val, ok := - MapChanIn // val: filename
  if !ok { // channel close
   break
  }
  inputFilename := val.Filename
  nr := val.Nr
  file, err := os.Open(inputFilename)
  if err != nil {
   errMsg := fmt.Sprintf("Read file(%s) error in mapper(%d)", inputFilename, nr)
   logger.Error(errMsg)
   MapChanOut - ""
   continue
  }
  mp := make(map[string]int)
  scanner := bufio.NewScanner(file)
  scanner.Split(bufio.ScanWords)
  for scanner.Scan() {
   str := scanner.Text()
   //logger.Info(str)
   mp[str]++
  }
  outputFilename := path.Join(fileDir, "mapper-output-" + strconv.Itoa(nr))
  outputFileHandler, err := os.Create(outputFilename)
  if err != nil {
   errMsg := fmt.Sprintf("Write file(%s) error in mapper(%d)", outputFilename, nr)
   logger.Error(errMsg)
  } else {
   for k, v := range mp {
    str := fmt.Sprintf("%s %d\n", k, v)
    outputFileHandler.WriteString(str)
   }
   outputFileHandler.Close()
  }
  MapChanOut - outputFilename
 }
}

2.4 reducer

  Reducer程序,讀入Master傳遞過來的中間文件并歸并。

package master
import ( 
 "fmt"
 "bufio"
 "os"
 "strconv"
 "path"
 "strings"
 "github.com/vinllen/go-logger/logger"
)
func reducer(nr int, fileDir string) { 
 mp := make(map[string]int) // store the frequence of words
 // read file and do reduce
 for {
  val, ok := - ReduceChanIn
  if !ok {
   break
  }
  logger.Debug("reducer called: ", nr)
  file, err := os.Open(val)
  if err != nil {
   errMsg := fmt.Sprintf("Read file(%s) error in reducer", val)
   logger.Error(errMsg)
   continue
  }
  scanner := bufio.NewScanner(file)
  for scanner.Scan() {
   str := scanner.Text()
   arr := strings.Split(str, " ")
   if len(arr) != 2 {
    errMsg := fmt.Sprintf("Read file(%s) error that len of line(%s) != 2(%d) in reducer", val, str, len(arr))
    logger.Warn(errMsg)
    continue
   }
   v, err := strconv.Atoi(arr[1])
   if err != nil {
    errMsg := fmt.Sprintf("Read file(%s) error that line(%s) parse error in reduer", val, str)
    logger.Warn(errMsg)
    continue
   }
   mp[arr[0]] += v
  }
  if err := scanner.Err(); err != nil {
   logger.Error("reducer: reading standard input:", err)
  }
  file.Close()
 }
 outputFilename := path.Join(fileDir, "reduce-output-" + strconv.Itoa(nr))
 outputFileHandler, err := os.Create(outputFilename)
 if err != nil {
  errMsg := fmt.Sprintf("Write file(%s) error in reducer(%d)", outputFilename, nr)
  logger.Error(errMsg)
 } else {
  for k, v := range mp {
   str := fmt.Sprintf("%s %d\n", k, v)
   outputFileHandler.WriteString(str)
  }
  outputFileHandler.Close()
 }
 ReduceChanOut - outputFilename
}

2.5 combiner

  Combiner程序,讀入Master傳遞過來的Reducer結果文件并歸并成一個,然后堆排序輸出最高頻的10個詞語。

package master
import ( 
 "fmt"
 "strings"
 "bufio"
 "os"
 "container/heap"
 "strconv"

 "github.com/vinllen/go-logger/logger"
)
type Item struct { 
 key string
 val int
}
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { 
 return len(pq)
}
func (pq PriorityQueue) Less(i, j int) bool { 
 return pq[i].val > pq[j].val
}
func (pq PriorityQueue) Swap(i, j int) { 
 pq[i], pq[j] = pq[j], pq[i]
}
func (pq *PriorityQueue) Push(x interface{}) { 
 item := x.(*Item)
 *pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} { 
 old := *pq
 n := len(old)
 item := old[n - 1]
 *pq = old[0 : n - 1]
 return item
}
func combiner() { 
 mp := make(map[string]int) // store the frequence of words
 // read file and do combine
 for {
  val, ok := - CombineChanIn
  if !ok {
   break
  }
  logger.Debug("combiner called")
  file, err := os.Open(val)
  if err != nil {
   errMsg := fmt.Sprintf("Read file(%s) error in combiner", val)
   logger.Error(errMsg)
   continue
  }
  scanner := bufio.NewScanner(file)
  for scanner.Scan() {
   str := scanner.Text()
   arr := strings.Split(str, " ")
   if len(arr) != 2 {
    errMsg := fmt.Sprintf("Read file(%s) error that len of line != 2(%s) in combiner", val, str)
    logger.Warn(errMsg)
    continue
   }
   v, err := strconv.Atoi(arr[1])
   if err != nil {
    errMsg := fmt.Sprintf("Read file(%s) error that line(%s) parse error in combiner", val, str)
    logger.Warn(errMsg)
    continue
   }
   mp[arr[0]] += v
  }
  file.Close()
 }
 // heap sort
 // pq := make(PriorityQueue, len(mp))
 pq := make(PriorityQueue, 0)
 heap.Init(pq)
 for k, v := range mp {
  node := Item {
   key: k,
   val: v,
  }
  // logger.Debug(k, v)
  heap.Push(pq, node)
 }
 res := []Item{}
 for i := 0; i  10  pq.Len() > 0; i++ {
  node := heap.Pop(pq).(*Item)
  res = append(res, *node)
 }
 CombineChanOut - res
}

3. 總結

  不足以及未實現之處:

  • 各模塊間耦合性高
  • master單點故障未擴展
  • 未采用多進程實現,進程間采用RPC通信
  • 未實現單個Workder時間過長,另起Worker執(zhí)行任務的代碼。

  接下來要是有空,我會實現分布式高可用的代碼,模塊間采用RPC通訊。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

您可能感興趣的文章:
  • golang 輸出重定向:fmt Log,子進程Log,第三方庫logrus的詳解
  • Golang信號處理及如何實現進程的優(yōu)雅退出詳解
  • golang守護進程用法示例
  • golang 后臺進程的啟動和停止操作

標簽:晉中 滄州 昭通 泰安 東營 瀘州 阿壩 駐馬店

巨人網絡通訊聲明:本文標題《golang如何實現mapreduce單進程版本詳解》,本文關鍵詞  golang,如何,實現,mapreduce,;如發(fā)現本文內容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內容系統(tǒng)采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《golang如何實現mapreduce單進程版本詳解》相關的同類信息!
  • 本頁收集關于golang如何實現mapreduce單進程版本詳解的相關信息資訊供網民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    狠狠色综合日日| 亚洲精品在线网站| 国模一区二区三区白浆| 亚洲最大的成人av| 亚洲日本va在线观看| 中文字幕在线一区| 一区二区三区91| 午夜在线成人av| 久久av资源网| 成人激情免费视频| 日本高清不卡aⅴ免费网站| 亚洲天堂免费看| 91精品国产欧美一区二区18| 2020国产精品久久精品美国| 久久精品人人做人人综合 | 一本色道久久综合狠狠躁的推荐| 97久久精品人人做人人爽50路| 精品无码三级在线观看视频| 亚洲欧洲av在线| 亚洲人精品一区| 国产呦萝稀缺另类资源| 不卡电影一区二区三区| 色婷婷久久久亚洲一区二区三区| 成人在线一区二区三区| 日本精品免费观看高清观看| 欧美日韩日日摸| 精品国产一区二区国模嫣然| 精品国产99国产精品| 国产高清久久久久| 国产乱人伦偷精品视频不卡| 99热在这里有精品免费| 欧美日韩高清在线播放| 国产三级一区二区三区| 午夜av一区二区| 北岛玲一区二区三区四区| 欧美精品久久天天躁| 日韩久久免费av| 日本一区中文字幕| 视频在线观看国产精品| 日韩一级二级三级精品视频| 中文字幕在线不卡| 日韩电影在线免费看| 欧美午夜不卡在线观看免费| 综合亚洲深深色噜噜狠狠网站| 青青草成人在线观看| 色综合一个色综合亚洲| www日韩大片| 久久精品国产在热久久| 欧美在线观看视频一区二区三区| 日本午夜一本久久久综合| 国产成人h网站| 欧美精品自拍偷拍| 欧美aaaaaa午夜精品| 精品国产电影一区二区| 国产精品一区一区| 国产精品网站一区| 91免费观看视频| 亚洲黄色小说网站| 国产一区二区调教| 国产亚洲欧洲997久久综合 | 色综合视频在线观看| 1000部国产精品成人观看| 99综合影院在线| 国产亚洲制服色| 国产一区 二区| 中文字幕亚洲综合久久菠萝蜜| 久久精品久久久精品美女| 欧美日韩成人激情| 色综合天天综合在线视频| 正在播放亚洲一区| 国产一区二区三区免费在线观看| 日韩免费福利电影在线观看| 懂色av一区二区在线播放| 亚洲另类色综合网站| 欧美日韩免费观看一区二区三区 | 国产成人精品在线看| 欧美日韩一区二区三区不卡| 奇米色777欧美一区二区| 国产精品妹子av| 在线不卡一区二区| 美女www一区二区| 亚洲日穴在线视频| 欧美sm美女调教| 不卡电影免费在线播放一区| 麻豆国产精品官网| 亚洲在线视频免费观看| 国产精品你懂的在线| 国产精品免费看片| 精品视频在线免费| 国产精品乡下勾搭老头1| 香蕉成人啪国产精品视频综合网 | 欧美日韩国产片| 成人avav在线| 风流少妇一区二区| 美腿丝袜亚洲一区| 免费视频最近日韩| 婷婷六月综合亚洲| 无码av免费一区二区三区试看| 韩国三级电影一区二区| 国产一区中文字幕| 国产乱人伦偷精品视频免下载| 麻豆久久一区二区| 中文字幕国产一区| 欧美国产一区在线| 亚洲国产精品黑人久久久| 欧美日韩久久久| av成人老司机| 色综合久久综合网97色综合| 91蝌蚪porny| 久久久久国产一区二区三区四区| 一本色道久久综合亚洲91| 日本午夜精品视频在线观看| 裸体健美xxxx欧美裸体表演| 麻豆精品精品国产自在97香蕉| 麻豆精品在线播放| 亚洲一区在线视频| 丝袜脚交一区二区| 香蕉成人伊视频在线观看| 麻豆91精品91久久久的内涵| 精品一区二区影视| 国产成人一区二区精品非洲| 中文字幕在线播放不卡一区| 亚洲夂夂婷婷色拍ww47| 久久久久久麻豆| 欧美成人vr18sexvr| 国产精品色哟哟| 亚洲国产精品一区二区www在线 | 欧美日韩在线播放三区四区| 日韩精品中文字幕一区| 亚洲欧美日韩一区二区三区在线观看| 洋洋成人永久网站入口| 欧美视频一区二区| 欧美日韩久久一区二区| 久久嫩草精品久久久久| 亚洲影院免费观看| 国产91在线|亚洲| 色婷婷精品久久二区二区蜜臀av | 91久久久免费一区二区| 日韩视频在线你懂得| 国产欧美1区2区3区| 日韩一区二区精品在线观看| 婷婷开心激情综合| 成人av免费观看| 久久久久9999亚洲精品| 免费欧美高清视频| 在线免费一区三区| 中文字幕一区不卡| 懂色av一区二区三区蜜臀| 欧美一区二区久久久| 亚洲三级在线观看| 成人在线综合网| 欧美一区二区三区四区五区| 97精品超碰一区二区三区| 国产一区二区影院| 欧美精品一级二级| 日韩主播视频在线| 色久综合一二码| 国产精品国产精品国产专区不片| 美女视频一区二区三区| 日韩欧美第一区| 国产一区免费电影| 国产精品三级视频| 国产精品99久久久久久久女警| 精品国产1区二区| 精品一区二区在线视频| 久久久久久久久久看片| 国产精品成人免费| 精品一区二区三区久久久| 91精品啪在线观看国产60岁| 久久99精品国产.久久久久久| 91精品国产综合久久久久久久久久| 亚欧色一区w666天堂| 欧美一级欧美一级在线播放| 国产一区二区三区日韩| 亚洲人成伊人成综合网小说| 欧美在线色视频| 精品一区免费av| 1024亚洲合集| 精品人在线二区三区| 夫妻av一区二区| 午夜电影一区二区| 国产日韩精品一区| 国产成人精品www牛牛影视| 国产精品美女一区二区在线观看| 欧美一区二区久久| 日韩免费一区二区三区在线播放| 另类小说综合欧美亚洲| 尤物在线观看一区| 国产精品三级av在线播放| 欧美日韩免费一区二区三区视频| 美女国产一区二区| 亚洲国产精品精华液网站| 久久精品亚洲国产奇米99| 精品三级av在线| 亚洲女与黑人做爰| wwww国产精品欧美| 欧美一区二区美女| 欧美一区二区三区日韩| 91精品国产综合久久久久久| 欧美亚洲免费在线一区|