前言
需求背景。我们有两个网关系统,一个是内网使用的,一个是外网使用。如果是内部系统使用调用的内网网关。但是作为网关管理平台,需要和两个网关系统都进行交互。如果对外接口,则和外部网关交互。
网关本身提供的restful接口,便于使用,我们使用OpenFeign进行调用。但是带来一个问题。调用的时候如何指定内网还是外网网关?
方案一
写一个FeignClient,地址是一台nginx。然后再加个Feign拦截器,调用的时候判断,该调内网网关的时候,在path前加上/inner。
nginx做代理配置,如果path前缀是/inner,则将请求转到内网网关。
上代码
client
package com.org.demo.clients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* 网关app客户端
*
* @author dzh
* @date 2022/12/2 16:37
*/
@FeignClient(url = "https://server.local",
name = "gatewayClient",
configuration = GatewayConfig.class,
fallback = GatewayClientFallback.class)
public interface GatewayClient {
// 用来标识调用哪个网关
ThreadLocal<Boolean> out = new ThreadLocal<>();
@GetMapping(value = "something/msg")
String msg();
}
config
package com.org.demo.clients;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
/**
* @author dzh
* @date 2022/12/2 17:24
*/
@Slf4j
public class GatewayConfig {
@Value("${gateway.auth}")
private String auth;
@Bean
@Order(1000)
public RequestInterceptor getRequestInterceptor() {
return template -> {
// 鉴权消息头
template.header("Authorization", auth);
String path = template.path();
if (GatewayClient.out.get()) {
GatewayClient.out.remove();
template.uri("outside" + path);
} else {
GatewayClient.out.remove();
template.uri("inside" + path);
}
};
}
}
方案二
大家觉着第一个方案不太好,还要依赖nginx,但是碍于项目工期,先那样上线了。所有有了后续的方案
写两个FeignClient。有个问题,因为两个网关系统出了ip不同,其他都是一样的,写两个就会有冗余代码。那其实有个偷懒的办法。基于方案一得代码,再写两个实现,分别是内网和外网的客户端。
此方案不用依赖nginx,url直接配置对应网关系统的地址。
这个方案需要在调用的Service中注入两个client,需要哪个调用哪个。
因为是基于之前的方案升级的,不可能一下子全改了,所以在使用@Resource GatewayClient gatewayClient的地方,都需要制定beanId,否则因为容器中有桑格GatewayClient实例,启动会失败。
这样就实现了过度,当都改成方案二的调用方式,就可以删除多余的拦截器了
内网
package com.org.demo.clients;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(url = "https://server.local",
name = "gatewayInsideClient",
qualifiers = "gatewayInsideClient",
fallback = GatewayClientFallback.class)
public interface GatewayInsideClient extends GatewayClient {
}
外网
package com.org.demo.clients;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(url = "https://server.local",
name = "gatewayOutsideClient",
qualifiers = "gatewayOutsideClient",
fallback = GatewayClientFallback.class)
public interface GatewayOutsideClient extends GatewayClient {
}
方案三
以上两个方案已经实践过是么有问题的。方案三目前只停留在理论阶段。方案二因为指定的是ip地址,而且只能指定一个,所以存在风险,如果服务器宕机那就糟了。
SpringCloud有服务发现的机制,也可以将网关的服务注册到Nacos,然后client做对应的修改问题就解决了。
后续
我比较了一下,三中方案没有谁是最好的,只是看合适不合适
方案一:如果我们有十个八个网关,使用该方案还是比较合适的
方案二:如果少量的网关服务,这个方案还是比较好的
方案三:解决方案二单点的问题