首页 > 其他分享 >Spring Cloud LoadBalancer

Spring Cloud LoadBalancer

时间:2023-04-01 18:14:41浏览次数:40  
标签:Spring cloud 实例 Cloud spring LoadBalancer public loadbalancer

ReactiveLoadBalancer与ServiceInstanceListSupplier

Spring Cloud提供了client的load-balance抽象和实现。在load-balance机制中添加了ReactiveLoadBalancer接口,并且为其提供了Round-Robin-based和Random实现。

为了从反应式服务中选择服务实例,使用了ServiceInstanceListSupplier接口。
Spring Cloud使用ServiceInstanceListSupplier的service-discovery-based的实现来从使用类路径中可用的DiscoveryClient的ServiceDiscovery中检索可用服务实例。

RoundRobinLoadBalancer

RoundRobinLoadBalancer是默认情况下使用的ReactiveLoadBalancer的实现类。
还可以自定义LoadBalancer的配置来切换不同的ReactiveLoadBalancer实现。

切换自己的Spring Cloud LoadBalancer配置

可以使用@LoadBalancerClient来切换自己的load-balancer client配置,如下:

@Configuration
//value的值指定了在给定的load-balancer client配置下,要发送请求到哪个服务(服务id)
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {
​
    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

或者通过@LoadBalancerClients来定义多个配置:

@LoadBalancerClients({
    @LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class),
    @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)
})

作为@LoadBalancerClient和@LoadBalancerClients注释的参数的类不应该使用@Configuration进行注释,也不应在组件扫描之外。
为了更容易地使用自己的LoadBalancer配置,可以使用ServiceInstanceListSupplier类的builder()方法。
也可以使用Spring Cloud预定义的其他配置方案,来替换默认配置,例如设置spring.cloud.loadbalancer.configurations属性为zone-preference来使用带有缓存的ZonePreferenceServiceInstanceListSupplier,或设置为health-check来使用带有缓存的HealthCheckServiceInstanceListSupplier。
可以利用这个特性来实例化不同的ServiceInstanceListSupplier或ReactorLoadBalancer的实现来覆盖默认的配置。

Zone-Based Load-Balancing

为了实现zone-based的load-balancing,Spring Cloud提供了ZonePreferenceServiceInstanceListSupplier,使用特定DiscoveryClient的zone配置(例如,eureka.instance.metadata-map.zone)来选择客户端尝试筛选可用服务实例的区域。

可以通过设置spring.cloud.loadbalancer.zone属性,覆盖特定DiscoveryClient的区域(zone)配置。

为了确定获取到的服务实例(ServiceInstance)的区域(zone),会检测它元数据(metadata)中"zone"的值。
ZonePreferenceServiceInstanceListSupplier会筛选获取到的服务实例,只返回在同一区域(zone)内的实例。如果zone的值为null,或者没有在区域内的服务实例,就会返回所有获取到的实例。

使用Zone-based Load-Balancing:
Spring Cloud使用委托来处理ServiceInstanceListSupplierbeans。
建议在ZonePreferenceServiceInstanceListSupplier的构造器中传入DiscoveryClientServiceInstanceListSupplier的委托,然后用CachingServiceInstanceListSupplier包装ZonePreferenceServiceInstanceListSupplier以利用LoadBalancer Cache。

//将该类作为@LoadBalancerClient或@LoadBalancerClients的configuration值,以配置LoadBalancer
public class CustomLoadBalancerConfiguration {
    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withZonePreference()
                    .withCaching()
                    .build(context);
    }
}
Health-Check Load-Balancing

为了让能够为LoadBalancer提供定期的健康检查成为可能,Spring Cloud提供了HealthCheckServiceInstanceListSupplier,它会通过委托ServiceInstanceListSupplier定期的验证这些服务实例是否存活,然后只返回健康的服务实例,除非一个健康的服务实例都没有,那么它就会返回所有获取到的服务实例。

