gateway整合nacos实现动态路由和swagger的接口文档
一、配置nacos实现动态路由
二、整合swagger
1.1. 配置naos的config
nacos依赖 版本<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- spring cloud nacos discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableDiscoveryClient
public class NacosDiscoveryConfiguration {
}
1.2.配置nacos路由数据源
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
/**
* nacos路由数据源
*
*/
@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {
private String routerDataId;
private ApplicationEventPublisher publisher;
private NacosConfigProperties nacosConfigProperties;
public NacosRouteDefinitionRepository(String routerDataId, ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) {
this.routerDataId = routerDataId;
this.publisher = publisher;
this.nacosConfigProperties = nacosConfigProperties;
addListener();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(routerDataId, nacosConfigProperties.getGroup(), 5000);
List<RouteDefinition> routeDefinitions = getListByStr(content);
return Flux.fromIterable(routeDefinitions);
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(Collections.EMPTY_LIST);
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(routerDataId, nacosConfigProperties.getGroup(), new Listener() {
public Executor getExecutor() {
return null;
}
public void receiveConfigInfo(String configInfo) {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
/**
* 解析路由
* @param content
* @return
*/
private List<RouteDefinition> getListByStr(String content) {
if (StringUtils.isNotEmpty(content)) {
return JSONObject.parseArray(content, RouteDefinition.class);
}
return new ArrayList<>(0);
}
}
1.3.动态路由配置
import com.alibaba.cloud.nacos.NacosConfigProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 动态路由配置
*/
@Configuration
public class DynamicRouteConfig {
@Autowired
private ApplicationEventPublisher publisher;
@Value("${spring.cloud.nacos.config.router-data-id:gateway-router.json}")
private String routerDataId;
/**
* Nacos实现方式
*/
@Configuration
public class NacosDynRoute {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Bean
public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() {
return new NacosRouteDefinitionRepository(routerDataId, publisher, nacosConfigProperties);
}
}
}
1.4.bootstrap.yml文件的配置
spring:
cloud:
# nacos-配置
nacos:
config:
# 是否开启配置中心,默认true
enabled: true
server-addr: ip地址:8848
file-extension: yaml
encode: UTF-8
group: FINANCE
router-data-id: gateway-router.json #nacos的配置中心的配置文件名
discovery:
server-addr: ip地址:8848
group: FINANCE #组名,与微服务组名一致
#可以访问你在配置中心配置的路由
#访问路径:IP地址:1111/actuator/gateway/routes
management:
endpoints:
health:
show-details: always
web:
exposure:
include: '*'
#日志文件的配置
logging:
level:
root: WARN
com.woniu: DEBUG
pattern:
console: "${CONSOLE_LOG_PATTERN:\
%clr(${LOG_LEVEL_PATTERN:%5p}) \
%clr(|){faint} \
%clr(%logger{39}){cyan} \
%clr(:){faint} \
%m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}"
1.5.nacos配置中的配置文件
访问地址:http://ip地址:8848/nacos
配置路由规则
[{
"filters": [{
"args": {
"parts": "0"
},
"name": "StripPrefix"
}],
"id": "order_route",
"order": 0,
"predicates": [{
"args": {
"pattern": "/order/**"
},
"name": "Path"
}],
"uri": "lb://finance-order"
},
{
"filters": [{
"args": {
"parts": "0"
},
"name": "StripPrefix"
}],
"id": "user_route",
"order": 0,
"predicates": [{
"args": {
"pattern": "/user/**"
},
"name": "Path"
}],
"uri": "lb://finance-usercenter"
}]
说明:动态路由的目的,在不重启和修改getaway配置文件的情况下,在nacos的配置中心可自由增减路由,即可实现路由访问。
-----------------------------------------整合swagger--------------------------------------------
2.1.swagger的配置文件
依赖
<!-- Swagger 接口文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
@RestController
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("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/")
public Mono<ResponseEntity> swaggerResourcesN() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/csrf")
public Mono<ResponseEntity> swaggerResourcesCsrf() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
import lombok.AllArgsConstructor;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
@Component
@Primary
@AllArgsConstructor
public class MySwaggerResourceProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
//取出gateway的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//结合配置的route-路径(Path),和route过滤,只获取有效的route节点
gatewayProperties.getRoutes().stream()
.filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs()
.get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)))));
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;
}
}
2.2.application.yml文件配置
spring:
application:
name: finance-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
#路由规则:如果不使用swagger可不用配置
#要使用swagger必须配置,
#通过Path=/finance-order/来区分不同的微服务中的swagger
routes:
- id: order微服务
uri: lb://finance-order
predicates:
- Path=/finance-order/**
filters:
- StripPrefix=1
- id: user微服务
uri: lb://finance-usercenter
predicates:
- Path=/finance-usercenter/**
filters:
- StripPrefix=1
- id: product微服务
uri: lb://finance-product
predicates:
- Path=/finance-product/**
filters:
- StripPrefix=1
- id: pay微服务
uri: lb://finance-pay
predicates:
- Path=/finance-pay/**
filters:
- StripPrefix=1
- id: promotion微服务
uri: lb://finance-promotion
predicates:
- Path=/finance-promotion/**
filters:
- StripPrefix=1
2.3.每一个微服务要配置自己的swagger(依赖是一样的)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean("xxx模块")
public Docket createApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(myApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("你的controller路径"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo myApiInfo() {
return new ApiInfoBuilder()
.title("xxx项目-xxx微服务接口文档")
.description("提供xxx模块的文档")
.termsOfServiceUrl("http://www.xxx.com/")
.version("1.0")
.build();
}
}
2.3.1.swagger的使用
controller
@RestController
@Api(description = "对外暴露接口")
@Slf4j
public class OrderController {
@PostMapping("xxxx")
@ApiOperation(value = "这是xxxx方法")
//对象不需要配置
public Object xxx(@RequestBody 对象 fo){
return "ok";
}
@ApiOperation("xxxx方法")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId",value = "xxx",required = true),
@ApiImplicitParam(name = "currentId",value = "xxx",required = true),
@ApiImplicitParam(name = "payPassword",value = "xxx",required = true)
})
@GetMapping("xxx")
public Object xxx(String userId,Long currentId,String payPassword){
return "ok";
}
}
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("xxx")
public class xxx{
@ApiModelProperty("xxx")
private Long xxx;
@ApiModelProperty("xxx")
private String xxx;
}
说明:getaway整合swagger,用于把多个微服务的swagger整合到一起,方便查看,以及在getaway中配置过滤器时,验证可不可行。