⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 cnblogs.com/zhoujie/p/kong4.html 「zhoujie」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

负载均衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最佳化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

使用带有负载均衡的多个服务器组件,取代单一的组件,可以通过冗余提高可靠性。负载均衡服务通常是由专用软体和硬件来完成。

对于互联网服务,负载均衡器通常是一个软体程序,这个程序侦听一个外部端口,互联网用户可以通过这个端口来访问服务,而作为负载均衡器的软体会将用户的请求转发给后台内网服务器,内网服务器将请求的响应返回给负载均衡器,负载均衡器再将响应发送到用户,这样就向互联网用户隐藏了内网结构,阻止了用户直接访问后台(内网)服务器,使得服务器更加安全,可以阻止对核心网络栈和运行在其它端口服务的攻击。

当所有后台服务器出现故障时,有些负载均衡器会提供一些特殊的功能来处理这种情况。例如转发请求到一个备用的负载均衡器、显示一条关于服务中断的消息等。负载均衡器使得 IT 团队可以显著提高容错能力。它可以自动提供大量的容量以处理任何应用程序流量的增加或减少。

对于核心 api,需要保证搞可靠性,那么就要对于该 api 有多个 backend service,即实际后端对该 api 有多个服务的节点;那么最好在 api-gateway 即 kong 这一层实现负载均衡。   Kong为多个后端服务提供了多种负载平衡请求方式:一种基于DNS的简单方法,以及一种更加动态的环平衡器,该方法还允许在不需要DNS服务器的情况下进行服务注册。

基于DNS的负载均衡

当使用基于DNS的负载均衡时,后端服务的注册是在Kong以外完成的,而Kong仅接收来自DNS服务器的更新。 如果名称解析为多个IP地址,并且主机名未解析为上游名称或名称,则每个使用包含hostname(而不是IP地址)的host定义的service都将自动使用基于DNS的负载平衡你的DNS hosts文件。 DNS记录ttl设置(生存时间)决定信息刷新的频率。当使用0的ttl时,每个请求都将使用自己的DNS查询来解析。很明显,这会导致性能下降,但更新/更改的延迟将非常低。

A记录

A记录包含一个或多个IP地址。因此,当主机名解析为A记录时,每个后端服务都必须具有自己的IP地址。 由于没有weight信息,因此所有条目在负载平衡器中将被视为具有相同的权重,并且平衡器(balancer)将进行简单的循环。

SRV记录

SRV记录包含所有IP地址的权重(weight)和端口(port)信息。后端服务可以通过IP地址和端口号的唯一组合来识别。因此,单个IP地址可以在不同的端口上托管同一服务的多个实例。 由于weight信息可用,每个条目将在负载均衡器中获得自己的权重,并执行加权循环。 同样,任何给定的端口信息都将被来自DNS服务器的端口信息覆盖。如果服务具有host = myhost.com和port = 123的属性,并且myhost.com解析为具有127.0.0.1:456的SRV记录,则该请求将被代理到http://127.0.0.1:456/somepath,因为123端口将被456覆盖。

DNS优先级

DNS解析器将按顺序解析以下记录类型:

  • 上次解析的最后一次成功类型(LAST)
  • SRV记录
  • A记录
  • CNAME记录

该顺序可通过dns_order配置属性进行配置。 解析不同记录类型的顺序。 LAST类型表示上次成功查找的类型(用于指定的名称)。格式是一个(不区分大小写)逗号分隔的列表。 默认值:LAST,SRV,A,CNAME

