## 为什么我要写这篇文章
作为一枚技术 Doge,每天总免不了和不懂技术的老板 探(si) 讨(bi) 业务的实现可能性;
前一段日子,老板在深(bi) 入(jiao) 调(jia) 研(ge) 之后决定引入一个第三方 IOT 平台,通过 RESTful API 实时反馈设备的监测数据,而因为公司业务的特殊性,所获得的原始监测数据我们无法直接使用,而是必须经过一番计算。以为每位登录后台的用户显示过去一周的实时能源消耗为例,我们需要在十五秒内完成大约 60 MB (120 万条记录) 数据的提取、清洗及计算,而这显然超出了浏览器和单次 HTTP 请求所能承受的极限。这个需求让我想起了之前自己学习 Golang 练手时写过的一个分布式计算模型,于是趁此机会把它扩展了一下,写成框架[发布到社区里](https://github.com/GoCollaborate/src),有兴趣的童鞋可以 star 回去试验一下哦。
## 为什么要用 Go 重新造轮子
Golang 是 Google 在2007年发布的一门开源的静态编译型编程语言,在垃圾回收、结构类型以及并发编程的处理上拥有自己的独到之处,近年来更是成为使用频率上升速度最快的编程语言之一。 可以参考这篇[文章](http://zhuanlan.51cto.com/art/201710/554509.htm),Go 在打包编译后的性能与 Java 或 C++相似。在我们的使用中,Go 一般比 Python 要快 30 倍。同时,其天然支持的 CSP 模型在很多情况下可以免去了消息队列的使用,对高并发场景独到的处理优势,更是能够大大缩短程序员的开发时间哦。
## GoCollaborate 是什么?
很多同学看到这里可能会问了,这个框架什么?我又能用它来做什么呢?
简而言之,
> GoCollaborate 是一个提供分布式服务管理搭建的轻量级通用框架,您可以轻松地用它进行编程,构建扩展,以及创建自己的高性能分布式服务。
有相关从业经验的同学可能听说过 Apache Hadoop,Spark,Lightbend 的 Akka, 阿里的 Dubbo 以及 Facebook 的 Thrift 等等,一套工具集下来是不是感觉晕头转向呢?不要紧,我们在这姑且暂时把它当成一个轻量级的 Hadoop 好了,随着教程展开,让我们一起来体验 Golang 的神奇魅力。
下面我们用一个简单的应用展示框架的基本用法和原理,更多应用请参考官方[例库](https://github.com/GoCollaborate/examples),或者直接提交 issue,我觉得有价值的会后续补充上去。
## 正文
首先是安装:
```sh
go get -u github.com/GoCollaborate/src
```
然后为你的项目创建基本结构,创建好之后看起来像这样
```
[Your_Project_Name]
┬
├ [core]
┬
└ example.go
├ case.json
└ main.go
```
然后是集群的配置,修改`case.json`为:
```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`作为本机地址,你可以修改为自己喜欢的端口,更多内容请参见我写的[官方文档](https://hastingsyoung.gitbooks.io/gocollaborateapi/content/),(目前只完成了英文文档,后续会陆续补充中文部分,着急的同学可以先用谷歌翻译哈!)。
然后打开刚才创建的example.go,给我们的计算任务写几个函数:
```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`里,把刚才写的函数都注册到框架里:
```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()
}
```
跑一下,看能运行吗?
```sh
go run main.go -mode=clbt
```
刚才创建的任务函数将被映射到
```
http://localhost:8080/core/ExampleJobHandler
```
![image](http://upload-images.jianshu.io/upload_images/5663540-f977e3963934d0e6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
退出程序,我们把刚才创建的项目文件夹复制一份,开始真正的分布式计算:
```sh
cp Your_Project_Name Your_Project_Name_Copy
```
在配置文件`case.json`内部修改本地端口ip:
```json
{
"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以免冲突哦:
```sh
go run main.go -mode=clbt -port=8081
```
```sh
go run main.go -mode=clbt
```
现在可以访问:
```
http://localhost:8080/core/ExampleJobHandler
// and
http://localhost:8081/core/ExampleJobHandler
```
执行刚才注册的任务啦,看看控制台,是不是输出了什么?
![image](http://upload-images.jianshu.io/upload_images/5663540-a92d16dc6113d50d?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后还有一个做了一半的 UI,提供一点基本统计分析:
```
http://localhost:8080
```
<br>
因为是个人项目,肯定还有很多不足,最后再附上 github 链接 [GoCollaborate](https://github.com/GoCollaborate/src) ,欢迎大家提交 issue 或者拍砖,当然愿意贡献代码的大虾就更欢迎了,谢谢阅读!
P.S. 框架的全文文档都在[这里](https://hastingsyoung.gitbooks.io/gocollaborateapi/content/),本文的例子在[这里](https://github.com/GoCollaborate/examples/tree/master/quickstart)。
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传