该项目基于SpringBoot+Eureka+Mybaties实现动态路由。
1、引入SpringCloudGateway的maven依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.1.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>spring-cloud-gateway-core</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.1.2.RELEASE</version>
</dependency>
<!-- 引入健康监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
<scope>runtime</scope>
</dependency>
<!--阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
若引入其他base项目,请排除对 spring-boot-starter-web 的依赖:
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
2、基础数据结构构建
路由配置表结构:
CREATE TABLE `gateway_route` (
`id` bigint(20) NOT NULL COMMENT '主键',
`route_name` varchar(500) NOT NULL COMMENT '路由名称',
`predicates` varchar(500) DEFAULT NULL COMMENT '路由断言',
`filters` varchar(500) DEFAULT NULL COMMENT '过滤器',
`service_uri` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '服务定位符 lb://XXX',
`route_order` int(6) DEFAULT NULL COMMENT '优先级',
`status` int(3) NOT NULL DEFAULT '1' COMMENT '状态: 1-有效,2-无效',
`create_by` varchar(30) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建日期',
`modify_by` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '更新人',
`modify_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='服务路由';
//数据如下:
INSERT INTO gateway_route (`id`, `route_name`, `predicates`, `filters`, `service_uri`, `route_order`, `status`, `create_by`, `create_time`, `modify_by`, `modify_time`) VALUES ('202009011800', 'sys', '[{\"name\": \"Path\",\"args\": {\"pattern\": \"/sys/**\"}}]', '[{\"name\": \"RewritePath\",\"args\": {\"regexp\": \"/sys/(?<remaining>.*)\",\"replacement\": \"/${remaining}\"}}]', 'lb://appl-zb-hospital-system', '0', '1', 'sys', '2020-09-01 17:59:21', NULL, NULL);
3、项目配置:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://XX.XX.XX.XX:3306/dbName?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: password
server:
port: xxx
ssl:
#ssl配置,若gateway使用HTTPS,需要添加如下配置,
#注意若使用该方式 服务实例向Eureka注册时应开启安全通信的端口
key-store: classpath:xxx.pfx
key-store-password: xxx
key-store-type: xxx
enabled: true
mybatis:
#对应项目中的model对应位置
type-aliases-package: com.xxx.gateway.model
#对应项目中xxxMapper.xml对应位置,一般为 resources/mapper/
mapper-locations: classpath*:mapper/**/*Mapper.xml
logging:
level:
#打印输出执行sql 对应xxxxMapper.java 路径
com.xxxx.gateway.mapper: debug
4、项目相关Bean的配置:
@EnableDiscoveryClient
@EnableEurekaClient
//@MapperScan("com.xxxx.gateway.mapper") 扫描 mybaties对应的 xxxxMapper.java 亦可在对应类上使用@Mapper注解
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
//路由定义定位器,用以加载路由信息到内存中
@Bean
public InMemoryRouteDefinitionRepository routeDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
}
5、路由信息加载(GatewayServiceImpl):
@Autowired
private InMemoryRouteDefinitionRepository routeDefinitionRepository;
@Autowired
private GatewayRouteMapper gatewayRouteMapper;
/**
* 加载数据库路由信息到内存
* @return
* @throws Exception
*/
@Override
public String loadRouteDefinition() throws Exception {
try {
//获取数据库中路由配置信息
List<GatewayRoute> gatewayRouteList = gatewayRouteMapper.getRoutes();
for (GatewayRoute gatewayRoute: gatewayRouteList) {
//将库中路由信息封装为RouteDefinition对象,加载到内存中
RouteDefinition definition = assembleRouteDefinition(gatewayRoute);
if(definition!=null){
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
}
}
return "success";
} catch (Exception e) {
log.error("Exception: ",e);
return "failure";
}
}
/**
* 封装成为 RouteDefinition 对象
* @param gatewayDefine
* @return
*/
private RouteDefinition assembleRouteDefinition(GatewayRoute gatewayRoute) {
RouteDefinition definition = new RouteDefinition();
definition.setId(gatewayRoute.getId());
definition.setOrder(gatewayRoute.getRouteOrder());
if(StringUtils.isEmpty(gatewayRoute.getPredicates()) || StringUtils.isEmpty(gatewayRoute.getFilters())
|| StringUtils.isEmpty(gatewayRoute.getServiceUri())){
return null;
}
if(gatewayRoute.getPredicateDefinition().size()>0)
definition.setPredicates(gatewayRoute.getPredicateDefinition());
if(gatewayRoute.getFilterDefinition().size()>0)
definition.setFilters(gatewayRoute.getFilterDefinition());
URI uri = UriComponentsBuilder.fromUriString(gatewayRoute.getServiceUri()).build().toUri();
definition.setUri(uri);
return definition;
}
6、启动路由加载配置:
@Component
public class ApplicationStartup implements ApplicationRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationStartup.class);
@Autowired
private GatewayService gatewayService;
@Override
public void run(ApplicationArguments args) throws Exception {
//调用上述方法
gatewayService.loadRouteDefinition();
}
}
之后启动项目,查看加载路由信息:
https://localhost:xxx/actuator/gateway/routes