写点什么

梳理日常开发涉及的负载均衡

作者:AlgoRain
  • 2023-08-07
    内蒙古
  • 本文字数:3607 字

    阅读完需:约 12 分钟

梳理日常开发涉及的负载均衡

负载均衡是当前分布式微服务时代最能提及的词之一,出于对分层、解耦、弱依赖、可配置、可靠性等概念的解读,一对一的模式变得不再可信赖,千变万化的网络环境中,冗余和备份显得格外重要,稍大型的系统就会存在大量微服务、服务器、中间件资源,如何将各个资源进行平衡调度,在不浪费算力的同时保证服务的可靠性、稳定性来提供基础架构。负载均衡是一个绕不开的话题,这里只列举出开发过程中需要了解到的负载均衡框架或技术,底层涉及的算法就不在此详述。以 web 服务为例,从用户侧出发到服务端请求的处理,大概要经过以下四层负载均衡,最终实现用户请求与接口处理的对应。


DNS 解析负载均衡

DNS 主要是对域名的解析,正常域名可以添加多条主机记录,例如 www 二级域名对应记录值的 IP 地址,同一 www 二级域名可以添加多条 IP 地址的记录值,当存在多个 IP,可以通过权重配置修改 IP 的权重,修改完成后,用户发起域名请求时,根据域名应答 DNS 查询时,所有 IP 地址按照预先设置的权重进行返回不同的解析结果,将解析流量分配到不同的服务器上,从而达到负载均衡的目的。


实现效果

假设 www 域名解析地址中添加了三条记录,分别对应 3 台服务器(IP 地址分别为 IP-1、IP-2、IP-3)


  • 未开启权重配置的效果


当 Local DNS 访问云解析 DNS,云解析 DNS 将这 3 个解析记录全部返回给 Local DNS,Local DNS 再将所有的 IP 地址返回给网站访问者,网站访问者的浏览器会随机访问其中一个 IP。在无 DNS 负载均衡的权威 DNS 中,这种方法能够在一定程度上减轻单台服务器的压力,但它不能区分服务器的差异,不能反映服务器的当前运行状态。默认权重效果权重配置未开启时默认配置的是 1:1:1 权重,云解析 DNS 会根据(默认权重 1:1:1),轮询 3 个 A 记录,依次返回 3 个 IP 地址,以响应网站访问者的请求。DNS 解析结果如下所示:


用户1 访问,返回 IP-1用户2 访问,返回 IP-2用户3 访问,返回 IP-3用户4 访问,返回 IP-1用户5 访问,返回 IP-2用户6 访问,返回 IP-3……
复制代码


  • 权重设置效果


权重配置开启后,进行权重设置,在 DNS 请求应答中,IP 地址按照预先设置的权重进行返回,可以实现将解析流量按照权重进行分配。例如,将上述 3 条解析记录的权重比设置为 2:1:1 时,则 DNS 解析结果如下所示:


用户1 访问,返回 IP-1用户2 访问,返回 IP-2用户3 访问,返回 IP-3用户4 访问,返回 IP-1用户5 访问,返回 IP-1用户6 访问,返回 IP-2……
复制代码

Nginx 负载均衡

Nginx 是日常开发中使用较多的服务器,可以通过不同的负载均衡算法来解决请求量过大情况下的服务器资源分配问题。较为常见的负载均衡算法有轮询、加权轮询、IP 哈希等等。可以用来处理前端请求,或者是代理后端服务接口,通常来说,一个正常的 Nginx Linux 服务器可以达到 10w 次/秒的请求处理性能。nginx 的负载均衡策略配置很简单,在 nginx.conf 文件中配置好需要轮询的服务器,写在 http 中的 upstream 对象里:


upstream backserver{     ip_hash;     server 127.0.0.1:9090 down; (down 表示单前的server暂时不参与负载)     server 127.0.0.1:8080 weight=2; (weight 默认为1.weight越大,负载的权重就越大)     server 127.0.0.1:6060;     server 127.0.0.1:7070 backup; (其它所有的非backup机器down或者忙的时候,请求backup机器) } 
复制代码

微服务网关负载均衡

