为什么Go服务容器化之后延迟变高

为什么Go服务容器化之后延迟变高

在今年的gopherchina上发现有两三个topic都在说Go服务容器化的P(如果你不知道P是什么,没有关系,马上就会解释)数量的问题,现象是被容器限制了4C的服务跑在96C的机器上,相比于物理机部署的延时高了很多。原因是因为Go启动的时候读的宿主机的CPU核心数,启动了96个P,96个P都在找G去执行但是只有4C的处理时间,导致大量CPU耗费在找G和上下文切换。解决方案就是引入uber的automaxprocs这个库就可以了。

我回公司之后通过trace发现自己的2C服务确实起了很多P,于是先在自己业务上加上了这个库,经过一周的观察发现并没有什么变化,后来得的结论是服务的QPS不够高,不能引发问题(后面会详细解释)。

后来同事的一个高QPS服务也遇到了这个问题,加上了这个库马上突破了那时候的QPS限制。再后来公司的框架升级,准备把这个库默认启动,我就对框架做了一系列的压测,对这个问题又深入了,所以写这篇文章总结一下。

因为事先已经知道结果了,所以你看的时候可能感觉是反过来了,我是从结论出发一步步推演问题原因。

一、Go 调度知识扫盲

在就不深入讲解GMP的调度模型,为了不影响初学者阅读,我只是简单介绍一下,理解的可以直接跳过这一小段。

Go是在runtime实现的任务调度,每个go关键字都会创建一个用户任务,称之为G;但是线程才是操作系统调度的最小单位,用户任务需要系统线程去执行,那么就需要创建系统线程,我们称之为M;这个就是Go 1.0及其之前的调度器。

后来感觉不行了,如果把所有的任务放在全局的话,每次执行一个任务都要加锁、以前的资源都是挂在M上的,陷入内核态的时候保存和恢复浪费内存以及G没有亲和性可言(在一个M上运行的G被挂起之后,下次执行很大概率不会到这个M,当时的缓存也就没有用了)。

我们可以先暂停一下看下linux的任务调度是怎么样的,简单的一句话:进程是资源的最小单位,线程是调度的最小单位。Go的runtime把G当作最小调度单位了,那么在加一个资源的最小单位不就得了么,这个时候在M和G之间的P就呼之欲出了,通过P这个结构来看下P的作用:

type p struct {
   
  // ...
  mcache *mcache
  pcache pageCache
  runq [256]guintptr
  runnext guintptr
 
  // ...
  sudogcache []*sudog
  sudogbuf [128]*sudog
   
  // ...
  mspancache struct {
   
    len int
    buf [128]*mspan
  }
  // ...
}

主要的作用有两个:

1、 通过增加本地队列减少锁操作:有个本地队列runq以及优先级最高的runnext,也就是说一个P最多包含257个G;

2、 内存管理:mcache、pcache以及mspancache这些东西一看就是管理内存的,现在可以简单的理解为他们管理的内存大小不一样,内存管理名词太多,再说能说到天亮;

那么有两个问题自然而然的出现了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kbc5yxZp-1631681285873)(./wenhao.png)]

问题一、如果一个P上的G超过了257个咋办?

答:G还有一个全局队列,如果超过了就会放在全局队列上,全局队列是个链表,无限长。

问题二、如果一个P上的所有G执行完了咋办?

答:这个就要从P寻找可用的G来说了,对应的函数是findrunnable(至少记住这个名字,后面还会看到),优先级如下:

1、 如果P的runnext存在有就用这个G【不加锁】,如果没有往下做

2、 从P的local queue中顺序拿一下【不加锁】,如果local queue为空没有往下走

3、 从全局队列中拿一个【加锁】,顺便从全局队列拿出128个给这个P(如果全局队列中没有128个就都给它,省的以后总来要),如果全区队列为空就继续往下走

4、 从netpoll中拿,如果有返回第一个,剩下的通过injectglist放到全局队列中【加锁】,如果没有往下走

5、 通过runqsteal函数随机从其他的P的本地队列中偷一半给当前P,然后将偷到的最后一个返回,如果还没有,那就算了,将P设置为空闲状态并且将M从P中拿下来,但是不会kill M。

这两个问题之后,你可能还会有两个问题:

问题三、系统线程不会被kill么

答:确实是这样的,如果开启100系统线程空闲之后,那岂不是这个100个系统线程都不会被kill就在那静静的等着。

所以Go服务最好不要用太多的CPU,几百个线程就将近一个G的内存了,你这台机器要是再部署多个就尴尬了。

问题四、goroutine没有优先级么

答:没有优先级,你

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值