这个机制在使用SimpleDiscoveryClient时尤其有用。对于由实际Service Registry支持的客户端没有必要使用,因为在查询外部的Service Discovery后,我们已经获得了健康的服务实例。
当每个服务只有很少数量的服务实例时,也推荐使用该机制,以避免对失效的服务实例不停地调用。
HealthCheckServiceInstanceListSupplier依赖于委托(代理)流提供的更新的服务实例。在少数情况下,当我们想要使用不刷新服务实例的委托操作时,你可以设置spring.cloud.loadbalancer.health-check.refetch-instances为true,来让HealthCheckServiceInstanceListSupplier刷新服务实例列表,尽管服务实例的列表可能会改变(例如DiscoveryClientServiceInstanceListSupplier)。
也可以通过修改spring.cloud.loadbalancer.health-check.refetch-instances-interval的值来调整刷新的时间间隔。
修改spring.cloud.loadbalancer.health-check.repeat-health-check为false来选择取消额外的重复的健康检查,因为每个服务实例的刷新也会触发一次健康检查。
HealthCheckServiceInstanceListSupplier使用前缀为spring.cloud.loadbalancer.health-check的属性。可以用其设置定期检查的initialDelay和interval。
可以通过设置spring.cloud.loadbalancer.health-check.path.default的值设置健康检查的默认URL。也可以用服务的ID作为spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]中[SERVICE_ID]的值([SERVICE_ID]的值为服务的正确ID)。如果[SERVICE_ID]的值没有被指定,则默认为/actuator/health。如果[SERVICE_ID]的值为null或是一个空值,则健康检查不会被执行。
如果依赖于默认路径/actuator/health,确保添加了spring-boot-starter-actuator到你的依赖中,除非打算自己添加一个/actuator/health路径的服务。
也可以通过设置spring.cloud.loadbalancer.health-check.port来自定义健康检查请求的端口。如果没有设置,则被健康检查请求的端口为服务实例上可用的端口。
使用Health-Check Load-Balancing:
Spring Cloud使用委托来处理ServiceInstanceListSupplierbeans。建议在HealthCheckServiceInstanceListSupplier的构造器中传入一个DiscoveryClientServiceInstanceListSupplier的委托。
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
对于non-reactive的模式,使用withBlockingHealthChecks()来创建HealthCheckServiceInstanceListSupplier。也可以传入自定义的WebClient和RestTemplate用于健康检查。
HealthCheckServiceInstanceListSupplier有它自己的基于Reactor Fluxreplay()的缓存机制。所以,如果使用该模式的Load-Balancing,可以跳过将其使用CachingServiceInstanceListSupplier的步骤。
Same-Instance Load-Balancing
在这种模式中,可以设置LoadBalancer优先选择先前选择过的服务实例(如果该实例可用的话)。
使用Same-Instance Load-Balancing:
需要使用SameInstancePreferenceServiceInstanceListSupplier。可以通过spring.cloud.loadbalancer.configurations为same-instance-preference或者通过提供自定义的ServiceInstanceListSupplierbean:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withSameInstancePreference()
.build(context);
}
}
这也是Zookeeper的StickyRule的一个替代方案。
Request-based Sticky Session Load-Balancing
在这种模式中,LoadBalancer优先选择与请求携带的cookie中提供的instanceId对应的服务实例。
如果请求通过Spring Cloud的LoadBalancer交换过滤器方法和过滤器时使用的ClientRequestContext或ServerHttpRequestContext传递给LoadBalancer,Spring Cloud才会支持这种模式。
该模式目前只被WebClient-backed load-balancing支持。
使用Request-based Sticky Session Load-Balancing:
需要使用RequestBasedStickySessionServiceInstanceListSupplier。可以通过设置spring.cloud.loadbalancer.configurations为request-based-sticky-session或提供自定义的ServiceInstanceListSupplierbean:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withRequestBasedStickySession()
.build(context);
}
}
本模式中,在向前发送请求之前更新所选服务实例(如果原始请求cookie中的服务实例不可用,则该服务实例可能与原始请求cookie中的服务实例不同)非常有用。可以通过设置spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie为true来启用这一功能。
默认情况下,cookie的名字为sc-lb-instance-id,通过设置spring.cloud.loadbalancer.instance-id-cookie-name修改它的名字。
Hint-based Load-Balancing
Spring Cloud LoadBalancer可以在Request中设置传入LoadBalancer的String Hints,之后会被用于ReactiveLoadBalancer来处理这些Hints。
可以通过spring.cloud.loadbalancer.hint.default设置所有服务默认的hint,也可以通过设置spring.cloud.loadbalancer.hint.SERVICE_ID来为特定的服务设置特定的hint。
Spring Cloud提供了HintBasedServiceInstanceListSupplier,这是一个ServiceInstanceListSupplier的hint-based实现。该类会检查hint请求头(默认的请求头名称为X-SC-LB-Hint,可以通过spring.cloud.loadbalancer.hint-header-name来修改该名称),如果找到一个hint请求头,就会用该请求头的hint值来筛选服务实例。如果没有hint请求头,HintBasedServiceInstanceListSupplier使用上面提到的spring.cloud.loadbalancer.hint.[SERVICE_ID]设置的hint值来筛选请求实例。如果们没有设置hint值,也没有hint请求头,就会返回所有服务实例。
当筛选服务实例时,HintBasedServiceInstanceListSupplier查找请求实例中的metadataMap中的hint值,并返回匹配的服务实例。如果没有匹配的实例,就会返回所有的服务实例。
使用Hint-based Load-Balancing:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHints()
.withCaching()
.build(context);
}
}
ReactorLoadBalancerExchangeFilterFunction
为了更容易的使用Spring Cloud的Load Balancer,可以使用ReactorLoadBalancerExchangeFilterFunction结合WebClient与BlockingLoadBalancerClient(配合RestTemplate)。
Spring RestTemplate作为Load Balancer Client
使用@LoadBalanced来创建一个load-balanced RestTemplate:
@Configuration
public class MyConfiguration {

@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}

