GoCollaborate: 用一百行代码写一个基于 Golang 的实时分布式计算应用

为什么我要写这篇文章

作为一枚技术 Doge,每天总免不了和不懂技术的老板 探(si) 讨(bi) 业务的实现可能性;

前一段日子,老板在深(bi) 入(jiao) 调(jia) 研(ge) 之后决定引入一个第三方 IOT 平台,通过 RESTful API 实时反馈设备的监测数据,而因为公司业务的特殊性,所获得的原始监测数据我们无法直接使用,而是必须经过一番计算。以为每位登录后台的用户显示过去一周的实时能源消耗为例,我们需要在十五秒内完成大约 60 MB (120 万条记录) 数据的提取、清洗及计算,而这显然超出了浏览器和单次 HTTP 请求所能承受的极限。这个需求让我想起了之前自己学习 Golang 练手时写过的一个分布式计算模型,于是趁此机会把它扩展了一下,写成框架发布到社区里,有兴趣的童鞋可以 star 回去试验一下哦。

为什么要用 Go 重新造轮子

Golang 是 Google 在2007年发布的一门开源的静态编译型编程语言,在垃圾回收、结构类型以及并发编程的处理上拥有自己的独到之处,近年来更是成为使用频率上升速度最快的编程语言之一。 可以参考这篇文章,Go 在打包编译后的性能与 Java 或 C++相似。在我们的使用中,Go 一般比 Python 要快 30 倍。同时,其天然支持的 CSP 模型在很多情况下可以免去了消息队列的使用,对高并发场景独到的处理优势,更是能够大大缩短程序员的开发时间哦。

GoCollaborate 是什么?

很多同学看到这里可能会问了,这个框架什么?我又能用它来做什么呢?

简而言之,

GoCollaborate 是一个提供分布式服务管理搭建的轻量级通用框架,您可以轻松地用它进行编程,构建扩展,以及创建自己的高性能分布式服务。

有相关从业经验的同学可能听说过 Apache Hadoop,Spark,Lightbend 的 Akka, 阿里的 Dubbo 以及 Facebook 的 Thrift 等等,一套工具集下来是不是感觉晕头转向呢?不要紧,我们在这姑且暂时把它当成一个轻量级的 Hadoop 好了,随着教程展开,让我们一起来体验 Golang 的神奇魅力。

下面我们用一个简单的应用展示框架的基本用法和原理,更多应用请参考官方例库,或者直接提交 issue,我觉得有价值的会后续补充上去。

正文

首先是安装:

go get -u github.com/GoCollaborate/src

然后为你的项目创建基本结构,创建好之后看起来像这样

[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
    }
}

这里有几个参数,caseid是集群的自定义id,id不同的集群之间将无法通信;而cards里面则囊括了当前网络上已知的主机地址,local作为本机地址,你可以修改为自己喜欢的端口,更多内容请参见我写的官方文档,(目前只完成了英文文档,后续会陆续补充中文部分,着急的同学可以先用谷歌翻译哈!)。

然后打开刚才创建的example.go,给我们的计算任务写几个函数:

package core

import (
    "fmt"
    "github.com/GoCollaborate/src/artifacts/task"
    "github.com/GoCollaborate/src/wrappers/taskHelper"
    "net/http"
)

// 任务处理器
func ExampleJobHandler(w http.ResponseWriter, r *http.Request) *task.Job {
    // 创建一个 Job 实例
    job := task.MakeJob()
    // 将任务输入 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})
    // 为当前阶段指定执行器,这里我们简单做一次 Map-Reduce
    job.Stacks("core.ExampleTask.Mapper", "core.ExampleTask.Reducer")
    
    // 这里大家可以根据需要为 HTTP 请求返回内容
    // ...
    
    return job
}

// 任务调用的处理函数
func ExampleFunc(source *task.Collection,
    result *task.Collection,
    context *task.TaskContext) bool {

    fmt.Println("Example Task Executed...")
    
    var total int
    // 计算数据集内数据的总和
    for _, n := range *source {
        total += n.(int)
    }
    
    // 将总和写入结果集
    result.Append(total)
    return true
}

type SimpleMapper int

func (m *SimpleMapper) Map(inmaps map[int]*task.Task) (map[int]*task.Task, error) {
    // 将任务平均映射成三个子任务
    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
    
    // 根据返回结果计算总和
    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里,把刚才写的函数都注册到框架里:

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()
}

跑一下,看能运行吗?

go run main.go -mode=clbt

刚才创建的任务函数将被映射到

http://localhost:8080/core/ExampleJobHandler
image

退出程序,我们把刚才创建的项目文件夹复制一份,开始真正的分布式计算:

cp Your_Project_Name Your_Project_Name_Copy

在配置文件case.json内部修改本地端口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
    }
}

保存,退出,然后依次进入不同目录下启动两个项目,这里如果大家在本地运行的话记得加个参数,记得把第二个应用的端口设为8081以免冲突哦:

go run main.go -mode=clbt -port=8081
go run main.go -mode=clbt

现在可以访问:

http://localhost:8080/core/ExampleJobHandler
// and 
http://localhost:8081/core/ExampleJobHandler

执行刚才注册的任务啦,看看控制台,是不是输出了什么?

image

然后还有一个做了一半的 UI,提供一点基本统计分析:

http://localhost:8080


因为是个人项目,肯定还有很多不足,最后再附上 github 链接 GoCollaborate ,欢迎大家提交 issue 或者拍砖,当然愿意贡献代码的大虾就更欢迎了,谢谢阅读!

P.S. 框架的全文文档都在这里,本文的例子在这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,590评论 18 139
  • 承载量是分布式系统存在的原因 当一个互联网业务获得大众欢迎的时候,最显著碰到的技术问题,就是服务器非常繁忙。当每天...
    XYLY阅读 1,457评论 1 48
  • 本文转载自http://geek.csdn.net/news/detail/112672 WeTest导读 我们常...
    shineegirl阅读 1,540评论 0 26
  • 听说 大自然正在孕育一场风暴 于是我来到这片滩涂 逢一个叫“玛娃”的孩子 看不见移动的浮萍 河流平静成了湖泊 水天...
    诗姬阅读 158评论 2 5
  • 研磨 居住地:湖北利川 身 高:168cm 职 业:经 商 初始体重:162 斤 目前体重:133 斤 初始腰围:...
    瘦朵朵教练阅读 352评论 0 0