Code Monkey home page Code Monkey logo

slaves's Introduction

My pool

为什么要有一个pool

golang的协程模型虽然支持海量的并发,但是并发到了一定量级后,调度的性能会变得很差, 当然,大部分情况下并不会有这样的问题。

那为什么还需要一个pool呢?

原因可能有以下几点:

  1. 程序员的追求,君不见别人的轮子圆又圆;
  2. 当极端情况下,例如cpu满载,服务处理不过来的时候,goroutine会大量创建,从而加剧 问题,当然这个也可以设置gouroutine的数量上限来缓解;
  3. 复用goroutine,减小GC压力;
  4. 封装并发代码,使用起来更为简单。

在调研的过程中,发现了tidb在go升级 到1.11之后,删除了自己实现的pool,于是我很好奇难道pool真的已经是不必要的了么?所以又 找了几个比较容易被用来做参考的pool实现:

  1. fasthttp
  2. ants

经过一番测试,有一个基本的结论,在goroutine的规模不至于大到导致占用太多cpu资源的时候, 原生的go语句会执行的更快,但同时内存的使用也是不受控制的。性能的损耗导致的运行时间会 增长一些,但是不至于不能忍受,所以结论是一个封装良好的Pool还是有必要的。

需要一个什么样的pool

一个pool基本的功能是获取资源,释放回资源,管理资源的生命周期。进阶一点的功能可能有管理 池的大小,预创建资源,空闲资源自动释放,允许暂时的资源溢出。

回归到我自己的需求,我需要启动一个进程,从消息队列中获取数据,然后分发到池内(由于每个 消息需要多个goroutine来完成,并且需要等待所有的结果返回后才能进行下一步,所以这里其实 是需要多个pool来一起工作的),由池内的goroutine完成消息的消费后返回给主进程,由主进程 ack消息,完成消息处理的流程。大概的示意图如下。

                            +----------+
                            |          |
                  +-------->+   Pool   |
+--------------+  |         |          |
|              |  |         +----------+
|              |  |         |          |
|    Master    +----------->+   Pool   |
|              |  |         |          |
|              |  |         +----------+
+--------------+  |         |          |
                  +-------->+   Pool   |
                            |          |
                            +----------+

然而,其实并没有必要维护多个pool对象,因为这样会有冗余数据和计算(比如池对象,池的配置,池的 自动资源配置)。所以这里其实可以引入分组的概念,多个pool共用一个池对象,共用其所有配置, 但可以等待一个单独分组的所有goroutine都完成后再返回结果。

另外,由于这个池其实是一个goroutine的池,其实并不需要从池里真正地取出资源,而只需把任务 扔进池里,等待完成就ok了,不过设计的时候还是可以保留资源对象,这样更方便组织代码。

最后,聊一下怎么设计任务的传递,Golang里能够runable的原生对象就只有function了,当然, 使用reflect也可以实现任务的执行,不过总所周知的reflect性能会比较差,所以这里不考虑 使用。那么传入的其实是一个function以及它运行需要的上下文,也就是传说中的闭包。那么返回 的呢?考虑到需要有时候需要判断任务是否成功,有时候还需要任务的结果,那么可以考虑返回一个 Job对象,包含Success和Result两个字段,分别用于保存是否成功和任务结果。

过程中碰到的问题

人非图灵,难免有不会的地方,这里记录下做这个半路碰到的问题:

这个pool性能的热点问题可能在哪

由于还没开始做,也没有高性能golang程序的经验,只能大概猜猜了:

1. channel 错误地使用或者本身存在的局限导致性能问题。

性能测试结果

这里算是一个总结吧,经过测试,在我的mbp-2019上,大概要500万以上的goroutine的时候,调度 才会有明显的压力,我感觉我这辈子可能都用不到那么多的goroutine,当然我测试的goroutine很 轻量,只使用了time.Sleep(),这个方法只会分配一次内存,也没多少cpu的操作。

而且,从原理上讲,一个pool不会运行的比golang的调度器更快,因为golang相当于只是往队列里 扔了一个任务,然后调度器去执行,所以go关键字是非常快的,主要的问题还是在大规模goroutine 的调度问题,以及内存消耗的问题,但限制goroutine的规模简单的用一个channel就可以办到了。 没必要为了这个搞一个pool。可能搞个pool主要还是为了造轮子。

当然,也有可能还有我没有了解,或者不愿意了解的部分(对,就是morestack这个runtime的调用)。

TODO

  • 较详细的性能分析,在什么情况下用池性能会好一些?内存会少吃一些?(控制变量,多少goroutine,处理什么类型的任务)

slaves's People

Contributors

xuwei0455 avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.