补课吧。
之前一直着急往前赶进度,
只梳理了微服务架构以及如何使用这些架构中的组件,
然而,并不了解运作原理,
我依旧还是太弱了,经不起大风大浪,
所以,想使自己强壮一些,继续研究一下源码。
还有另外一个原因,最近看了K8S,并实践了K8S部署SpringBoot服务,
发现,可以直接使用K8S进行负载均衡,
于是,又想到,Spring自己也有负载均衡,是如何实现的?
所以,有了这篇文章。
我们知道,
当开发同学使用SpringBoot进行开发时,
没有配置某些Bean,但是,可以直接使用这些Bean,
就说明,SpringBoot在启动时自动装配了这些Bean,
OpenFeign的负载均衡是同样的的道理,
开发同学没有配置负载均衡策略,但是,使用OpenFeign可以自动进行均衡,
因此,OpenFeign的负载均衡是通过自动装配自动完成的,
所以, 找负载均衡的自动装配类,
进入OpenFeign的源码,找到的LoadBalancer自动装配类,源码如下图所示。
位置:org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
该类即一种自动装配,实例化基于LoadBalancerClient的Client实现,
LoadBalancerClient是接口,即实现LoadBalancerClient。
通过负载均衡入口类FeignLoadBalancerAutoConfiguration,我们看到标识了很多个注解,
需要关注的是LoadBalancerClientFactory负载均衡工厂,
我是怎么知道的?
挨个点进去看看呗。
这个工厂是干啥的?
从厂名猜测一下,应该是装配负载均衡客户端的。
下面,穿上工作服,进入负载均衡工厂吧!
首先看看工厂里都有啥?
源码如下图所示,由注释可知,该工厂就是生产客户端、负载均衡和客户端配置实例的。
他为每个客户端名称创建一个Spring应用上下文,并且根据需要提取。
位置:org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory
通过上图,仔细阅读,不难发现,
工厂的构造器LoadBalancerClientFactory()
通过继承NamedContextFactory
而LoadBalancerClientSpecification就是指定负载均衡客户端,
所以,可以看下使用的类:LoadBalancerClientConfiguration.class,
猜想,这个类就是指定负载均衡策略的。(其实,是先点进去才知道,马后炮)
位置:org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration
该类源码如下图所示,由源码可知,
创建的Bean:reactorServiceInstanceLoadBalancer使用的负载均衡策略是“轮询”方式,
RoundRobinLoadBalancer。
老规矩,进入源码查看实现,
看看这个“轮询”的负载均衡策略是如何实现的?
位置:org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#RoundRobinLoadBalancer(org.springframework.beans.factory.ObjectProvider
第一层的方法如下图所示,
这里关注第三个参数: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调用 |
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
3.2 配置消费者(Consumer)FeignClient:服务Aorg.springframework.cloud spring-cloud-dependencies 2020.0.3 pom import
服务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
- 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,
如下图所示。
启动注册中心Eureka,
之后启动消费者和3个生产者,
总共有4个服务注册到Eureka,
登录Eureka,如下图所示,可以看到。
通过消费者接口调用消费者,
每次调用都会均衡到不同的消费者。
下面测试了三次,结果如下图所示,
通过不同的接口判定请求的是哪个消费者。
(1)SpringCloud:2020.0.3,已经弃用Netflix的Ribbon,
使用自有组件spring-cloud-loadbalancer做负载均衡,因此,集成Eureka时需要移除ribbon;
(2)spring-cloud-loadbalancer使用默认的负载均衡策略为:随机“轮询”方式,通过随机种子与服务数量取余,选择均衡的服务;
(3)消费者无需选择负载均衡策略,OpenFeign启动时,自动装配负载均衡器;
(4)spring-cloud-loadbalancer负载均衡有两种:纯随机和随机轮询。



