简介
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用
Git地址
https://gitee.com/wqrzsy/lp-demo/tree/master/lp-springboot-gateway
更多demo请关注
springboot demo实战项目
java 脑洞
java 面试宝典
开源工具
基础项目
springboot spring cloud gateway demo实战项目
聚合
1. swagger
效果图:
聚合后通过访问gateway的swagger-ui页面就能访问所有微服务的swagger了,是不是很方便
先分享作者连接: https://blog.csdn.net/ttzommed/article/details/81103609
思路:通过gateway获取注册到eureka上的服务列表,然后把服务的url封装成swagger资源,让swagger-ui页面能获取到
关键类: swagger资源提供者
@Component
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
public static final String EUREKA_SUB_PRIX = "CompositeDiscoveryClient_";
private final DiscoveryClientRouteDefinitionLocator routeLocator;
public SwaggerProvider(DiscoveryClientRouteDefinitionLocator routeLocator) {
this.routeLocator = routeLocator;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
//从DiscoveryClientRouteDefinitionLocator 中取出routes,构造成swaggerResource
routeLocator.getRouteDefinitions().subscribe(routeDefinition -> {
String resourceName = routeDefinition.getId().substring(EUREKA_SUB_PRIX.length());
String location = routeDefinition.getPredicates().get(0).getArgs().get("pattern").replace("/**", API_URI);
resources.add(swaggerResource(resourceName, location));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
关键类: swagger资源访问Api
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
2. 接口版本化
先分享作者连接: https://blog.mariojd.cn/how-to-design-spring-boot-api-version-number-elegantly.html
效果图:
思路:通过RequestMappingHandlerMapping在匹配路径的时候做版本号匹配
关键类: 映射RequestCondition
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
// 扫描类上的 @ApiVersion
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createRequestCondition(apiVersion);
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
// 扫描方法上的 @ApiVersion
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createRequestCondition(apiVersion);
}
private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion) {
if (Objects.isNull(apiVersion)) {
return null;
}
int value = apiVersion.value();
Assert.isTrue(value >= 1, "Api Version Must be greater than or equal to 1");
return new ApiVersionCondition(value);
}
}
关键类: 路径匹配的逻辑
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
/**
* 接口路径中的版本号前缀,如: api/v[1-n]/test
*/
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+)/");
private int apiVersion;
ApiVersionCondition(int apiVersion) {
this.apiVersion = apiVersion;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
// 最近优先原则,方法定义的 @ApiVersion > 类定义的 @ApiVersion
return new ApiVersionCondition(other.getApiVersion());
}
/**
* 检查当前请求匹配条件和指定请求request是否匹配,如果不匹配返回null,
* 如果匹配,生成一个新的请求匹配条件,该新的请求匹配条件是当前请求匹配条件
* 针对指定请求request的剪裁。
* 举个例子来讲,如果当前请求匹配条件是一个路径匹配条件,包含多个路径匹配模板,
* 并且其中有些模板和指定请求request匹配,那么返回的新建的请求匹配条件将仅仅
* 包含和指定请求request匹配的那些路径模板。
* @param request
* @return
*/
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
// 获得符合匹配条件的ApiVersionCondition
int version = Integer.valueOf(m.group(1));
if (version >= getApiVersion()) {
return this;
}
}
return null;
}
/**
* 针对指定的请求对象request比较两个请求匹配条件。
* 该方法假定被比较的两个请求匹配条件都是针对该请求对象request调用了
* #getMatchingCondition方法得到的,这样才能确保对它们的比较
* 是针对同一个请求对象request,这样的比较才有意义(最终用来确定谁是
* 更匹配的条件)。
*
* @param other
* @param request
* @return
*/
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
// 当出现多个符合匹配条件的ApiVersionCondition,优先匹配版本号较大的
return other.getApiVersion() - getApiVersion();
}
public int getApiVersion() {
return apiVersion;
}
}
demo项目导入
PS :本项目使用了JDK11,主要导入的时候要先安装JDK11
参考: https://www.jianshu.com/p/cd0275a2f5fb
如果这篇文章对你有帮助请给个star