spring cloud gateway在使用 zookeeper 注册中心时,配置https 进行服务转发
本文为博主原创,转载请注明出处:
在spring cloud gateway 为 2.x 的版本的时候,可以通过引入 ribbon ,在进行过滤器 LoadBalancerClientFilter 进行服务请求路由时,通过调用 choose 方法选择具体的服务实例,源码如下:
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
protected final LoadBalancerClient loadBalancer;
private LoadBalancerProperties properties;
public LoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
this.loadBalancer = loadBalancer;
this.properties = properties;
}
public int getOrder() {
return 10100;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url before: " + url);
}
ServiceInstance instance = this.choose(exchange);
if (instance == null) {
throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
} else {
URI uri = exchange.getRequest().getURI();
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
URI requestUrl = this.loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
} else {
return chain.filter(exchange);
}
}
protected ServiceInstance choose(ServerWebExchange exchange) {
return this.loadBalancer.choose(((URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR)).getHost());
}
}
在choose 方法的调用中,打断点跟进,如果服务中引入了 ribbon ,断点会进入 到 RibbonLoadBalancerClient 中
在使用 ribbon 的过程中,服务的实例是在 RibbonLoadBalancerClient.RibbonServer 类中进行了实例的封装,源码如下:
public static class RibbonServer implements ServiceInstance {
private final String serviceId;
private final Server server;
private final boolean secure;
private Map<String, String> metadata;
public RibbonServer(String serviceId, Server server) {
this(serviceId, server, false, Collections.emptyMap());
}
public RibbonServer(String serviceId, Server server, boolean secure, Map<String, String> metadata) {
this.serviceId = serviceId;
this.server = server;
this.secure = secure;
this.metadata = metadata;
}
public String getInstanceId() {
return this.server.getId();
}
public String getServiceId() {
return this.serviceId;
}
public String getHost() {
return this.server.getHost();
}
public int getPort() {
return this.server.getPort();
}
public boolean isSecure() {
return this.secure;
}
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
public Map<String, String> getMetadata() {
return this.metadata;
}
public Server getServer() {
return this.server;
}
public String getScheme() {
return this.server.getScheme();
}
public String toString() {
StringBuilder sb = new StringBuilder("RibbonServer{");
sb.append("serviceId='").append(this.serviceId).append('\'');
sb.append(", server=").append(this.server);
sb.append(", secure=").append(this.secure);
sb.append(", metadata=").append(this.metadata);
sb.append('}');
return sb.toString();
}
}
在 服务路由进行请求时,会根据其中的 isSecure 属性判断是否进行 https 请求,如果是 isSecure 是 true,则请求的scheme 协议为 https,反之为 http ;该值可以通过以下方式进行配置:
ribbon.isSecure=true
在 spring cloud gateway 中spring-cloud--openfeign-core为 3.x 的版本的时候,该依赖中 将 ribbon 单独的脱离了出来,此时的ribbon 配置不会生效。通过阅读源码之后会发现,该请求的协议是通过调用服务的 sslPort端口来判断是否进行https 请求,如果 sslPort端口不为空,则进行https 请求,
以使用 zookeeper 为注册中心时,查看自动发现配置的property 文件:
@ConfigurationProperties("spring.cloud.zookeeper.discovery")
public class ZookeeperDiscoveryProperties {
public static final String DEFAULT_URI_SPEC = "{scheme}://{address}:{port}";
private HostInfo hostInfo;
private boolean enabled = true;
private String root = "/services";
private String uriSpec = "{scheme}://{address}:{port}";
private String instanceId;
private String instanceHost;
private String instanceIpAddress;
private boolean preferIpAddress = false;
private Integer instancePort;
private Integer instanceSslPort;
private boolean register = true;
private Map<String, String> metadata = new HashMap();
private String initialStatus = "UP";
private int order = 0;
}
在 choose 选择路由的服务时,会 根据ServiceInstance的实例的 isSecure 属性,解析得出http 或https,而zookeeperInstance 是根据 instanceSslPort 端口是否为空进行解析出http或https。zookeeperInstance解析http与https的源码在ZookeeperServiceInstance中
如果想在服务路由时,进行https 的路由,可以进行如下的配置:
server:
port: 9000
spring:
application:
name: my-zookeeper-registry
cloud:
zookeeper:
connect-string: localhost:2181
discovery:
register: true
enabled: true
root: my-registry
instance-ssl-port: {{server.port}}
按照上述配置,可以实现 https 的网关路由转发。