网关是系统的唯一对外的入口,介于前端端和后端之间的中间层,处理非业务功能,提供路由请求、鉴权、监控、缓存、限流等功能。承接上面的 Nginx,正常微服务集群都有多个网关作为入口,这里可以用 nginx 做请求分发,将前端请求分发到不同的网关接口上,当请求到达网关并处理完成后,此时网关需要将具体的请求转发到具体微服务,以 SpringCloud Gateway 为例,spring 提供了丰富的路由策略,可以解析到 HTTP 层的数据。因此它可以根据请求的 Path 或 Domain 甚至是 Header 作为条件,再通过本地自定义的负载均衡策略,将请求转发到不同的微服务实例上。具体代码实现过程中,可以从注册中心查询当前微服务注册的实例列表,并缓存到 redis 中间隔几秒进行更新,防止网关流量过大打垮注册中心,网关每次拿到实例列表后根据内部实现的负载均衡策略进行分发。


      @Autowired    private LoadBalanceHandler loadBalance;
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpResponse response = exchange.getResponse(); //获取原来的请求路径 String requestPath = exchange.getAttribute(FilterDict.SYSTEM_REQUEST_PATH); //自实现负载均衡策略 String instanceInfo = loadBalance.randomSelectInstance(); //如果没有服务,则直接返回报错 if (StrUtil.isEmpty(instanceInfo)) { return response.writeWith(Mono.just(GateWayFilterUtils.writeData(exchange, RecoError.GEN_SERVER_BUSY))); } //用于测试负载均衡算法对IP分配是否均衡// redisUtil.zIncrementScore("test:gateway:load:ip",instanceInfo,1); //分割地址中IP和端口 String[] serviceAddress = instanceInfo.split(StrUtil.COLON); String requestSchema = exchange.getRequest().getURI().getScheme(); //拼接URL的数据 assert ObjectUtil.isNotNull(requestPath); URI uri = UriComponentsBuilder. newInstance().scheme(requestSchema). host(serviceAddress[0].trim()).port(Integer.parseInt(serviceAddress[1].trim())) .path(requestPath).query(exchange.getRequest().getURI().getRawQuery()).build(true) .toUri(); //将拼接好的URL装入新的exchange ServerWebExchange mutateExchange = exchange.mutate().request(builder -> builder.uri(uri).build()).build(); Optional<Route> route = Optional.of(exchange.getAttribute(GATEWAY_ROUTE_ATTR)); Route newRoute = Route.async() .asyncPredicate(route.get().getPredicate()) .filters(route.get().getFilters()) .id(route.get().getId()) .order(route.get().getOrder()) .uri(uri).build(); mutateExchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute); mutateExchange.getAttributes().put(FilterDict.SYSTEM_APP_IP_ADDR, serviceAddress[0]); return chain.filter(mutateExchange); }
复制代码

微服务接口负载均衡

上面提到的网关负载是找到具体微服务来承接,不同于微服务之间接口调用的负载均衡,如 SpringCloud feign 中对 Robbin 进行了封装,在使用 Feign 时提供负载平衡的 http 客户端,如果需要配置自己的负载算法,可以自定义 Ribbon 的算法即可。还有 dubbo 的接口中也提供了负载均衡功能,类似于官方文档中提供的使用方式,只需要调整 loadbalance 相应取值即可,每种负载均衡策略取值请参见文档负载均衡


服务端服务级别<dubbo:service interface="..." loadbalance="roundrobin" />客户端服务级别<dubbo:reference interface="..." loadbalance="roundrobin" />服务端方法级别<dubbo:service interface="...">    <dubbo:method name="..." loadbalance="roundrobin"/></dubbo:service>客户端方法级别<dubbo:reference interface="...">    <dubbo:method name="..." loadbalance="roundrobin"/></dubbo:reference>
复制代码

总结

借用 dubbo 文档中对负载均衡的场景解释,负载均衡的好处有以下方面


  • 高可用性:部署服务的多个实例以确保即使一个或多个实例失败服务保持可用,负载均衡功能可用于在这些实例之间分配传入的请求确保以负载均衡方式使用每个实例的方式,还能最大限度地降低服务停机的风险。

  • 流量管理:限制指向特定服务实例的流量,以防止过载或确保公平的资源分配,负载均衡特性提供了 Round Robin、Weighted Round Robin、Random、Least Active Load Balancing 等多种负载均衡策略,可以用来实现流量控制。

  • 服务划分:将一个服务划分成多个逻辑组件,每个逻辑组件可以部署在不同的实例上,使用负载平衡以确保每个分区平衡的方式在这些实例之间分配请求,同时在实例发生故障的情况下提供故障转移功能。

  • 性能优化:负载平衡可用于优化服务的性能,通过跨多个实例分发请求可以利用可用的计算资源来缩短响应时间并减少延迟。


但有好处就有坏处,比起以往一条龙服务的形式,负载均衡的引入必然会增加系统复杂度,如果说微服务增加系统的宽度,那负载均衡无疑将这种宽度进一步放大,容易造成资源的浪费。


https://developer.aliyun.com/adc/scenario/ed4acf49b8b842aeb89311b4e152b82b?spm=a2c6h.13858375.devcloud-scene-list.60.790f4090CceGoP


发布于: 17 小时前阅读数: 19
用户头像

AlgoRain

关注

还未添加个人签名 2020-04-06 加入

还未添加个人简介

评论

发布
暂无评论
梳理日常开发涉及的负载均衡_负载均衡_AlgoRain_InfoQ写作社区