DNS警告(DNS caveats)

  • 无论何时刷新DNS记录,都会生成一个列表以正确处理权重。尽量保持权重为对方的倍数以保持算法的高效性,例如,17和31的2个权重将导致具有527个项目的结构,而权重16和32(或其最小的相对对应项1和2)将导致在只有3个条目的结构中,尤其是具有非常小(甚至0)ttl值的结构。
  • 在这些情况下,某些域名服务器不会返回所有条目(由于UDP数据包的大小)(例如Consul最多返回3个),给定的Kong节点将只使用由名称服务器提供的少数上游服务实例。在这种情况下,由于名称服务器提供的信息有限,Kong节点实际上不了解某些实例,因此上游实例池可能会不一致地加载。为了缓解这种情况,可以使用不同的名称服务器,使用IP地址而不是名称,或者确保使用足够的Kong节点来继续使用所有上游服务。
  • 当名称服务器返回3 name error时,那么对于Kong来说这是一个有效的响应。如果这是意外,请首先验证是否正在查询正确的name,然后检查您的nameserver配置。
  • 从DNS记录(A或SRV)中初始选择IP地址不是随机的。因此,当使用ttl为0的记录时,nameserver应该随机记录条目。

环平衡器(ring-balancer)

当使用环平衡器时,添加和删除后端服务将由Kong处理,并且不需要DNS更新。kong将担任服务登记。节点可以通过一个HTTP请求added/deleted,并立即start/stop接收流量。 配置环平衡器是通过上游和目标实体完成的

target:具有后端服务驻留端口号的IP地址或主机名,例如。 “192.168.100.12:80”。每个target都会得到一个额外的权重来指示它获得的相对负载。 IP地址可以是IPv4和IPv6两种格式。

upstream:可以在路由主机字段中使用的'virtual hostname',例如,上游命名的weather.v2.service将从具有host = weather.v2.service的服务获得所有请求

上游(upstream)

每个upstream都有自己的环形平衡器。每个upstream可以有许多target条目附加到它,代理到'virtual hostname'的请求将在target上进行负载平衡。环形平衡器具有预定义(pre-defined)数量的槽(number of slots),并且基于目标权重,槽(slots)被分配给上游的目标。

添加和删除目标可以通过Admin API上的简单HTTP请求完成。这个操作相对便宜。改变上游本身更昂贵,因为例如当槽的数量改变时平衡器将需要重建。

自动重建平衡器的唯一情况是清理目标历史记录时;除此之外,它只会在改变时重建。

在平衡器内部有(从1到slots)的位置,它们随机分布( randomly distributed)在环上。在运行时需要随机性来调用环平衡器。轮子上的简单循环(位置)将为目标提供良好的分布式加权循环,同时在插入/删除目标时也具有廉价的操作。

每个目标使用的插槽数量(至少)应该在100个左右,以确保插槽正确分布。例如。对于预期最多8个目标,即使初始设置仅包含2个目标,上游应至少定义为slot = 800。

这里的折衷是,插槽数量越多,随机分布越好,但更改更为昂贵(添加/删除目标)

有关添加和操作上游的详细信息,请参阅Admin API

目标(target)

由于上游保留了更改历史记录,目标只能添加,不能修改或删除。要更改目标,只需为目标添加一个新条目,然后更改重量值。最后一个条目是将要使用的条目。因为这样的设置权重= 0将禁用目标,有效地从平衡器中删除它。有关添加和操作目标的详细信息,请参阅Admin API参考的目标部分。

当活动条目比活动条目多10倍时,目标将自动清除。清洁将涉及重建平衡器,因此比添加目标条目更昂贵。 目标也可以具有主机名而不是IP地址,在这种情况下,名称将被解析,所有找到的条目将被单独添加到环形平衡器中,例如,添加api.host.com:123且权重= 100。名称'api.host.com'解析为具有2个IP地址的A记录。然后这两个IP地址将被添加为目标,每个获得weight = 100和端口123.注意:权重用于单个条目,而不是整个! 它是否会解析为SRV记录,然后DNS记录中的端口和权重字段将被拾取,并且会否定给定的端口123和权重= 100。 平衡器将遵守DNS记录的ttl设置和重新查询,并在平衡器到期时更新。 例外情况:当DNS记录的ttl = 0时,主机名将被添加为具有指定权重的单个目标。在对该目标的每个代理请求时,它将再次查询名称服务器。

平衡算法

