Spring Cloud提供了自己的客户端负载均衡器抽象和实现。对于负载平衡机制,增加了ReactiveLoadBalancer
接口,并为其提供了基于Round Robin
和Random
的实现。负载均衡策略默认是Round Robin
。支持ServiceInstanceListSupplier
的基于服务发现的实现,该实现使用类路径中可用的发现客户端从服务发现中检索可用实例
。
修改负载均衡策略
要修改执行服务的负载均衡策略,配置:
@Configuration
@LoadBalancerClient(value = "producer", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {
}
@LoadBalancerClient的value是服务名
,configuration是配置类。
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory
.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
修改ReactorLoadBalancer负载均衡实现为RandomLoadBalancer
。启动端口为8002,8004的两个Producer,访问http://localhost:8500/producer/hello,多访问几次,可以看到负载均衡改为随机。要自定义负载均衡策略,只需实现ReactiveLoadBalancer
接口。
RestTemplate和WebClient要使用LoadBalancer作为负载均衡,只需加 @LoadBalanced
,如:
@Configuration
public class MyConfiguration {
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Configuration
public class MyConfiguration {
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
}
ServiceInstanceListSupplier
ServiceInstanceListSupplier从DiscoveryClient获取服务实例。
CachingServiceInstanceListSupplier
CachingServiceInstanceListSupplier缓存了服务实例,首先从缓存中查找服务,如果没有再从DiscoveryClientServiceInstanceListSupplier查找。CachingServiceInstanceListSupplier默认使用DefaultLoadBalancerCache
。如果类路径下有com.github.ben-manes.caffeine:caffeine
,则使用Caffeine作为缓存。缓存配置如下:
spring:
cloud:
loadbalancer:
cache:
enabled: true
capacity: 256
ttl: 35s
caffeine:
spec:
enabled表示是否启用cache,默认为true,capacity表示缓存初始容量,默认256。ttl表示缓存过期时间,默认35秒。caffeine.spec是caffeine的配置属性。如果注册中心客户端自带了缓存,比如Eureka,不需要开启CachingServiceInstanceListSupplier,避免双重缓存。
ZonePreferenceServiceInstanceListSupplier
为了实现基于区域的负载平衡,提供了ZoneReferenceServiceInstanceListSupplier。使用DiscoveryClient特定的区域配置(例如,eureka.instance.metadata-map.zone)来选择客户端尝试为其筛选可用服务实例的区域。ZoneReferenceServiceInstanceListSupplier过滤检索到的实例,并且只返回同一区域内的实例。如果区域为null或同一区域内没有实例,则返回所有检索到的实例。比如为Producer服务配置:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withZonePreference()
.build(context);
}
}
@Configuration
@LoadBalancerClient(value = "producer", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {
}
修改Producer,增加多个服务,端口为8002的application.properties配置:
server.port=8002
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.zone=区域1
端口为8004的application.properties配置:
server.port=8004
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.zone=区域1
端口为8006的application.properties配置:
server.port=8006
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.zone=区域2
分别启动上面3个Producer服务。访问http://localhost:8500/producer/hello,只看到端口为8002和8004。
HealthCheckServiceInstanceListSupplier
可以为LoadBalancer启用计划的运行状况检查。为此提供了HealthCheckServiceInstanceListSupplier。它定期验证委托ServiceInstanceListSupplier提供的实例是否仍然有效,并且只返回健康的实例,除非没有,然后返回所有检索到的实例。配置如下:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
配置参数由HealthCheck提供:
private Duration initialDelay = Duration.ZERO;
private Duration interval = Duration.ofSeconds(25);
private Duration refetchInstancesInterval = Duration.ofSeconds(25);
private Map<String, String> path = new LinkedCaseInsensitiveMap<>();
private Integer port;
private boolean refetchInstances = false;
private boolean repeatHealthCheck = true;
initialDelay是HealthCheck计划程序的初始延迟值。interval是运行HealthCheck计划程序的间隔。refetchInstancesInterval是重新刷新可用服务实例的间隔。path是检查健康状态的路径。可以根据serviceId进行设置。也可以设置默认值。如果没有设置,将使用/actuator/health。port是健康检查的端口。refetchInstances指示服务实例是否应由HealthCheckServiceInstanceListSupplier重新获取。repeatHealthCheck指示是否应继续重复运行状况检查。
SameInstancePreferenceServiceInstanceListSupplier
SameInstancePreferenceServiceInstanceListSupplier优先选择以前的服务实例。配置如下:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withSameInstancePreference()
.build(context);
}
}
RequestBasedStickySessionServiceInstanceListSupplier
通过Cookie值选择服务实例。Cookie名是 sc-lb-instance-id
,可以通过spring.cloud.loadbalancer.instance-id-cookie-name
改变Cookie名。配置如下:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withRequestBasedStickySession()
.build(context);
}
}
HintBasedServiceInstanceListSupplier
Spring Cloud LoadBalancer允许设置String提示,这些提示将在Request对象中传递给LoadBalancers在ReactiveLoadBalanceer的实现中使用。通过设置spring.cloud.loadbalancer.hint.defect
属性的值,可以为所有服务设置默认提示。还可以通过设置spring.cloud.loadbalancer.hint.[service_ID]
属性的值,将[service_ID]替换为服务的正确ID,为任何给定服务设置特定值。如果提示不是由用户设置的,则使用默认值。
HintBasedServiceInstanceListSupplier检查提示请求标头(默认标头名称为X-SC-LB-hint
,可以通过更改spring.cloud.loadbalancer.hint-header-name
属性的值来修改它),如果找到提示请求标头,则使用标头中传递的提示值来筛选服务实例。如果没有添加提示头,HintBasedServiceInstanceListSupplier将使用属性中的提示值来筛选服务实例。如果没有通过标头或属性设置任何提示,则会返回委托提供的所有服务实例。
筛选时,HintBasedServiceInstanceListSupplier会在元数据映射中查找在提示键下设置了匹配值的服务实例。如果找不到匹配的实例,则返回委托提供的所有实例。
配置如下:
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHints()
.build(context);
}
}
application.yml配置:
spring:
cloud:
loadbalancer:
hint:
producer: abcd
配置Producer服务的提示为abcd
修改Producer,增加多个服务,端口为8002的application.properties配置:
server.port=8002
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.hint=abcd
端口为8004的application.properties配置:
server.port=8004
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.hint=abcd
端口为8006的application.properties配置:
server.port=8006
spring.application.name=producer
eureka.client.service-url.defaultZone=http://user:123@localhost:8761/eureka/
eureka.instance.metadata-map.hint=abcde
分别启动上面3个Producer服务。访问http://localhost:8500/producer/hello,只看到端口为8002和8004。
LoadBalancerLifecycle
LoadBalancerLifecycle是LoadBalancer生命周期接口,允许定义在负载平衡之前和之后应该执行的操作。
default boolean supports(Class requestContextClass, Class responseClass, Class serverTypeClass) {
return true;
}
void onStart(Request<RC> request);
void onStartRequest(Request<RC> request, Response<T> lbResponse);
void onComplete(CompletionContext<RES, T, RC> completionContext);
supports判断是否应该执行生命周期bean的回调。onStart是在负载平衡之前执行的回调方法。onStartRequest是在选择服务实例之后,在执行实际负载平衡请求之前执行的回调方法。onComplete是负载平衡后执行的回调方法。
例子:
@Bean
public LoadBalancerLifecycle loadBalancerLifecycle() {
return new LoadBalancerLifecycle() {
@Override
public void onStart(Request request) {
System.out.println("start");
}
@Override
public void onStartRequest(Request request, Response lbResponse) {
System.out.println("onStartRequest");
}
@Override
public void onComplete(CompletionContext completionContext) {
System.out.println("onComplete");
}
};
}
访问http://localhost:8500/producer/hello,看到
start
onStartRequest
onComplete
MicrometerStatsLoadBalancerLifecycle
提供了一个名为MicrometerStatsLoadBalancerLifecycle的LoadBalancer生命周期bean,它使用Micrometer为负载平衡调用提供统计信息。
MicrometerStatsLoadBalancerLifecycle在MeterRegistry中注册以下仪表:
- loadbalancer.requests.active: 任何服务实例的当前活动请求数量
- loadbalancer.requests.success:任何服务实例的请求成功数量
- loadbalancer.requests.failed:任何服务实例的请求失败数量
- loadbalancer.requests.discard:任何服务实例的请求丢弃数量
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.yml配置:
spring:
cloud:
loadbalancer:
stats:
micrometer:
enabled: true
management:
endpoints:
web:
exposure:
include: '*'
开启Micrometer指标收集。
访问http://localhost:8500/producer/hello后,在访问http://localhost:8500/actuator/metrics,看到:
{
"names": [
"application.ready.time",
"application.started.time",
"disk.free",
"disk.total",
"executor.active",
"executor.completed",
"executor.pool.core",
"executor.pool.max",
"executor.pool.size",
"executor.queue.remaining",
"executor.queued",
"http.server.requests",
"jvm.buffer.count",
"jvm.buffer.memory.used",
"jvm.buffer.total.capacity",
"jvm.classes.loaded",
"jvm.classes.unloaded",
"jvm.gc.live.data.size",
"jvm.gc.max.data.size",
"jvm.gc.memory.allocated",
"jvm.gc.memory.promoted",
"jvm.gc.overhead",
"jvm.gc.pause",
"jvm.memory.committed",
"jvm.memory.max",
"jvm.memory.usage.after.gc",
"jvm.memory.used",
"jvm.threads.daemon",
"jvm.threads.live",
"jvm.threads.peak",
"jvm.threads.states",
"loadbalancer.requests.active",
"loadbalancer.requests.success",
"logback.events",
"process.cpu.usage",
"process.files.max",
"process.files.open",
"process.start.time",
"process.uptime",
"spring.cloud.gateway.requests",
"spring.cloud.gateway.routes.count",
"system.cpu.count",
"system.cpu.usage",
"system.load.average.1m"
]
}
可以看到loadbalancer.requests.active,loadbalancer.requests.success两个参数。在访问http://localhost:8500/actuator/metrics/loadbalancer.requests.success,看到:
{
"name": "loadbalancer.requests.success",
"description": null,
"baseUnit": "seconds",
"measurements": [
{
"statistic": "COUNT",
"value": 1
},
{
"statistic": "TOTAL_TIME",
"value": 3.853133232
},
{
"statistic": "MAX",
"value": 0
}
],
"availableTags": [
{
"tag": "serviceInstance.host",
"values": [
"192.168.31.148"
]
},
{
"tag": "serviceInstance.port",
"values": [
"8002"
]
},
{
"tag": "serviceInstance.instanceId",
"values": [
"192.168.31.148:producer:8002"
]
},
{
"tag": "method",
"values": [
"GET"
]
},
{
"tag": "serviceId",
"values": [
"PRODUCER"
]
},
{
"tag": "uri",
"values": [
"/hello"
]
},
{
"tag": "outcome",
"values": [
"SUCCESS"
]
},
{
"tag": "status",
"values": [
"200"
]
}
]
}
标签:SpringCloud,eureka,实例,loadbalancer,jvm,spring,public,LoadBalancer
From: https://www.cnblogs.com/shigongp/p/17397145.html