栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

源码分析及实践测试OpenFeign负载均衡

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

源码分析及实践测试OpenFeign负载均衡

1 缘起

补课吧。
之前一直着急往前赶进度,
只梳理了微服务架构以及如何使用这些架构中的组件,
然而,并不了解运作原理,
我依旧还是太弱了,经不起大风大浪,
所以,想使自己强壮一些,继续研究一下源码。
还有另外一个原因,最近看了K8S,并实践了K8S部署SpringBoot服务,
发现,可以直接使用K8S进行负载均衡,
于是,又想到,Spring自己也有负载均衡,是如何实现的?
所以,有了这篇文章。

2 源码分析 2.1 如何找到入口

我们知道,
当开发同学使用SpringBoot进行开发时,
没有配置某些Bean,但是,可以直接使用这些Bean,
就说明,SpringBoot在启动时自动装配了这些Bean,
OpenFeign的负载均衡是同样的的道理,
开发同学没有配置负载均衡策略,但是,使用OpenFeign可以自动进行均衡,
因此,OpenFeign的负载均衡是通过自动装配自动完成的,
所以, 找负载均衡的自动装配类,
进入OpenFeign的源码,找到的LoadBalancer自动装配类,源码如下图所示。
位置:org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
该类即一种自动装配,实例化基于LoadBalancerClient的Client实现,
LoadBalancerClient是接口,即实现LoadBalancerClient。

2.2 寻找负载均衡策略

通过负载均衡入口类FeignLoadBalancerAutoConfiguration,我们看到标识了很多个注解,
需要关注的是LoadBalancerClientFactory负载均衡工厂,
我是怎么知道的?
挨个点进去看看呗。

2.2.1 负载均衡客户端工厂

这个工厂是干啥的?
从厂名猜测一下,应该是装配负载均衡客户端的。
下面,穿上工作服,进入负载均衡工厂吧!
首先看看工厂里都有啥?
源码如下图所示,由注释可知,该工厂就是生产客户端、负载均衡和客户端配置实例的。
他为每个客户端名称创建一个Spring应用上下文,并且根据需要提取。
位置:org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory

2.2.2 负载均衡客户端配置

通过上图,仔细阅读,不难发现,
工厂的构造器LoadBalancerClientFactory()
通过继承NamedContextFactory进行实例化,
而LoadBalancerClientSpecification就是指定负载均衡客户端,
所以,可以看下使用的类:LoadBalancerClientConfiguration.class,
猜想,这个类就是指定负载均衡策略的。(其实,是先点进去才知道,马后炮)
位置:org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration
该类源码如下图所示,由源码可知,
创建的Bean:reactorServiceInstanceLoadBalancer使用的负载均衡策略是“轮询”方式,
RoundRobinLoadBalancer。

2.3 “轮询式”负载均衡策略

老规矩,进入源码查看实现,
看看这个“轮询”的负载均衡策略是如何实现的?
位置:org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#RoundRobinLoadBalancer(org.springframework.beans.factory.ObjectProvider, java.lang.String)
第一层的方法如下图所示,
这里关注第三个参数:new Random().nextInt(1000)
随机生成值域为[1, 1000]的种子,用于均衡客户端,而不是按顺序轮询。

实例化的参数如下图所示,
position作为种子。

接下来,看看是如何使用这个种子进行均衡的,
方法调用路径:choose-》processInstanceResponse-》getInstanceResponse,
因此,最终的均衡逻辑在:getInstanceResponse
位置:org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse
均衡逻辑中,有一个TODO: enforce order?是否强制均衡顺序?
这里使用的均衡,并不是顺序均衡,
而是通过随机种子与服务数量取余(pos%servcie.size),来决定将请求均衡到哪个服务上。

到这里,完成OpenFeign负载均衡探索。
下面验证下OpenFeign的负载均衡。


3 实践

本次实验的服务架构如下图所示。
共有三个模块:注册中心(Eureka)、消费者(tutorial)和生产者(spring-boot-template,三个)。

序号模块描述
1注册中心这里绘制的是Eureka集群,但是,实际实验过程,只用了一个Eureka服务
2消费者通过集成OpenFeign和LoadBalancer调用生产者,验证LoadBalancer的负载均衡功能
3生产者对外提供接口,注册到Eureka,其他服务可以通过OpenFeign调用

3.1 依赖