默认情况下,环平衡器将使用加权循环方案。另一种方法是使用基于散列的算法。散列的输入可以是none,consumer,ip或header。如果设置为none,则将使用加权循环方案,并且散列将被禁用。

有两种选择,一种primary 和 一种回退(fallback),以防primary失败(例如,如果primary设置为consumer,但没有consumer通过验证)

不同的散列选项:

  • none:不要使用散列,而是使用weighted-round-robin(默认)。
  • consumer:使用消费者ID作为散列输入。如果没有消费者ID可用(如果使用外部身份验证,如ldap),此选项将回退到凭证ID。
  • ip:远程(始发)IP地址将用作输入。在使用此设置时,查看确定真实IP的配置设置。
  • header:使用指定的标题(在hash_on_header或hash_fallback_header字段中)作为散列的输入。

哈希算法基于'一致哈希'(或'ketama原理'),它确保当平衡器通过改变目标(添加,移除,失败或改变权重)而被修改时,只有最小数量的哈希损失发生。这将最大化上游缓存命中。

有关确切设置的更多信息,请参阅Admin API参考的上游upstream部分。

平衡警告(Balancing caveats)

环平衡器设计为既可以在单个节点上工作,也可以在群集中工作。对于加权循环算法没有太大的区别,但是当使用基于散列的算法时,重要的是所有节点构建完全相同的环平衡器以确保它们都工作一致。要做到这一点,平衡器必须以确定性的方式构建。

不要在平衡器中使用主机名称,因为平衡器可能会/会慢慢发生分歧,因为DNS ttl只有第二精度,更新取决于实际请求名称的时间。最重要的是一些域名服务器没有返回所有条目的问题,这加剧了这个问题。因此,在Kong群集中使用哈希方法时,只能通过IP地址添加目标实体,而不能通过名称添加target实体。

当选择你的散列输入时,确保输入具有足够的方差以得到散布良好的散列。哈希将使用CRC-32摘要进行计算。例如,如果您的系统有成千上万的用户,但只有少数用户(每个平台定义了3个用户:Web,iOS和Android),那么挑选consumer散列输入是不够的,通过设置使用远程IP地址对于ip的哈希将提供更多的输入差异,从而更好地分配哈希输出

蓝绿部署(Blue-Green Deployments)

使用环形平衡器,可以轻松地为一项服务策划一个蓝绿色部署。切换目标基础架构只需要服务上的PATCH请求,即可更改其主机值。

设置“blue”环境,运行version 1 of the address service:

# create an upstream
$ curl -X POST http://kong:8001/upstreams \
--data "name=address.v1.service"

# add two targets to the upstream
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
--data "target=192.168.34.15:80"
--data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
--data "target=192.168.34.16:80"
--data "weight=50"

# create a Service targeting the Blue upstream
$ curl -X POST http://kong:8001/services/ \
--data "name=address-service" \
--data "host=address.v1.service" \
--data "path=/address"

# finally, add a Route as an entry-point into the Service
$ curl -X POST http://kong:8001/services/address-service/routes/ \
--data "hosts[]=address.mydomain.com"

在部署version 2 of the address service之前,请设置“green”环境:

# create a new Green upstream for address service v2
$ curl -X POST http://kong:8001/upstreams \
--data "name=address.v2.service"

# add targets to the upstream
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=100"
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=100"

要激活blue/green开关,现在只需要更新服务:

# Switch the Service from Blue to Green upstream, v1 -> v2
$ curl -X PATCH http://kong:8001/services/address-service \
--data "host=address.v2.service"

主机头设置为address.mydomain.com的传入请求现在由Kong代理到新目标; 1/2的请求将转到http://192.168.34.17:80/address(权重= 100),另一半将转到http://192.168.34.18:80/address(权重= 100 )。

与往常一样,通过Kong Admin API进行的更改是动态的,并且会立即生效。不需要重新加载或重新启动,并且没有进行中的请求将被丢弃。

金丝雀版本(Canary Releases)

使用环形平衡器,可以精确调整目标重量,从而实现平稳.。