public class MyClass {
@Autowired
private RestTemplate restTemplate;

public String doOtherStuff() {
//URI需要使用一个虚拟的host name(即service name),BlockingLoadBalancerClient会用其来创建一个完整的物理地址
String results = restTemplate.getForObject("http://stores/stores", String.class);
return results;
}
}
要使用load-balanced RestTemplate,类路径中必须要有load-balancer的实现类。(添加Spring Cloud LoadBalancer stater依赖)
Spring WebClient作为Load Balancer Client
使用@LoadBalanced来自动创建一个load-balanced client:
@Configuration
public class MyConfiguration {

@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}

public class MyClass {
@Autowired
private WebClient.Builder webClientBuilder;

public Mono doOtherStuff() {
//URI需要使用一个虚拟的host name(即service name),BlockingLoadBalancerClient会用其来创建一个完整的物理地址
return webClientBuilder.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
}
要使用load-balanced RestTemplate,类路径中必须要有load-balancer的实现类。(添加Spring Cloud LoadBalancer stater依赖)
在底层ReactiveLoadBalancer会起作用。
Spring WebFlux WebClient结合ReactorLoadBalancerExchangeFilterFunction**
如果spring-webflux在类路径中,ReactorLoadBalancerExchangeFilterFunction会被自动配置。
public class MyClass {
@Autowired
private ReactorLoadBalancerExchangeFilterFunction lbFunction;

public Mono doOtherStuff() {
//URI需要使用一个虚拟的host name(即service name),BlockingLoadBalancerClient会用其来创建一个完整的物理地址
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
Spring WebFlux WebClient结合Non-reactive Load Balancer Client
如果spring-webflux在类路径中,LoadBalancerExchangeFilterFunction会被自动配置。
public class MyClass {
@Autowired
private LoadBalancerExchangeFilterFunction lbFunction;

public Mono doOtherStuff() {
//URI需要使用一个虚拟的host name(即service name),BlockingLoadBalancerClient会用其来创建一个完整的物理地址
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
该方法已经被废弃了,建议使用Spring WebFlux WebClient结合ReactorLoadBalancerExchangeFilterFunction
Spring Cloud LoadBalancer Caching
基本上,ServiceInstanceListSupplier实现类每次要选择服务实例时都要通过DiscoveryClient类来获取,但在此基础之上我们还提供了两层缓存实现。
Caffeine-backed LoadBalancer Cache实现
如果com.github.ben-manes.caffeine:caffeine在类路径中,Caffeine-based实现就会被使用。
可以通过设置spring.cloud.loadbalancer.cache.caffeine.spec属性为自己定义的CaffeineSpec类来覆盖Caffeine Cache对LoadBalancer的默认配置。
传入自定义的CaffeineSpec类会覆盖所有其他的LoadBalancerCache设置,包括ttl和capacity。
默认的LoadBalancer Cache实现
如果类路径中没有Caffeine Cache,则DefaultLoadBalancerCache就会被使用。该类由spring-cloud-starter-loadbalancer自动配置。
配置LoadBalancer Cache
你可以自定义ttl(entries生存时间)值,设置spring.cloud.loadbalancer.cache.ttl为Duration类的String格式。同理可以设置spring.cloud.loadbalancer.cache.capacity属性来自定义LoadBalancer Cache的初始容量。
ttl和initialCapacity的默认值分别为35秒和256。
可以设置spring.cloud.loadbalancer.cache.enabled为false来禁用缓存。
虽然基本的、非缓存的实现对于原型设计和测试很有用,但它的效率远远低于缓存版本,因此建议在生产中始终使用缓存版本。如果DiscoveryClient实现(例如EurekaDiscoveryClient)已经完成了缓存,则应禁用配置LoadBalancer Cache以防止双重缓存。
Transform the load-balanced HTTP request
可以使用被筛选出来的服务实例来转换load-balanced HTTP request。
要用RestTemplate的话,需要实现并定义LoadBalancerRequsetTransfomerbean:
@Bean
public LoadBalancerRequestTransformer transformer() {
return new LoadBalancerRequestTransformer() {
@Override
public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) {
return new HttpRequestWrapper(request) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.putAll(super.getHeaders());
headers.add("X-InstanceId", instance.getInstanceId());
return headers;
}
};
}
};
}
要用WebClient的话,需要实现并定义LoadBalancerClientRequestTransformerbean:
@Bean
public LoadBalancerClientRequestTransformer transformer() {
return new LoadBalancerClientRequestTransformer() {
@Override
public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) {
return ClientRequest.from(request)
.header("X-InstanceId", instance.getInstanceId())
.build();
}
};
}
如果定义了多个transformer,它们会按照bean的定义顺序来作用。或者,可以使用LocalBalancerRequestTransformer.DEFAULT_ORDER或者LoadBalancerClientRequestTransformer.DEFAULT_ORDER来定义它们的作用顺序。
Spring Cloud LoadBalancer Lifecycle
LoadBalancerLifecyclebeans提供了一些回调方法:onStart(Request request)、onStartRequest(Request request, Response lbResponse)和onComplete(CompletionContext<RES, T, RC> completionContext),需要实现这些方法来规定load-balancing前后发生的逻辑。
onStart(Request request)方法需要传入一个Request对象参数,其中包含着用来筛选合适服务实例的信息(包括downstream client request和hint)。onStartRequest(Request request, Response lbResponse)方法需要传入一个Request对象和一个Response对象作为参数。onComplete(CompletionContext<RES, T, RC> completionContext)方法需要传入一个CompletionContext对象,其中包含LoadBalancer Response(包括选择的服务实例),还有在服务实例上执行的请求的Status以及(如果有的话)返回给downstream client的Response,如果有异常发生的话还会包含相应的Throwable对象。
supports(Class requestContextClass, Class responseClass, Class serverTypeClass)方法返回的boolean值决定了请求过程中的处理器(processor in question)是否处理给定类型的对象,默认情况下(方法没有被重写)返回true。
RC:RequestContext type; RES:client的response type;T:返回的server type
Spring Cloud LoadBalancer Statistics
Spring Cloud提供了一个LoadBalancerLifecyclebean叫做MicrometerStatsLoadBalancerLifecycle,它使用MicroMeter来提供load-balancer的调用统计。
可以通过设置spring.cloud.loadbalancer.stats.micrometer.enabled为true,并且有一个可用的MeterRegistry对象(Spring Boot Actuator会将其添加到项目中)。
MicrometerStatsLoadBalancerLifecycle将下列的meters注册到MeterRegistry中:
loadbalancer.requests.active: 一个仪表,让你能够监测任何服务实例当前激活的请求数(或其他通过tags附加的服务实例信息)。
loadbalancer.requests.success:一个计时器,测量任何load-balanced请求的执行时间,该请求以将响应传递给underlying client而结束。
loadbalancer.requests.failed:一个计时器,测量任何load-balanced请求的执行时间,该请求因异常而结束。
loadbalancer.requests.discard:一个计数器,计量被丢弃的load-balanced请求数,即LoadBalancer尚未检索到运行请求的服务实例的请求数。
关于服务实例、请求数据和响应数据的附加信息在可用时通过tags添加到度量(metrics)中。
对于有些实现类,如BlockingLoadBalancerClient,request和response data可能不可用,因为我们根据参数建立泛型类型,所以可能无法确定类型并读取数据。
当为给定Meter添加至少一条记录时,Meters将在MeterRegistry中注册。
Configuring Individual LoadBalancerClients
单独的LoadBalancer clients可以通过一个不同的前缀spring.cloud.loadbalancer.clients.来单独配置,为loadbalancer的名称。默认配置值可以被spring.cloud.loadbalancer.命名空间设置,并且会被优先合成为client的特定值。
spring:
cloud:
loadbalancer:
health-check:
initial-delay: 1s
clients:
myclient:
health-check:
interval: 30s
上面的例子将合成一个@ConfigurationProperties的对象并且设置了initial-dalay=1s和interval=30s。
大多数client属性可以单独配置,除了以下的全局配置:
spring.cloud.loadbalancer.enabled:全局启用/关闭load-balancing
spring.cloud.loadbalancer.retry.enabled:全局启用/关闭load-balancing重试机制,如果全局启用了它,仍然可以在单独的client属性中配置来关闭它。
spring.cloud.loadbalancer.cache.enabled:全局启用/关闭load-balancing缓存,如果全局启用了它,仍然可以通过创建自定义配置(在ServiceInstanceListSupplier委托层级中不包含CachingServiceInstanceListSupplier)来关闭它。
spring.cloud.loadbalancer.stats.micrometer.enabled:全局启用/关闭LoadBalancer Micrometer metrics。

标签:Spring,cloud,实例,Cloud,spring,LoadBalancer,public,loadbalancer
From: https://www.cnblogs.com/woshi/p/17279014.html

相关文章

