Go routine 编排框架:oklog/run 包

问题引入

oklog/run 包提供了一套非常简单、易用的 Go routine 编排框架。在介绍 oklog/run 前,我们先考虑以下问题:

假设我们有四个 Go routine 组件,如图所示,分别是运行一个状态机 sm.Run 、启动一个 HTTP 服务器、执行定时任务 cronJobs(sm) 读取状态机状态、和运行信号监听器。每个 Go routine 组件互相独立运行。

问题在于,我们如何将各个组件作为一个整体运行,并有序地结束?

对于每一个 Go routine 组件,我们都有相应的办法来执行结束操作。状态机通过 Context 对象,HTTP 服务器通过调用 Listener 的 Close 方法,定时任务和监听器通过 channel。当一个组件结束的时候,需要通知其他组件有序执行结束操作。这个问题的解决方法可以用 Actor 模型来描述。每个 Go routine 都是一个 actor,互相独立,互相之间只能通过 message 通信。oklog/run 包实现了 Actor 模型,能非常简洁的实现 Go routine 编排功能。

oklog/run 包介绍

oklog/run 包非常简单,只有一个类型,两个方法,共 60 行代码。其中 Group 是一组 actor,通过调用 Add 方法将 actor 添加到 Group 中。

type Group
func (g *Group) Add(execute func() error, interrupt func(error))
func (g *Group) Run() error
type Group struct {
    actors []actor
}

func (g *Group) Add(execute func() error, interrupt func(error)) {
    g.actors = append(g.actors, actor{execute, interrupt})
}

每个 actor 有两个方法:execute 和 interrupt。execute 完成 Go routine 的计算任务,interrupt 结束 Go routine 并退出。

type actor struct {
    execute   func() error
    interrupt func(error)
}

调用 Run 方法后会启动所有 Go routine(或者称为 actor),并等待第一个结束的 Go routine(无论正常退出或因为异常终止)。一旦捕获到第一个结束信号,会依次结束其他 Go routine 直到所有 Go routine 完全退出。

func (g *Group) Run() error {
    if len(g.actors) == 0 {
        return nil
    }

    // Run each actor.
    errors := make(chan error, len(g.actors))
    for _, a := range g.actors {
        go func(a actor) {
            errors <- a.execute()
        }(a)
    }

    // Wait for the first actor to stop.
    err := <-errors

    // Signal all actors to stop.
    for _, a := range g.actors {
        a.interrupt(err)
    }

    // Wait for all actors to stop.
    for i := 1; i < cap(errors); i++ {
        <-errors
    }

    // Return the original error.
    return err
}

使用例子

下面例子定义了三个 actor,前两个 actor 一直等待。第三个 actor 在 3s 后结束退出。引起前两个 actor 退出。

package main

import (
    "fmt"
    "github.com/oklog/run"
    "time"
)

func main() {
    g := run.Group{}
    {
        cancel := make(chan struct{})
        g.Add(
            func() error {

                select {
                case <- cancel:
                    fmt.Println("Go routine 1 is closed")
                    break
                }

                return nil
            },
            func(error) {
                close(cancel)
            },
        )
    }
    {
        cancel := make(chan struct{})
        g.Add(
            func() error {

                select {
                case <- cancel:
                    fmt.Println("Go routine 2 is closed")
                    break
                }

                return nil
            },
            func(error) {
                close(cancel)
            },
        )
    }
    {
        g.Add(
            func() error {
                for i := 0; i <= 3; i++ {
                    time.Sleep(1 * time.Second)
                    fmt.Println("Go routine 3 is sleeping...")
                }
                fmt.Println("Go routine 3 is closed")
                return nil
            },
            func(error) {
                return
            },
        )
    }
    g.Run()
}

打印结果:

Go routine 3 is sleeping...
Go routine 3 is sleeping...
Go routine 3 is sleeping...
Go routine 3 is closed
Go routine 2 is closed
Go routine 1 is closed

参考资料

Ways To Do Things - Peter Bourgon