使用一个非常简单的2 target示例:

# first target at 1000
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=1000"

# second target at 0
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=0"

通过重复请求,但每次改变权重,流量将缓慢路由到另一个target。例如,将其设置为10%:

# first target at 900
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.17:80"
--data "weight=900"

# second target at 100
$ curl -X POST http://kong:8001/upstreams/address.v2.service/targets \
--data "target=192.168.34.18:80"
--data "weight=100"

通过Kong Admin API进行的更改是动态的,并会立即生效。不需要重新加载或重新启动,并且没有进行中的请求将被丢弃。

KONG负载均衡实现

实际在操作过程中,采用的是 kong 的 Ring-balancer 做负载均衡。

使用 Kong Community Edition(社区版 v0.13)来搭建一个负载均衡器,由于 Kong 是基于 Openresty 的,而 Openresty 又是 Nginx 的二次封装,所有很多配置项和 Nginx 类似。

来看一个较为典型的 Nginx 负载均衡配置:

upstream hello {
server localhost:3000 weight=100;
server localhost:3001 weight=50;
}

server {
listen 80;
location /hello {
proxy_pass http://hello;
}
}

nginx 监听来自本地 80 端口的请求,如果路径与 /hello 匹配,便将请求原封不动的转发到名称为 hello 的upstream,而该 upstream 我们配置了一个负载均衡器,会路由到本地的 3000 端口和 3001 端口。

接下来便可以针对 Kong 进行负载均衡的配置了。

配置 upstream 和 target

创建一个名称 hello 的 upstream

curl -X POST http://localhost:8001/upstreams --data "name=hello"

为 hello 添加两个负载均衡节点

curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3000" --data "weight=100"
curl -X POST http://localhost:8001/upstreams/hello/targets --data "target=localhost:3001" --data "weight=50"

如上的配置对应了 Nginx 的配置:

upstream hello {
server localhost:3000 weight=100;
server localhost:3001 weight=50;
}

配置 service 和 route

使用 Kong v0.13之前版本的用户可能会接触过 api 这个概念,但是在 Kong v0.13.0 中,已经被废除了,取而代之的是 service 和 route 的配置。

配置一个 service

curl -X POST http://localhost:8001/services --data "name=hello" --data "host=hello"

host 的值便对应了 upstream 的名称,配置成功后会返回生成的 service 的 id,返回结果:8695cc65-16c1-43b1-95a1-5d30d0a50409。

为上面的 service 配置路由信息

curl -X POST http://localhost:8001/routes --data "paths[]=/hello" --data "service.id=8695cc65-16c1-43b1-95a1-5d30d0a50409"

请求路径包含 /hello 的请求都会被转移到对应的 service 进行处理。

如上的配置便对应了Nginx的配置:

location /hello {
proxy_pass http://hello;
}

测试 Kong 的负载均衡

curl http://localhost:8000/hello/hi

因为复杂均衡的原因,需要多测试几次,多次 curl 之后结果如下:

3000
3000
3000
3000
3000
3000
3001
3001
3001
3000
3001

reference:

https://getkong.org/docs/0.13.x/loadbalancing/

https://getkong.org/docs/0.13.x/configuration/

文章目录
  1. 1. 基于DNS的负载均衡
    1. 1.1. A记录
    2. 1.2. SRV记录
    3. 1.3. DNS优先级
    4. 1.4. DNS警告(DNS caveats)
  2. 2. 环平衡器(ring-balancer)
    1. 2.1. 上游(upstream)
    2. 2.2. 目标(target)
    3. 2.3. 平衡算法
    4. 2.4. 平衡警告(Balancing caveats)
  3. 3. 蓝绿部署(Blue-Green Deployments)
  4. 4. 金丝雀版本(Canary Releases)
  5. 5. KONG负载均衡实现
    1. 5.1. 配置 upstream 和 target
    2. 5.2. 配置 service 和 route
    3. 5.3. 测试 Kong 的负载均衡