為什么我要寫這篇文章
作為一枚技術(shù) Doge,每天總免不了和不懂技術(shù)的老板 探(si) 討(bi) 業(yè)務(wù)的實現(xiàn)可能性;
前一段日子,老板在深(bi) 入(jiao) 調(diào)(jia) 研(ge) 之后決定引入一個第三方 IOT 平臺,通過 RESTful API 實時反饋設(shè)備的監(jiān)測數(shù)據(jù),而因為公司業(yè)務(wù)的特殊性,所獲得的原始監(jiān)測數(shù)據(jù)我們無法直接使用,而是必須經(jīng)過一番計算。以為每位登錄后臺的用戶顯示過去一周的實時能源消耗為例,我們需要在十五秒內(nèi)完成大約 60 MB (120 萬條記錄) 數(shù)據(jù)的提取、清洗及計算,而這顯然超出了瀏覽器和單次 HTTP 請求所能承受的極限。這個需求讓我想起了之前自己學(xué)習(xí) Golang 練手時寫過的一個分布式計算模型,于是趁此機(jī)會把它擴(kuò)展了一下,寫成框架發(fā)布到社區(qū)里,有興趣的童鞋可以 star 回去試驗一下哦。
為什么要用 Go 重新造輪子
Golang 是 Google 在2007年發(fā)布的一門開源的靜態(tài)編譯型編程語言,在垃圾回收、結(jié)構(gòu)類型以及并發(fā)編程的處理上擁有自己的獨(dú)到之處,近年來更是成為使用頻率上升速度最快的編程語言之一。 可以參考這篇文章,Go 在打包編譯后的性能與 Java 或 C++相似。在我們的使用中,Go 一般比 Python 要快 30 倍。同時,其天然支持的 CSP 模型在很多情況下可以免去了消息隊列的使用,對高并發(fā)場景獨(dú)到的處理優(yōu)勢,更是能夠大大縮短程序員的開發(fā)時間哦。
GoCollaborate 是什么?
很多同學(xué)看到這里可能會問了,這個框架什么?我又能用它來做什么呢?
簡而言之,
GoCollaborate 是一個提供分布式服務(wù)管理搭建的輕量級通用框架,您可以輕松地用它進(jìn)行編程,構(gòu)建擴(kuò)展,以及創(chuàng)建自己的高性能分布式服務(wù)。
有相關(guān)從業(yè)經(jīng)驗的同學(xué)可能聽說過 Apache Hadoop,Spark,Lightbend 的 Akka, 阿里的 Dubbo 以及 Facebook 的 Thrift 等等,一套工具集下來是不是感覺暈頭轉(zhuǎn)向呢?不要緊,我們在這姑且暫時把它當(dāng)成一個輕量級的 Hadoop 好了,隨著教程展開,讓我們一起來體驗 Golang 的神奇魅力。
下面我們用一個簡單的應(yīng)用展示框架的基本用法和原理,更多應(yīng)用請參考官方例庫,或者直接提交 issue,我覺得有價值的會后續(xù)補(bǔ)充上去。
正文
首先是安裝:
go get -u github.com/GoCollaborate/src
然后為你的項目創(chuàng)建基本結(jié)構(gòu),創(chuàng)建好之后看起來像這樣
[Your_Project_Name]
┬
├ [core]
┬
└ example.go
├ case.json
└ main.go
然后是集群的配置,修改case.json為:
{
"caseid": "GoCollaborateStandardCase",
"cards": {
"localhost:57851": {
"ip": "localhost",
"port": 57851,
"alive": false,
"seed": false
},
"localhost:57852": {
"ip": "localhost",
"port": 57852,
"alive": true,
"seed": true
}
},
"timestamp": 1508619931,
"local": {
"ip": "localhost",
"port": 57852,
"alive": true,
"seed": true
},
"coordinator": {
"ip": "localhost",
"port": 0,
"alive": true,
"seed": false
}
}
這里有幾個參數(shù),caseid是集群的自定義id,id不同的集群之間將無法通信;而cards里面則囊括了當(dāng)前網(wǎng)絡(luò)上已知的主機(jī)地址,local作為本機(jī)地址,你可以修改為自己喜歡的端口,更多內(nèi)容請參見我寫的官方文檔,(目前只完成了英文文檔,后續(xù)會陸續(xù)補(bǔ)充中文部分,著急的同學(xué)可以先用谷歌翻譯哈!)。
然后打開剛才創(chuàng)建的example.go,給我們的計算任務(wù)寫幾個函數(shù):
package core
import (
"fmt"
"github.com/GoCollaborate/src/artifacts/task"
"github.com/GoCollaborate/src/wrappers/taskHelper"
"net/http"
)
// 任務(wù)處理器
func ExampleJobHandler(w http.ResponseWriter, r *http.Request) *task.Job {
// 創(chuàng)建一個 Job 實例
job := task.MakeJob()
// 將任務(wù)輸入 Job 的隊列
job.Tasks(&task.Task{task.SHORT,
task.BASE, "exampleFunc",
task.Collection{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4},
task.Collection{0},
task.NewTaskContext(struct{}{}), 0})
// 為當(dāng)前階段指定執(zhí)行器,這里我們簡單做一次 Map-Reduce
job.Stacks("core.ExampleTask.Mapper", "core.ExampleTask.Reducer")
// 這里大家可以根據(jù)需要為 HTTP 請求返回內(nèi)容
// ...
return job
}
// 任務(wù)調(diào)用的處理函數(shù)
func ExampleFunc(source *task.Collection,
result *task.Collection,
context *task.TaskContext) bool {
fmt.Println("Example Task Executed...")
var total int
// 計算數(shù)據(jù)集內(nèi)數(shù)據(jù)的總和
for _, n := range *source {
total += n.(int)
}
// 將總和寫入結(jié)果集
result.Append(total)
return true
}
type SimpleMapper int
func (m *SimpleMapper) Map(inmaps map[int]*task.Task) (map[int]*task.Task, error) {
// 將任務(wù)平均映射成三個子任務(wù)
return taskHelper.Slice(inmaps, 3), nil
}
type SimpleReducer int
func (r *SimpleReducer) Reduce(maps map[int]*task.Task) (map[int]*task.Task, error) {
var sum int
// 根據(jù)返回結(jié)果計算總和
for _, s := range maps {
for _, r := range (*s).Result {
sum += r.(int)
}
}
fmt.Printf("The sum of numbers is: %v \n", sum)
fmt.Printf("The task set is: %v", maps)
return maps, nil
}
然后在我們的入口文件main.go里,把剛才寫的函數(shù)都注冊到框架里:
package main
import (
"./core"
"github.com/GoCollaborate/src"
)
func main() {
mp := new(core.SimpleMapper)
rd := new(core.SimpleReducer)
collaborate.Set("Function", core.ExampleFunc, "exampleFunc")
collaborate.Set("Mapper", mp, "core.ExampleTask.Mapper")
collaborate.Set("Reducer", rd, "core.ExampleTask.Reducer")
collaborate.Set("Shared", []string{"GET", "POST"}, core.ExampleJobHandler)
collaborate.Run()
}
跑一下,看能運(yùn)行嗎?
go run main.go -mode=clbt
剛才創(chuàng)建的任務(wù)函數(shù)將被映射到
http://localhost:8080/core/ExampleJobHandler
退出程序,我們把剛才創(chuàng)建的項目文件夾復(fù)制一份,開始真正的分布式計算:
cp Your_Project_Name Your_Project_Name_Copy
在配置文件case.json內(nèi)部修改本地端口ip:
{
"caseid": "GoCollaborateStandardCase",
"cards": {
"localhost:57852": {
"ip": "localhost",
"port": 57852,
"alive": true,
"seed": true
}
},
"timestamp": 1508619931,
"local": {
"ip": "localhost",
"port": 57851,
"alive": true,
"seed": false
},
"coordinator": {
"ip": "localhost",
"port": 0,
"alive": true,
"seed": false
}
}
保存,退出,然后依次進(jìn)入不同目錄下啟動兩個項目,這里如果大家在本地運(yùn)行的話記得加個參數(shù),記得把第二個應(yīng)用的端口設(shè)為8081以免沖突哦:
go run main.go -mode=clbt -port=8081
go run main.go -mode=clbt
現(xiàn)在可以訪問:
http://localhost:8080/core/ExampleJobHandler
// and
http://localhost:8081/core/ExampleJobHandler
執(zhí)行剛才注冊的任務(wù)啦,看看控制臺,是不是輸出了什么?
然后還有一個做了一半的 UI,提供一點基本統(tǒng)計分析:
http://localhost:8080
因為是個人項目,肯定還有很多不足,最后再附上 github 鏈接 GoCollaborate ,歡迎大家提交 issue 或者拍磚,當(dāng)然愿意貢獻(xiàn)代碼的大蝦就更歡迎了,謝謝閱讀!