一、Gateway服务网关
1.1 什么是网关
服务网关又称API网关,是系统对外的唯一入口,封装了系统的内部架构。所有的客户端和消费端,都通过网关接入微服务,在网关层处理非业务逻辑。API网关不是微服务场景中的必须组件,如图没有网关,微服务一样可以对外提供服务
但是对应微服务数量较多,复杂度比较高的系统,使用网关可以带来一些好处
- 聚合接口,使得服务对调研者透明,降低客户端与服务端的耦合度
- 聚合后台服务,节省流量,提升用户体验
- 提供安全,流量控制、API监控、计费等功能
1.2 Spring Cloud Gateway
Spring Cloud Gateway做Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,它不仅提供统一的路由方式,并且基于Filter链的方式提供了网关的基本功能。旨在提供一种简单而有些的方法来路由到API。使用Spring Cloud Gateway有如下优点
- 客户端只与网关交互,降低客户端的复杂度
- 解决了特定场景的跨越问题
- 集成actuator,进行监控和健康检查
- 集成Hystrix,进行服务熔断和限流
解决的问题:
1.3 其他
网关除了以上功能外,还可以做如下功能
- 统一的安全认证(token验证),使用全局过滤器
- 验证码产生与验证、短信验证码认证,使用自定义端点(RouterFunctions)免校验
- Swagger的Api文档聚合,集成Swagger,过滤Swagger的端点进行处理
- 无需认证的配置信息,使用自定义端点免校验
二、Gateway环境与配置
2.1 pom.xml
这块所有的版本都在父工程定义好了
- SpringBoot 2.6.5
- SpringCloud 2021.0.1
- SpringCloudAlibaba 2021.0.1.0
2.1.1 gateway
gateway依赖了swagger和openfeign,这两个单独封装成公共包方便使用。自己学习可以合到一起,先看gateway的pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--添加bootstrap的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--gateway 网关依赖,内置webflux 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--swagger的依赖-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mms-common-swagger</artifactId>
</dependency>
<!--openfeign的依赖-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mms-common-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2 sawgger
swagger的使用前面讲过SpringBoot2.6.5集成Swagger3和Knife3,使用相同的依赖即可,替换上面的mms-common-openfeign
2.1.3 openfeign
替换上面的mms-common-swagger
<!--feign服务间调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--注册中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--客户端负载均衡策略,替换原来的Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
2.2 bootstrap.yml
server:
port: 8081
logging:
level:
com.alibaba.nacos.client.config.impl: ERROR
management:
endpoints:
web:
exposure:
include: "health,prometheus"
endpoint:
health:
show-details: always
metrics:
tags:
application: ${spring.application.name}
spring:
application:
name: gateway-center
#设置Web模式为reactive
main:
web-application-type: reactive
cloud:
nacos:
discovery:
namespace: 7d720891-624c-4677-90fe-9004a3f73888
server-addr: http://192.168.136.133:8848
file-extension: yml
# Nacos 服务开启认证时需要配置
# username:
# password:
# context-path: nacos
config:
namespace: 7d720891-624c-4677-90fe-9004a3f73888
server-addr: http://192.168.136.133:8848
file-extension: yml
# 读取Nacos配置时间(默认3000,单位:毫秒)
# timeout: 3000
# Nacos 服务开启认证时需要配置
# username:
# password:
# context-path: nacos
gateway:
authCenter: user-center
authCenterUrl: /token/check
routes:
- id: user
uri: lb://user-center
auth: true
predicates:
- Path=/user/**
filters:
#使用StripPrefixGatewayFilterFactory分割路由,1代表分割1截。作用是将/user去掉
- StripPrefix=1
#使用自定义的
- ValidateCode=true
urls:
- pattern: /user/oauth/**
- pattern: /user/token/logout
- id: admin
uri: lb://admin-service
auth: true
predicates:
- Path=/admin/**
filters:
- StripPrefix=1
swagger:
title: mms cloud API
license: Powered By yex
licenseUrl: https://demo.com/
terms-of-service-url: https://demo.com/
contact:
email: xx@qq.com
url: https://demo.com/about.html
authorization:
name: auth-center
auth-regex: ^.*$
authorization-scope-list:
- scope: server
description: server all
token-url-list:
- http://${GATEWAY-HOST:gateway-center}:${GATEWAY-PORT:8081}/auth/oauth/token
三、Swagger聚合配置
聚合Api文档就是将其他微服务的api文档地址合到一起,形成一个下拉选择。启动网关和用户中心看看效果
3.1 SwaggerProviderConfig
用于读取gateway的路由配置信息,封装成SwaggerResource,这些信息会在/doc.html页面展示
@Slf4j
@Primary
@Component
public class SwaggerProviderConfig implements SwaggerResourcesProvider {
public static final String API_URI = "/v3/api-docs";
@Autowired
private SwaggerProperties swaggerProperties;
@Autowired
private GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
Set<String> strings = new HashSet<>();// 记录已经添加过的server
gatewayProperties.getRoutes().stream()
.filter(routeDefinition -> !swaggerProperties.getIgnoreProviders().contains(routeDefinition.getUri().getHost()))
.forEach(routeDefinition -> {
String url = "/" + routeDefinition.getId() + API_URI;// 拼接url
if (!strings.contains(url)) {
strings.add(url);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setUrl(url);///设置服务文档user-center/v3/api-docs
swaggerResource.setName(routeDefinition.getUri().getHost());//设置服务名user-center
swaggerResource.setSwaggerVersion("3.0");
resources.add(swaggerResource);
}
}
);
return resources;
}
}
3.2 RouterFunctionConfiguration
gateway使用的webflux,不支持webmvc。对swagger的路由添加处理器,来处理接口请求
@Slf4j
@Configuration
@AllArgsConstructor
public class RouterFunctionConfiguration {
private final HystrixFallbackHandler hystrixFallbackHandler
private final SwaggerSecurityHandler swaggerSecurityHandler;
private final SwaggerUiHandler swaggerUiHandler;
private final SwaggerResourceHandler swaggerResourceHandler;
@Bean
public RouterFunction routerFunction() {
return RouterFunctions.route(
RequestPredicates.path("/fallback")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), hystrixFallbackHandler)
.andRoute(RequestPredicates.GET("/swagger-resources")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler)
.andRoute(RequestPredicates.GET("/swagger-resources/configuration/security")
.and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler);
}
}
3.3 SwaggerResourceHandler
@Slf4j
@Component
@AllArgsConstructor
public class SwaggerResourceHandler implements HandlerFunction<ServerResponse> {
private final SwaggerResourcesProvider swaggerResources;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(swaggerResources.get()));
}
}
3.4 SwaggerSecurityHandler
@Slf4j
@Component
public class SwaggerSecurityHandler implements HandlerFunction<ServerResponse> {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(
Optional.ofNullable(securityConfiguration)
.orElse(SecurityConfigurationBuilder.builder().build())));
}
}
3.5 SwaggerUiHandler
@Slf4j
@Component
public class SwaggerUiHandler implements HandlerFunction<ServerResponse> {
@Autowired(required = false)
private UiConfiguration uiConfiguration;
/**
* Handle the given request.
*
* @param request the request to handler
* @return the response
*/
@Override
public Mono<ServerResponse> handle(ServerRequest request) {
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(
Optional.ofNullable(uiConfiguration)
.orElse(UiConfigurationBuilder.builder().build())));
}
}
四、遇到的问题
4.1 webflux与mvc不兼容
webflux与mvc不兼容会导致各种问题
解决方法一:
在依赖中排除MVC相关的jar包
<!--spring cloud gateway是基于webflux的,与web不兼容-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
排除spring-boot-starter-web之后又可能导致一些类找不到报错,例如我这里使用了CommonResponseDataAdvice implements ResponseBodyAdvice
做全局返回值处理,就报ResponseBodyAdvice找不到。排除了CommonResponseDataAdvice 都不行
解决方法二:
在配置文件增加如下配置
spring:
#设置Web模式为reactive
main:
web-application-type: reactive
使用方案二,完美解决。
4.2 swagger异常
swagger文档出不来,f12发现/v3/api-docs报错,检查SwaggerProviderConfig的配置,一般是url配错。反复调试即可解决