Go routine 编排框架:oklog/run 包的更多相关文章

  1. Spring 框架的架包分析、功能作用、优点,及jar架包简介

    Spring 框架的架包详解    Spring的作用     Spring的优势  由于刚搭建完一个MVC框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或 ...

  2. SSH框架整合jar包时的注意事项

    SSH框架整合jar包时的注意事项: 在将三个框架所需的jar整合到一起后,要看一下有没有相同类型但是版本不同的jar包,如果有的话,需要把低版本的jar包删除掉,否则会报错.我这里整合的时候java ...

  3. 初识 Knative: 跨平台的 Serverless 编排框架

    Knative 是什么 Knative 是 Google 在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 框架.Knativ ...

  4. Spring MVC 框架的架包分析,功能作用,优点

    由于刚搭建完一个MVC框架,决定分享一下我搭建过程中学习到的一些东西.我觉得不管你是个初级程序员还是高级程序员抑或是软件架构师,在学习和了解一个框架的时候,首先都应该知道的是这个框架的原理和与其有关j ...

  5. Python基于Flask框架配置依赖包信息的项目迁移部署小技巧

    一般在本机上完成基于Flask框架的代码编写后,如果有接口或者数据操作方面需求需要把代码部署到指定服务器上. 一般情况下,使用Flask框架开发者大多数都是选择Python虚拟环境来运行项目,不同的虚 ...

  6. 手把手安装Laravel框架(permissions扩展包)实现RBAC权限---以及一些安装时的ERROR

    a.依赖管理工具,框架,环境 1.composer 2.laravel(我的是5.5) 3.PHP(我的7.2),MySql(我的5.7) b,安装 1.首先需要安装一个干净的 Laravel 项目, ...

  7. Struts2+Hibernate+Spring(SSH)三大框架整合jar包

    Struts2 + Spring3 + Hibernate3 框架整合 1. 每个框架使用 (开发环境搭建 )* 表现层框架 struts2 1) jar包导入: apps/struts2_blank ...

  8. c#基于事件模型的UDP通讯框架(适用于网络包编解码)

    之前写过一篇关于c#udp分包发送的文章 这篇文章里面介绍的方法是一种实现,可是存在一个缺点就是一个对象序列化后会增大非常多.不利于在网络中的传输. 我们在网络中的传输是须要尽可能的减小传送的数据包的 ...

  9. Gin-Go学习笔记八:Gin-Web框架 常用的包

    常用的包 1>     在java,.net,php,node.js等语言常常会使用到包的概念.包的使用,可以加快项目的进度的开发,以及更好的实现项目的效果.我在网上查到了包的作用如下: 1.包 ...

随机推荐

  1. MYSQL学习系列

    1.myslq5.7安装以及root密码找回 2.mysql之innodb存储引擎介绍 3.mysql之innodb存储引擎---数据存储结构 4.mysql之innodb存储引擎---BTREE索引 ...

  2. 理解Yarn的执行流程和组件作用

    Yarn引入案例 1.学生找院长报到,院长给学生一个学号 2.院长比较忙,继续找主任处理学生事务 3.系主任找院办给学生分配资源(书本) 4.主任找张老师教授java 5.张老师给学生安排座位 6.学 ...

  3. 把windows下的压缩包放到Linux目录下去

    今天在自学redis时出现了问题,因为楼主linux也是空白纸,前几天安装了Linux后就只会基本的命令,其他的一概不通啊,所以当redis要在Linux中用时就傻眼了,索性就在windows中下载了 ...

  4. spring中的XML Catalog配置

    XML Catalog配置: 1.window-->preferences-->XML Catalog 2.如图: 点击File System,选择下图中的最后一个 图二中的key时sch ...

  5. Net基础篇_学习笔记_第十二天_面向对象继承(命名空间 、值类型和引用类型)

    命名空间可以认为类是属于命名空间的. 解决类的重名问题,可以看做类的“文件夹”如果在当前项目中没有这个类的命名空间,需要我们手动的导入这个类所在的命名空间.1).用鼠标去点2).alt+shift+F ...

  6. Java第三次作业第二题

    2. [请复制本程序,作为java程序代码,进行编译,补充填写缺失代码部分,并实现题目要求功能,从而获得空白填写所需的内容.] 定义3个线程,模拟红绿灯的效果 一个线程控制画一个实心红圆 一个线程控制 ...

  7. 深入理解JVM内存分配策略

    理解JVM内存分配策略 三大原则+担保机制 JVM分配内存机制有三大原则和担保机制 具体如下所示: 优先分配到eden区 大对象,直接进入到老年代 长期存活的对象分配到老年代 空间分配担保 对象优先在 ...

  8. zookeeper伪集群

    Zookeeper运行需要java环境,需要安装jdk,建议本地下载好需要的安装包然后上传到服务器上面,服务器上面下载速度太慢. 上传 [root@192 ~]# java -version java ...

  9. [Spark] 04 - HBase

    BHase基本知识 基本概念 自我介绍 HBase是一个分布式的.面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”. ...

  10. BOM之window核心模块

    Window对象包含以下五大核心:document,screen,navigator,history,location. 一     document 文档 document包含了浏览器对标准DOM实 ...