  • SpringCloud之openFeign
    FeignOpenFeign是Netflix开发的声明式、模板化的HTTP请求客户端。可以更加便捷、优雅地调用httpapi。OpenFeign会根据带有注解的函数信息构建出网络请求的模板,在发送网络请求之前,OpenFeign会将函数的参数值设置到这些请求模板中。feign主要是构建微服务消费端。只要使用OpenF......
  • SpringBoot项目启动时初始化操作方式
    1.实现InitializingBean重写afterPropertiesSet()方法。@Component@Slf4jpublicclassInitOneTestimplementsInitializingBean{@OverridepublicvoidafterPropertiesSet()throwsException{log.info("InitOneTestinitsuccess");}}2......
  • spring的bean的加载方式
    1、配置文件+<bean/>标签配置文件<beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"/>使用publicclassApp1{publicstaticvoidmain(String[]args){ApplicationContextcontext=newClassPathXmlAppl......
  • 使用 IntelliJ IDEA 构建 Spring Framework 5.3.21 源码问题解决
    源码版本1、下载地址:https://github.com/spring-projects/spring-framework/tags2、选择要构建的源码版本并下载,例如:5.3.21相关环境1、操作系统:Windows102、JDK版本:Jdk173、IDE工具:IntelliJIDEA2021.3.34、项目构建工具:Gradle7.3.3使用IntelliJIDEA构建Spring......
  • mini spring learning
    https://www.pexels.com/zh-cn/photo/768089/http://www.implements.fun:8080/tag/minispringpackagecom.minis.beans.factory;importcom.minis.beans.BeansException;publicinterfaceBeanFactory{ObjectgetBean(Stringname)throwsBeansException; boole......
  • springboot和redis执行lua脚本——踩坑
    问题:原先想使用redis执行lua脚本作为项目限流基础,lua脚本后写完后执行一直报错如下图:  卡了几天了,没看明白咋回事,一次偶然试了一下解决了,传递lua参数需要时String类型难怪说报错强转String类型异常  灵感来源参考文章:踩坑之RedisTemplate执行Lua脚本-知乎(zhihu.c......
  • 基于Spring的AOP(注解方式)
    面向切面编程:基于Spring的AOP(注解方式)1-配置:pom文件:<packaging>jar</packaging><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactI......
  • Spring(Bean详解)
    GoF之工厂模式GoF是指二十三种设计模式GoF23种设计模式可分为三大类:创建型(5个):解决对象创建问题。单例模式工厂方法模式抽象工厂模式建造者模式原型模式结构型(7个):一些类或对象组合在一起的经典结构。代理模式装饰模式适配器模式组......
  • 从原理上理解Spring如何解决循环依赖
    上图展示了循环依赖是什么,类A存在B类的成员变量,所以类A依赖于类B,类B同样存在类A的成员变量,所以类B也依赖于类A,就形成了循环依赖问题。Spring是如何创建Bean的Spring中Bean初始化的精简流程如下:简要描述一下SpringBean的创建流程:(1)首先Spring容器启动之后,会根据使用不同类型......
  • Springboot 系列 (26) - Springboot+HBase 大数据存储(四)| Springboot 项目通过 HBase
    ApacheHBase是Java语言编写的一款Apache开源的NoSQL型数据库,不支持SQL,不支持事务,不支持Join操作,没有表关系。ApacheHBase构建在ApacheHadoop和ApacheZookeeper之上。ApacheHBase:https://hbase.apache.org/HBase的安装配置,请参考“Springboot系列(24)-......