1.传统路由配置
通过在配置文件中具体指定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由。
- 单实例配置:通过一组zuul.routes.<route>.path与zuul.routes.<route>.url参数对的方式配置,比如:
zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.url=http://localhost:8080/
- 多实例配置:通过一组zuul.routes.<route>.path与zuul.routes.<route>.serviceId参数对的方式配置,比如:
zuul.routes.user-service.path=/user-service/**
zuul.routes.user-service.serviceId=user-service
但是在实际微服务架构设计中,寄宿在网关下的微服务数量比较多,在网关配置文件中一个个的加上如上所示的路由配置会非常繁琐,所以这里引入“网关白名单”概念
2.动态路由-“服务白名单”
-
gateway.properties
中配置服务白名单
xxx.includes=\
serviceName1,\
serviceName2,\
serviceName3
- 定义
NamespaceDiscoveryClient
类继承DiscoveryClient
,重写getServices()
方法,获取白名单服务列表和服务发现注册中心(consul)服务列表的交集
@Override
public List<String> getServices() {
// 获取白名单服务集合
Set<String> services = StringUtils.commaDelimitedListToSet(env.getProperty("xxx.includes", ""));
// client.getServices()获取服务发现中服务集合
// retainAll() 集合取交集
services.retainAll(client.getServices());
return new ArrayList<>(services);
}
- 主类
GatewayApp
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class GatewayApp {
public static void main(String[] args) {
SpringApplication.run(GatewayApp.class, args);
}
@Autowired
private Environment env;
@Autowired
private DiscoveryClient discovery;
// 服务路由适配
// user-service -> user/service
@Bean
public ServiceRouteMapper serviceRouteMapper() {
return new ServiceRouteMapper() {
@Override
public String apply(String serviceId) {
return StringUtils.replaceChars(serviceId, '-', '/');
}
};
}
// 初始化路由器过滤bean
@Bean
public GateWayFilter filter() {
String url = env.getProperty("xxx.filter", "");
return new GateWayFilter(url);
}
@Bean
public DiscoveryClientRouteLocator discoveryRouteLocator(ServerProperties server, ZuulProperties zuulProperties) {
DiscoveryClient discovery = new NamespaceDiscoveryClient(this.env, this.discovery);
return new DiscoveryClientRouteLocator(server.getServletPrefix(), discovery, zuulProperties,
serviceRouteMapper()) {
protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routes = super.locateRoutes();
for (ZuulRoute route : routes.values()) {
route.setStripPrefix(false);
}
return routes;
}
};
}
}
-
DiscoveryClient discovery = new NamespaceDiscoveryClient(this.env, this.discovery);
初始化前面自定义的NamespaceDiscoveryClient
-
LinkedHashMap<String, ZuulRoute> routes = super.locateRoutes();
调用父类DiscoveryClientRouteLocator
的locateRoutes()
方法 - 父类
DiscoveryClientRouteLocator
中,对白名单和服务注册中心服务交集,进行类似zuul.routes.user-service.path=/user-service/**,zuul.routes.user-service.serviceId=user-service
的拼接和构造
@Override
protected LinkedHashMap<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
routesMap.putAll(super.locateRoutes());
if (this.discovery != null) {
Map<String, ZuulRoute> staticServices = new LinkedHashMap<String, ZuulRoute>();
for (ZuulRoute route : routesMap.values()) {
String serviceId = route.getServiceId();
if (serviceId == null) {
serviceId = route.getId();
}
if (serviceId != null) {
staticServices.put(serviceId, route);
}
}
// Add routes for discovery services by default
// 这里的services集合是取出的白名单和服务注册中心的交集
List<String> services = this.discovery.getServices();
String[] ignored = this.properties.getIgnoredServices()
.toArray(new String[0]);
for (String serviceId : services) {
// Ignore specifically ignored services and those that were manually
// configured
String key = "/" + mapRouteToService(serviceId) + "/**";
if (staticServices.containsKey(serviceId)
&& staticServices.get(serviceId).getUrl() == null) {
// Explicitly configured with no URL, cannot be ignored
// all static routes are already in routesMap
// Update location using serviceId if location is null
ZuulRoute staticRoute = staticServices.get(serviceId);
if (!StringUtils.hasText(staticRoute.getLocation())) {
staticRoute.setLocation(serviceId);
}
}
if (!PatternMatchUtils.simpleMatch(ignored, serviceId)
&& !routesMap.containsKey(key)) {
// Not ignored
routesMap.put(key, new ZuulRoute(key, serviceId));
}
}
}
if (routesMap.get(DEFAULT_ROUTE) != null) {
ZuulRoute defaultRoute = routesMap.get(DEFAULT_ROUTE);
// Move the defaultServiceId to the end
routesMap.remove(DEFAULT_ROUTE);
routesMap.put(DEFAULT_ROUTE, defaultRoute);
}
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
// Prepend with slash if not already present.
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
- 过滤后的route进行处理,在配置文件中,设置了默认路由前缀
zuul.servletPath=/services
,所以设置route.setStripPrefix(flase)
关闭移除代理前缀的动作。
for (ZuulRoute route : routes.values()) {
route.setStripPrefix(false);
}
网友评论