关于fasthttp + K8S service负载均衡的一些心得

栏目: IT技术 · 发布时间: 4年前

内容简介:最近在做一个项目,项目中用golang 写了一个网关gateway,gateway接受来自外部的请求,并转发到后端的容器中。gateway和应用的容器都部署在同一个K8S集群当中。流程如下图gateway到pod的请求,是通过K8S的dns机制来访问service,使用的是service的endpoint的负载均衡机制。当gateway得到一个请求之后,通过解析对应的参数,然后可以判断需要转发到哪个host,例如:请求转发到service.namespace.svc.cluster.local:8080,然

最近在做一个项目,项目中用golang 写了一个网关gateway,gateway接受来自外部的请求,并转发到后端的容器中。gateway和应用的容器都部署在同一个K8S集群当中。流程如下图

关于fasthttp + K8S service负载均衡的一些心得

gateway到pod的请求,是通过K8S的dns机制来访问service,使用的是service的endpoint的负载均衡机制。当gateway得到一个请求之后,通过解析对应的参数,然后可以判断需要转发到哪个host,例如:请求转发到service.namespace.svc.cluster.local:8080,然后DNS解析会解析出对应service的clusterIp,通过service转发请求到后端的pod上(具体转发原理可以了解一下kube-proxy的原理),gateway到service的请求通过golang的 fasthttp实现,并且为了提高效率,采用的是长连接的形式。

我们现在为了实现自动化扩缩容,引入了HPA扩缩容机制,也就是说service对应的pod会根据访问量和CPU的变化进行自动的扩缩容。现在的问题是,这种方案能否在扩容之后实现负载均衡吗?答案是不能,或者说负载均衡的效果并不好(如果采用RoundRobin的负载均衡策略,多个pod并不能均匀的接受到请求),下面说一下我的分析:

我们知道,使用fasthttp作为客户端并采用长连接的时候,TPC的连接存在一个连接池,而这个连接池是如何管理的至关重要。看代码: client.go

func (c *Client) Do(req *Request, resp *Response) error {
	uri := req.URI()
	host := uri.Host()

	isTLS := false
	scheme := uri.Scheme()
	if bytes.Equal(scheme, strHTTPS) {
		isTLS = true
	} else if !bytes.Equal(scheme, strHTTP) {
		return fmt.Errorf("unsupported protocol %q. http and https are supported", scheme)
	}

	startCleaner := false

	c.mLock.Lock()
	m := c.m
	if isTLS {
		m = c.ms
	}
	if m == nil {
		m = make(map[string]*HostClient)
		if isTLS {
			c.ms = m
		} else {
			c.m = m
		}
	}
	hc := m[string(host)]
	if hc == nil {
		hc = &HostClient{
			Addr:                          addMissingPort(string(host), isTLS),
			Name:                          c.Name,
			NoDefaultUserAgentHeader:      c.NoDefaultUserAgentHeader,
			Dial:                          c.Dial,
			DialDualStack:                 c.DialDualStack,
			IsTLS:                         isTLS,
			TLSConfig:                     c.TLSConfig,
			MaxConns:                      c.MaxConnsPerHost,
			MaxIdleConnDuration:           c.MaxIdleConnDuration,
			MaxIdemponentCallAttempts:     c.MaxIdemponentCallAttempts,
			ReadBufferSize:                c.ReadBufferSize,
			WriteBufferSize:               c.WriteBufferSize,
			ReadTimeout:                   c.ReadTimeout,
			WriteTimeout:                  c.WriteTimeout,
			MaxResponseBodySize:           c.MaxResponseBodySize,
			DisableHeaderNamesNormalizing: c.DisableHeaderNamesNormalizing,
		}
		m[string(host)] = hc
		if len(m) == 1 {
			startCleaner = true
		}
	}
	c.mLock.Unlock()

	if startCleaner {
		go c.mCleaner(m)
	}

	return hc.Do(req, resp)
}

其中

hc := m[string(host)]

这一行代码就是关键。大概解释一下,httpclient当中维护了一个  map[string]*HostClient ,其中key即为host,value为hostClient对象。那这个host,即为我们请求的host。在本例中就是service.namespace.svc.cluster.local:8080,而每一个hostClient,又维护了一个TCP的连接池,这个连接池中,真正维护着TCP连接。每次进行http请求时,先通过请求的host找到对应的hostClient,再从hostClient的连接池中取一个连接来发送http请求。问题的关键就在于,map中的key,用的是域名+端口还是ip+端口的形式。如果是域名+端口,那么对应的hostClient中的连接,就会可能包含到该域名对应的各个ip的连接,而这些连接的数量无法保证均匀。但如果key是ip+端口,那么对应hostClient中的连接池只有到该ip+端口的连接。如下图:

关于fasthttp + K8S service负载均衡的一些心得

图中每一个方框代表一个hostclient的连接池,框1指的就是本例中的情况,而框2和框3指的是通过ip+端口建立连接的情况。在K8S中,service的负载均衡指的是建立连接时,会均衡的和pod建立连接,但是,由于我们pod的创建顺序有先后区别(初始的时候只有一个pod,后面通过hpa扩容起来),导致框1中的连接肯定无法做到均匀分配,因此扩容起来之后的pod,无法做到真正意义的严格的负载均衡。

那么有什么办法改进呢:

1.gateway到后端的请求是通过host(K8S的域名)通过service进行请求的,如果改成直接通过podIP进行访问,那么就可以自己实现负载均衡方案,但是这样的复杂度在于必须要自己做服务发现机制,即不能依赖K8S的service服务发现。

2.采用短连接,短连接显然没有任何问题,完全取决于service的负载均衡。但是短连接必然会影响转发效率,所以,可以采用一种长短连接结合的方式,即每个连接设置最大的请求次数或连接持续时间。这样能在一定程度上解决负载分配不均匀的问题。

以上是个人的一些理解和看法,因笔者水平有限,难免有理解错误或不足的地方,欢迎大家指出,也欢迎大家留言讨论。

------------------------------------------------------------------------------------2019.11.11更新------------------------------------------------------------------------------------------

以前的faasthttp的client里,没有 MaxConnDuration 字段,我也在github上提出了一个issue,希望作者能加上该字段,很高兴,作者已经更新,加上了该字段。参考 https://github.com/valyala/fasthttp/issues/692

这个字段的意思是连接持续最大多长时间后就会关闭,用该参数,实现了长短连接结合的方式,兼顾了效率和负载均衡的问题。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员面试金典(第5版)

程序员面试金典(第5版)

[美] Gayle Laakmann McDowell / 李琳骁、漆 犇 / 人民邮电出版社 / 2013-11 / 59.00

本书是原谷歌资深面试官的经验之作,层层紧扣程序员面试的每一个环节,全面而详尽地介绍了程序员应当如何应对面试,才能在面试中脱颖而出。第1~7 章主要涉及面试流程解析、面试官的幕后决策及可能提出的问题、面试前的准备工作、对面试结果的处理等内容;第8~9 章从数据结构、概念与算法、知识类问题和附加面试题4 个方面,为读者呈现了出自微软、苹果、谷歌等多家知名公司的150 道编程面试题,并针对每一道面试题目......一起来看看 《程序员面试金典(第5版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具