一、spring cloud gateway
我们都知道spring cloud gateway是一个基于spring boot、spring webflux、project reactor构建的高性能网关,旨在提供简单、高效的api路由。spring cloud gateway基于netty运行,因此在传统servlet容器中或者打成war包是不能正常运行的。
二、spring cloud gateway两种负载均衡器
2.1 官网说明两种负载均衡器
gateway有两种客户端负载均衡器,loadbalancerclientfilter和reactiveloadbalancerclientfilter。loadbalancerclientfilter使用一个ribbon的阻塞式loadbalancerclient,gateway建议使用reactiveloadbalancerclientfilter。可以通过设置spring.cloud.loadbalancer.ribbon.enabled=false,切换到reactiveloadbalancerclientfilter。无论使用ribbon还是loadbalancer,在route中配置的lb是一样的
一、spring cloud gateway
我们都知道spring cloud gateway是一个基于spring boot、spring webflux、project reactor构建的高性能网关,旨在提供简单、高效的api路由。spring cloud gateway基于netty运行,因此在传统servlet容器中或者打成war包是不能正常运行的。
二、spring cloud gateway两种负载均衡器
2.1 官网说明两种负载均衡器
gateway有两种客户端负载均衡器,loadbalancerclientfilter和reactiveloadbalancerclientfilter。loadbalancerclientfilter使用一个ribbon的阻塞式loadbalancerclient,gateway建议使用reactiveloadbalancerclientfilter。可以通过设置spring.cloud.loadbalancer.ribbon.enabled=false,切换到reactiveloadbalancerclientfilter。无论使用ribbon还是loadbalancer,在route中配置的lb是一样的
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- path=/user/**
- id: message-service
uri: lb://message-service
predicates:
- path=/message/**
nacos:
discovery:
server-addr: localhost:8848
如果uri以lb开头,比如如上配置中的lb://user-service,spring cloud gateway会用reactiveloadbalancerclientfilter 解析服务名为user-service的实例对应的实际host和端口,并做集群负载均衡。
2.2 跳坑
官网说用lb://lakerservice形式即可,但是配置完成后,并未生效。这个官网没有详细说明,查资料也没有,最后发现必须加入依赖:
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>
2.3 简易流程说明
client ----> gateway ----> ribbion负载均衡 取一个服务a ---->转发到服务a
2.4 ribbon说明
spring cloud ribbon 在高版本移除了
三、用routerecordglobalfilter记录路由后的实际代理地址
@slf4j
@component
public class routerecordglobalfilter implements globalfilter, ordered {
@override
public mono<void> filter(serverwebexchange exchange, gatewayfilterchain chain) {
// routetorequesturlfilter会把实际路由的url通过该属性保存
uri proxyrequesturi = exchange.getattribute(serverwebexchangeutils.gateway_request_url_attr);
long start = system.currenttimemillis();
return chain.filter(exchange).then(mono.fromrunnable(() -> {
long end = system.currenttimemillis();
log.info("实际调用地址为:{},调用耗时为:{}ms", proxyrequesturi, (end - start));
}));
}
@override
public int getorder() {
// 优先级设为最低,先让routetorequesturlfilter先调用
return ordered.lowest_precedence;
}
}
routerecordglobalfilter 这个全局过滤器我们主要用来记录路由后的实际代理地址,以及调用耗时。我们看下routetorequesturlfilter的描述会发现实际路由地址会通过serverwebexchange中名为serverwebexchangeutils.gateway_request_url_attr的属性保存。
四、源码
4.1 基于ribbon的loadbalancerclientfilter
gateway中的自动配置类gatewayloadbalancerclientautoconfiguration。
package org.springframework.cloud.gateway.config;
import org.springframework.boot.autoconfigure.autoconfigureafter;
import org.springframework.boot.autoconfigure.condition.conditionalonbean;
import org.springframework.boot.autoconfigure.condition.conditionalonclass;
import org.springframework.boot.autoconfigure.condition.conditionalonmissingbean;
import org.springframework.boot.context.properties.enableconfigurationproperties;
import org.springframework.cloud.client.loadbalancer.loadbalancerclient;
import org.springframework.cloud.gateway.config.conditional.conditionalonenabledglobalfilter;
import org.springframework.cloud.gateway.filter.loadbalancerclientfilter;
import org.springframework.cloud.gateway.filter.reactiveloadbalancerclientfilter;
import org.springframework.cloud.netflix.ribbon.ribbonautoconfiguration;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.web.reactive.dispatcherhandler;
@configuration(
proxybeanmethods = false
)
@conditionalonclass({loadbalancerclient.class, ribbonautoconfiguration.class, dispatcherhandler.class})
@autoconfigureafter({ribbonautoconfiguration.class})
@enableconfigurationproperties({loadbalancerproperties.class})
public class gatewayloadbalancerclientautoconfiguration {
public gatewayloadbalancerclientautoconfiguration() {
}
@bean
@conditionalonbean({loadbalancerclient.class})
@conditionalonmissingbean({loadbalancerclientfilter.class, reactiveloadbalancerclientfilter.class})
@conditionalonenabledglobalfilter
public loadbalancerclientfilter loadbalancerclientfilter(loadbalancerclient client, loadbalancerproperties properties) {
return new loadbalancerclientfilter(client, properties);
}
}
该自动配置类需要在ribbonautoconfiguration自动配置类之后执行,该类是spring-cloud-netflix-ribbon的自动配置类,因此需要引入下面的jar包依赖
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-netflix-ribbon</artifactid>
</dependency>
使用默认的ribbon,则ribbon的配置如下
#=======================ribbon配置(使用netflix的ribbon负载均衡)=======================
#关闭nacos集成ribbon,否则ribbon客户端会从nacos注册中心获取服务列表
ribbon.nacos.enabled=false
#配置serviceid为providerservice的服务list
providerservice.ribbon.listofservers=http://192.168.10.1:8080,http://192.168.10.2:8080
#配置serviceid为providerservice的服务负载均衡
#providerservice.ribbon.nfloadbalancerruleclassname=com.netflix.loadbalancer.roundrobinrule
providerservice.ribbon.nfloadbalancerruleclassname=com.netflix.loadbalancer.availabilityfilteringrule
4.2 spring cloud loadbalancer
spring cloud load balancer并不是一个独立的项目,而是spring-cloud-commons其中的一个模块,因此很多配置和类可以在spring-cloud-common中找到。gateway中的自动配置类gatewayreactiveloadbalancerclientautoconfiguration
spring cloud提供了自己的客户端负载均衡器抽象和实现。对于负载平衡机制,reactiveloadbalancer已添加了接口,并为其提供了基于round-robin和random的实现。为了获得实例以从反应式中进行选择serviceinstancelistsupplier 。当前,我们支持基于服务发现的实现,serviceinstancelistsupplier 该实现使用类路径中可用的发现客户端从服务发现中检索可用实例。
@configuration(
proxybeanmethods = false
)
@conditionalonclass({loadbalancerclient.class, reactiveloadbalancer.class, loadbalancerautoconfiguration.class, dispatcherhandler.class})
@autoconfigurebefore({gatewayloadbalancerclientautoconfiguration.class})
@autoconfigureafter({loadbalancerautoconfiguration.class})
@enableconfigurationproperties({loadbalancerproperties.class})
public class gatewayreactiveloadbalancerclientautoconfiguration {
public gatewayreactiveloadbalancerclientautoconfiguration() {
}
@bean
@conditionalonbean({loadbalancerclientfactory.class})
@conditionalonmissingbean({reactiveloadbalancerclientfilter.class})
@conditional({gatewayreactiveloadbalancerclientautoconfiguration.onnoribbondefaultcondition.class})
@conditionalonenabledglobalfilter
public reactiveloadbalancerclientfilter gatewayloadbalancerclientfilter(loadbalancerclientfactory clientfactory, loadbalancerproperties properties) {
return new reactiveloadbalancerclientfilter(clientfactory, properties);
}
private static final class onnoribbondefaultcondition extends anynestedcondition {
private onnoribbondefaultcondition() {
super(configurationphase.register_bean);
}
@conditionalonmissingclass({"org.springframework.cloud.netflix.ribbon.ribbonloadbalancerclient"})
static class ribbonloadbalancernotpresent {
ribbonloadbalancernotpresent() {
}
}
@conditionalonproperty(
value = {"spring.cloud.loadbalancer.ribbon.enabled"},
havingvalue = "false"
)
static class ribbonnotenabled {
ribbonnotenabled() {
}
}
}
}
该自动配置类在loadbalancerautoconfiguration自动配置类之后执行,该配置类在spring-cloud-commons中。
该自动配置类在gatewayloadbalancerclientautoconfiguration自动配置类之前执行,也就是前面的基于ribbon的自动装配类,由此可见,gateway是优先使用reactiveloadbalancer的,只有没有开启reactiveloadbalancer时才使用使用ribbon。
引入依赖:
<!-- 引入spring-cloud-loadbalancer -->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>
配置如下:
从配置文件中读取服务,而不是从服务注册中心自动发现服务
注意:如果在项目的类路径下存在spring cloud ribbon相关的类,需要通过配置关闭ribbon功能,因为spring cloud默认优先使用ribbon,因此spring.cloud.loadbalancer.ribbon.enabled禁用调ribbon,这也是上面刚提到过的。
#=======================springcloudloadbalancer配置服务列表=======================
#使用reactiveloadbalancerclient时通过该参数禁用调ribbon
spring.cloud.loadbalancer.ribbon.enabled=false
#配置providerservice的instances,不是从注册中心自动发现服务实例
spring.cloud.discovery.client.simple.instances.providerservice[0].uri=http://192.168.10.1:8080
spring.cloud.discovery.client.simple.instances.providerservice[1].uri=http://192.168.10.2:8080
#指定健康检查请求路径,默认健康检查路径是/actuator/health
spring.cloud.loadbalancer.health-check.path.providerservice=/custom/customhealthcheckpath
#运行状况检查计划程序的初始延迟值
spring.cloud.loadbalancer.health-check.initial-delay=0
#重新运行运行状况检查计划程序的时间间隔
spring.cloud.loadbalancer.health-check.interval=5s
#启用预定义的负载平衡器配置
spring.cloud.loadbalancer.configurations=health-check
4.3 simplediscoveryclient
simplediscoveryclient可以结合注册中心使用,也可以静态配置。如果在类路径中没有支持从注册中心发现服务的discoveryclient实例,则将使用simplediscoveryclient实例,该实例使用simplediscoveryproperties来获取有关服务和实例的信息。参考上面的配置文件中的配置。
public class simplediscoveryclient implements discoveryclient {
private simplediscoveryproperties simplediscoveryproperties;
public simplediscoveryclient(simplediscoveryproperties simplediscoveryproperties) {
this.simplediscoveryproperties = simplediscoveryproperties;
}
public string description() {
return "simple discovery client";
}
public list<serviceinstance> getinstances(string serviceid) {
list<serviceinstance> serviceinstances = new arraylist();
list<defaultserviceinstance> serviceinstanceforservice = (list)this.simplediscoveryproperties.getinstances().get(serviceid);
if (serviceinstanceforservice != null) {
serviceinstances.addall(serviceinstanceforservice);
}
return serviceinstances;
}
public list<string> getservices() {
return new arraylist(this.simplediscoveryproperties.getinstances().keyset());
}
public int getorder() {
return this.simplediscoveryproperties.getorder();
}
}
simplediscoveryproperties 服务实例的属性配置
@configurationproperties(
prefix = "spring.cloud.discovery.client.simple"
)
public class simplediscoveryproperties {
//在配置文件中配置的实例属性
private map<string, list<defaultserviceinstance>> instances = new hashmap();
private defaultserviceinstance local = new defaultserviceinstance((string)null, (string)null, (string)null, 0, false);
private int order = 0;
}
defaultserviceinstance默认的服务实例定义
public class defaultserviceinstance implements serviceinstance {
private string instanceid;
private string serviceid;
private string host;
private int port;
private boolean secure;
private map<string, string> metadata;
private uri uri;
...省略...
}
4.3.1 在负载均衡算法之间切换
reactiveloadbalancer默认情况下使用的实现是roundrobinloadbalancer。要针对选定的服务或所有服务切换到不同的实现,可以使用自定义loadbalancer配置机制。例如,可以通过@loadbalancerclient注释传递以下配置以切换为使用randomloadbalancer:
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);
}
}
4.3.2 实例健康检查
可以为loadbalancer启用计划健康检查。为此提供了healthcheckserviceinstancelistsupplier。它定期验证委托serviceinstancelistsupplier提供的实例是否仍然存在,并且只返回健康的实例,除非没有实例—然后返回所有检索到的实例。
在使用simplediscoveryclient时,这种机制特别有用。对于由实际服务注册中心支持的客户端,不需要使用它,因为我们在查询外部servicediscovery之后已经获得了健康的实例。
对于每个服务只有少量实例的设置,也建议使用此供应商,以避免重试调用失败的实例。
发表评论