SpingCloud版本:2020.0.3
因此,集成Eureka后,需要将默认的Ribbon移除,
添加SpringCloud的负载均衡。


	org.springframework.cloud
	spring-cloud-starter-netflix-eureka-server
	
		
			org.springframework.cloud
			spring-cloud-starter-netflix-ribbon
		
	
	2.2.3.RELEASE


	org.springframework.cloud
	spring-cloud-starter-openfeign


	org.springframework.cloud
	spring-cloud-loadbalancer


	
		
			org.springframework.cloud
			spring-cloud-dependencies
			2020.0.3
			pom
			import
		
	

3.2 配置消费者(Consumer)FeignClient:服务A

服务A需要调用同一注册中心(Eureka实验用)的服务B,
因此,需要在服务A(tutorial)中配置服务B的Feigin客户端,以调用服务B(spring-boot-template),
服务A(tutorial)中配置的FeiginClient如下所示,
通过@FeignClient(value = “spring-boot-template”)指定生产者(Provider)。

package com.monkey.tutorial.common.rpc;

import com.monkey.tutorial.common.constant.MicroServiceApiConstant;
import com.monkey.tutorial.common.constant.MicroServiceNameConstant;
import com.monkey.tutorial.common.response.Response;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;


@FeignClient(value = "spring-boot-template")
public interface IFeignTemplateService {

    
    @RequestMapping(MicroServiceApiConstant.API_GET_TEST)
    Response feign1Test(@RequestParam("msg") String msg);

    @RequestMapping(MicroServiceApiConstant.API_FEIGN_TEST)
    String feign2Test();
}
3.3 启用FeignClient
package com.monkey.tutorial;

import com.monkey.tutorial.common.constant.MicroServiceNameConstant;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

@SpringBootApplication
@EnableCaching
@EnableFeignClients
@ServletComponentScan
public class TutorialApplication {

	private static final Logger logger = LoggerFactory.getLogger(TutorialApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(TutorialApplication.class, args);
		logger.info("Tutorial 成功启动");
	}
}
3.4 配置生产者(Provider):服务B

这里,在单台机器上开多个生产者的服务,
使用3个生产者:p1,p2和p3

3.4.1 配置文件
  • application.yml
spring:
  main:
    allow-bean-definition-overriding: true
  application:
    name: spring-boot-template
  profiles:
    active: dev
  web:
    resources:
      static-locations: classpath:/resources/
mybatis:
  config-location: classpath:/config/mybatis-config.xml
  mapper-locations: classpath:mapper
@RestController
@RequestMapping("/api/v1")
public class TestApi {

    @GetMapping("/get/test")
    public Response getTest(@RequestParam("msg") String msg, HttpServletRequest httpServletRequest) {
        int localPort = httpServletRequest.getLocalPort();
        StringBuilder sb = new StringBuilder();
        sb.append(msg).append(",LocalPort:").append(localPort);

        return Response.success(sb.toString());
    }

    @GetMapping("/feign")
    public String feignTest() {
        return "feign";
    }
}

4 IDEA配置

IDEA指定配置文件,分别启动生产者。
在Configuration中配置需要启动的主函数,如下图所示。

这里使用三个生产者(Provider),因此新建三个Application,
分别为Provider-1、Provider-2和Provider-3,
添加运行参数,指定运行时激活的配置文件,如下图所示。

--spring.profiles.active


分别启动三个生产者,启动成功后,在IDEA中开启了三个Provider,
如下图所示。

5 注册中心

启动注册中心Eureka,
之后启动消费者和3个生产者,
总共有4个服务注册到Eureka,
登录Eureka,如下图所示,可以看到。

6 测试

通过消费者接口调用消费者,
每次调用都会均衡到不同的消费者。
下面测试了三次,结果如下图所示,
通过不同的接口判定请求的是哪个消费者。


7 小结

(1)SpringCloud:2020.0.3,已经弃用Netflix的Ribbon,
使用自有组件spring-cloud-loadbalancer做负载均衡,因此,集成Eureka时需要移除ribbon;
(2)spring-cloud-loadbalancer使用默认的负载均衡策略为:随机“轮询”方式,通过随机种子与服务数量取余,选择均衡的服务;
(3)消费者无需选择负载均衡策略,OpenFeign启动时,自动装配负载均衡器;
(4)spring-cloud-loadbalancer负载均衡有两种:纯随机和随机轮询。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939118.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号