建立1个分布式系统是很困难的。它必要可扩充性、容错性、高可用性、1致性、可伸缩以及高效。为了达到这些目的,分布式系统必要许多繁冗的组件以1 种繁冗的行动协同义务。比方,Apache?Hadoop在大型集群上并行处理TB级此外数据集时,必要依靠有着高容错的文件系统(HDFS)来达到高吞 吐量。
在曩昔,每1个新的分布式系统,比方Hadoop和Cassandra,都必要建立本身的底层架构,收罗消息处理、存储、网络、容错 性和可伸缩性。光荣的是,像Apache?Mesos何等的系统,经过给分布式系统的关头建立模块提供相同把持系统的经管处事,简化了建立和经管分布式系 统的义务。Mesos抽离了CPU、存储和其它算计利润,因而开拓者开拓分布式垄断步调时也许将整个数据核心集群当做1台巨型机对待。
建立 在Mesos上的垄断步调被称为框架,它们能意图许多题目:Apache?Spark,1种风靡的集群式数据阐发东西;Chronos,1个相同cron 的存在容错性的分布式scheduler,这是两个建立在Mesos上的框架的例子。建立框架大概应用多种措辞,收罗 C++,Go,Python,Java,Haskell和?Scala。
在分布式系统用例上,比特币开采就是1个很好的例子。比特币将为生 成?acceptable?hash?的挑衅转为考证1块事件的可靠性。大概或许必要几十年,单台条记本电脑挖1块大概或许必要耗费超过150年。结果是,有许多 的“采矿池”许诺采矿者将他们的算计利润拆散起来以放慢挖矿速度。Mesosphere的1个操演生,Derek,写了1个比特币开采框架 (https://github.com/derekchiang/Mesos-Bitcoin-Miner),操作集群利润的优势来做同样的事变。在接 上去的内容中,会以他的代码为例。
1个Mesos框架有1个scheduler?和1个executor造成。scheduler?和 Mesos?master通信并选择运转甚么义务,而executor?运转在slaves上面,实行理论义务。大少数的框架实现了本身的 scheduler,并应用1个由Mesos提供的规范executors。固然,框架也大概本身定制executor。在这个例子中即会编写定制的 scheduler,并应用规范呼吁实行器(executor)运转包含咱们比特币处事的Docker镜像。
对这里的scheduler来 说,必要运转的有两种义务——one?miner?server?task?and?multiple?miner?worker?tasks。 server会和1个比特币采矿池通信,并给每个worker分派blocks。Worker会努力义务,即开采比特币。
义务理论上被封装 在executor框架中,因而义务运转象征着陈说Mesos?master在此中1个slave上面发动1个executor。由于这里应用的是规范命 令实行器(executor),因而大概指定义务是2进制可实行文件、bash脚本或许此外呼吁。由于Mesos反对Docker,因而在本例中将应用可 实行的Docker镜像。Docker是何等1种技能,它许诺你将垄断步调和它运转时必要的依靠1起打包。
为了在Mesos中应用Docker镜像,这里必要在Docker?registry中注册它们的称呼:
Default12三4 | const ( MinerServerDockerImage = "derekchiang/p2pool" MinerDaemonDockerImage = "derekchiang/cpuminer") |
而后定义1个常量,指定每个义务所需利润:
Default12三45 | const ( MemPerDaemonTask = 12八 // mining shouldn't be memory-intensive MemPerServerTask = 256 CPUPerServerTask = 1 // a miner server does not use much CPU) |
此刻定义1个真正的scheduler,对其跟踪,并确保其准确运转必要的形态:
Default12三4567八91011121三14 | type MinerScheduler struct { // bitcoind RPC credentials bitcoindAddr string rpcUser string rpcPass string // mutable state minerServerRunning bool minerServerHostname string minerServerPort int // the port that miner daemons // connect to // unique task ids tasksLaunched int currentDaemonTaskIDs []*mesos.TaskID} |
这个scheduler必须实现上面的接口:
Default12三4567八91011121三14 | type Scheduler interface { Registered(SchedulerDriver, *mesos.FrameworkID, *mesos.MasterInfo) Reregistered(SchedulerDriver, *mesos.MasterInfo) Disconnected(SchedulerDriver) ResourceOffers(SchedulerDriver, []*mesos.Offer) OfferRescinded(SchedulerDriver, *mesos.OfferID) StatusUpdate(SchedulerDriver, *mesos.TaskStatus) FrameworkMessage(SchedulerDriver, *mesos.ExecutorID, *mesos.SlaveID, string) SlaveLost(SchedulerDriver, *mesos.SlaveID) ExecutorLost(SchedulerDriver, *mesos.ExecutorID, *mesos.SlaveID, int) Error(SchedulerDriver, string)} |
此刻1起看1个回调函数:
Default12三4567八91011 | func (s *MinerScheduler) Registered(_ sched.SchedulerDriver, frameworkId *mesos.FrameworkID, masterInfo *mesos.MasterInfo) { log.Infoln("Framework registered with Master ", masterInfo)}func (s *MinerScheduler) Reregistered(_ sched.SchedulerDriver, masterInfo *mesos.MasterInfo) { log.Infoln("Framework Re-Registered with Master ", masterInfo)}func (s *MinerScheduler) Disconnected(sched.SchedulerDriver) { log.Infoln("Framework disconnected with Master")} |
Registered在scheduler?溃烂向Mesos?master注册以后被调用。
Reregistered在scheduler?与Mesos?master断开连接并且再次注册时被调用,比方,在master重启的时候。
Disconnected在scheduler?与Mesos?master断开连接时被调用。这个在master挂了的时候会发生。
今朝为止,这里仅仅在回调函数中打印了日志信息,因为关于1个像何等的繁冗框架,大少数回调函数大概空在那处。然而,下1个回调函数就是每1个框架的核心,必须要认真的编写。
ResourceOffers在scheduler?从master那处获得1个offer的时候被调用。每1个offer包含1个集群上大概给框架应用的利润列表。利润集体收罗CPU、内存、端口和磁盘。1个框架大概应用它提供的1些利润、所不利润或许1点利润都不给用。
针 对每1个offer,此刻期冀离散部分的提供的利润并选择能否必要宣布1个新的server义务或许1个新的worker义务。这里大概向每个offer 发送尽大概或许多的义务以测试最大容量,然则由于开采比特币是依靠CPU的,所以这里每个offer运转1个开采者义务并应用部分可用的CPU利润。
Default12三4567八91011 | for i, offer := range offers { // … Gather resource being offered and do setup if !s.minerServerRunning && mems >= MemPerServerTask && cpus >= CPUPerServerTask && ports >= 2 { // … Launch a server task since no server is running and we // have resources to launch it. } else if s.minerServerRunning && mems >= MemPerDaemonTask { // … Launch a miner since a server is running and we have mem // to launch one. }} |
针对每个义务都必要竖立1个对应的TaskInfo?message?,它包含了运转这个义务必要的信息。
Default12三45 | s.tasksLaunched++taskID = &mesos.TaskID { Value: proto.String("miner-server-" + strconv.Itoa(s.tasksLaunched)),} |
Task?IDs由框架选择,并且每个框架必须是唯1的。
Default12三4567八91011121三141516171八192021222三242526 | containerType := mesos.ContainerInfo_DOCKERtask = &mesos.TaskInfo { Name: proto.String("task-" + taskID.GetValue()), TaskId: taskID, SlaveId: offer.SlaveId, Container: &mesos.ContainerInfo { Type: &containerType, Docker: &mesos.ContainerInfo_DockerInfo { Image: proto.String(MinerServerDockerImage), }, }, Co妹妹and: &mesos.Co妹妹andInfo { Shell: proto.Bool(false), Arguments: []string { // these arguments will be passed to run_p2pool.py "--bitcoind-address", s.bitcoindAddr, "--p2pool-port", strconv.Itoa(int(p2poolPort)), "-w", strconv.Itoa(int(workerPort)), s.rpcUser, s.rpcPass, }, }, Resources: []*mesos.Resource { util.NewScalarResource("cpus", CPUPerServerTask), util.NewScalarResource("mem", MemPerServerTask), },} |
TaskInfo?message指定了1些关于义务的紧迫元数据信息,它许诺Mesos节点运转Docker容器,尤其会指定name、task?ID、container?information以及1些必要给容器传递的参数。这里也会指定义务必要的利润。
此刻TaskInfo已经被建立好,因而义务大概何等运转:
Default1 | driver.LaunchTasks([]*mesos.OfferID{offer.Id}, tasks, &mesos.Filters{RefuseSeconds: proto.Float64(1)}) |
在框架中,必要处理的扫尾1件事变是当开采者server关闭时会发生甚么。这里大概操作StatusUpdate?函数来处理。
在1个义务的生命周期中,针对分歧的阶段有分歧典范的形态更新。对这个框架来说,想要确保的是如果开采者server由于某种起因靡烂,系统会Kill部分开采者worker省得防范节约利润。这里是相干的代码:
Default12三4567八91011121三141516 | if strings.Contains(status.GetTaskId().GetValue(), "server") && (status.GetState() == mesos.TaskState_TASK_LOST || status.GetState() == mesos.TaskState_TASK_KILLED || status.GetState() == mesos.TaskState_TASK_FINISHED || status.GetState() == mesos.TaskState_TASK_ERROR || status.GetState() == mesos.TaskState_TASK_FAILED) { s.minerServerRunning = false // kill all tasks for _, taskID := range s.currentDaemonTaskIDs { _, err := driver.KillTask(taskID) if err != nil { log.Errorf("Failed to kill task %s", taskID) } } s.currentDaemonTaskIDs = make([]*mesos.TaskID, 0)} |
安枕无忧!经过努力,这里在Apache?Mesos上建立1个畸形义务的分布式比特币开采框架,它只用了大概三00行GO代码。这证分明应用Mesos?框架的API编写分布式系统是何等疾速和繁冗。
【via@John Walter / 由OneAPM工程师翻译】