目录
- 建议
- Redis集群模式,或者来多一个二级缓存
- 暴露的接口,建议只能内网访问
准备数据表
DDL 点击查看
-- auto-generated definition
drop table if exists route;
create table route
(
id varchar(64) not null comment '主键'
primary key,
app_id int not null comment '应用ID',
app_name varchar(64) not null comment '应用名字',
target_url varchar(128) null comment '目标地址',
seq int default 0 not null comment '执行顺序',
create_user varchar(64) not null comment '创建时间',
update_user varchar(64) null,
create_time datetime null,
update_time datetime null,
deleted int(1) default 0 not null comment '是否删除(1是0否)'
)
comment '网关主表';
create index idx_app_id
on route (app_id);
-- auto-generated definition
drop table if exists route_filter;
create table route_filter
(
id bigint auto_increment comment '主键ID'
primary key,
route_id varchar(64) not null,
filter_name varchar(64) not null comment '过滤器名字',
deleted int(1) null comment '是否删除'
)
comment '网关过滤器';
create index idx_route_id
on route_filter (route_id);
-- auto-generated definition
drop table if exists route_filter_args;
create table route_filter_args
(
id bigint auto_increment
primary key,
filter_id bigint not null,
`key` varchar(64) not null,
value varchar(64) null,
deleted int(1) null
);
create index idx_filter_id
on route_filter_args (filter_id);
-- auto-generated definition
drop table if exists route_metadata;
create table route_metadata
(
id bigint auto_increment comment '主键ID'
primary key,
route_id varchar(64) not null,
`key` varchar(64) not null comment '元数据key',
value varchar(128) null comment '元数据值',
create_user varchar(64) null,
update_user varchar(64) null,
create_time datetime null,
update_time datetime null,
deleted int(1) default 0 not null comment '是否删除(1是0否)'
)
comment '网关元数据表';
create index idx_route_id
on route_metadata (route_id);
-- auto-generated definition
drop table if exists route_predicate;
create table route_predicate
(
id bigint auto_increment
primary key,
route_id varchar(64) not null,
predicate_name varchar(64) not null comment '断言名字',
update_time datetime null,
create_user varchar(64) null,
update_user varchar(64) null,
create_time datetime null,
deleted int(1) default 0 not null
)
comment '网关断言';
create index idx_route_id
on route_predicate (route_id);
-- auto-generated definition
drop table if exists route_predicate_args;
create table route_predicate_args
(
id bigint auto_increment
primary key,
predicate_id bigint not null,
`key` varchar(64) null,
value varchar(128) null,
update_user varchar(64) null,
create_user varchar(64) null,
update_time datetime null,
create_time datetime null,
deleted int(1) default 0 not null
);
create index idx_predicate_id
on route_predicate_args (predicate_id);
--
INSERT INTO `gateway_center`.`route`(`id`, `app_id`, `app_name`, `target_url`, `seq`, `create_user`, `update_user`, `create_time`, `update_time`, `deleted`) VALUES ('user_route', 1, 'app1', 'lb://user-center', 0, 'eddie', 'eddie', '2020-07-02 13:11:31', '2020-07-02 13:11:33', 0);
INSERT INTO `gateway_center`.`route`(`id`, `app_id`, `app_name`, `target_url`, `seq`, `create_user`, `update_user`, `create_time`, `update_time`, `deleted`) VALUES ('myRoute', 1, 'app1', 'https://www.baidu.com', 0, 'eddie', 'eddie', '2020-07-02 13:13:23', '2020-07-02 13:13:24', 0);
INSERT INTO `gateway_center`.`route_filter`(`id`, `route_id`, `filter_name`, `deleted`) VALUES (1, 'user_route', 'AddRequestParameter', 0);
INSERT INTO `gateway_center`.`route_filter`(`id`, `route_id`, `filter_name`, `deleted`) VALUES (2, 'before_route', 'AddRequestHeader', 0);
INSERT INTO `gateway_center`.`route_filter_args`(`id`, `filter_id`, `key`, `value`, `deleted`) VALUES (1, 1, '_genkey_0', 'header', 0);
INSERT INTO `gateway_center`.`route_filter_args`(`id`, `filter_id`, `key`, `value`, `deleted`) VALUES (2, 1, '_genkey_1', 'addHeader', 0);
INSERT INTO `gateway_center`.`route_filter_args`(`id`, `filter_id`, `key`, `value`, `deleted`) VALUES (3, 2, '_genkey_0', 'param', 0);
INSERT INTO `gateway_center`.`route_filter_args`(`id`, `filter_id`, `key`, `value`, `deleted`) VALUES (4, 2, '_genkey_1', 'addParam', 0);
INSERT INTO `gateway_center`.`route_predicate`(`id`, `route_id`, `predicate_name`, `update_time`, `create_user`, `update_user`, `create_time`, `deleted`) VALUES (1, 'user_route', 'Path', '2020-07-02 13:14:11', 'eddie', 'eddie', '2020-07-02 13:14:22', 0);
INSERT INTO `gateway_center`.`route_predicate`(`id`, `route_id`, `predicate_name`, `update_time`, `create_user`, `update_user`, `create_time`, `deleted`) VALUES (2, 'myRoute', 'Path', '2020-07-02 13:14:11', 'eddie', 'eddie', '2020-07-02 13:14:22', 0);
INSERT INTO `gateway_center`.`route_predicate_args`(`id`, `predicate_id`, `key`, `value`, `update_user`, `create_user`, `update_time`, `create_time`, `deleted`) VALUES (1, 1, 'pattern', '/users/**', 'eddie', 'eddie', '2020-07-02 13:20:56', '2020-07-02 13:20:57', 0);
INSERT INTO `gateway_center`.`route_predicate_args`(`id`, `predicate_id`, `key`, `value`, `update_user`, `create_user`, `update_time`, `create_time`, `deleted`) VALUES (2, 2, 'pattern', '/home', 'eddie', 'eddie', '2020-07-02 15:06:58', '2020-07-02 15:06:59', 0);
元数据,Spring Cloud Hoxton 之前的版本是没有的
Maven 依赖 (主要部分)
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置暴露健康
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
操作数据
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware {
private final RouteMapper routeMapper;
private final RouteMetadataMapper routeMetadataMapper;
private final RouteFilterMapper routeFilterMapper;
private final RouteFilterArgsMapper routeFilterArgsMapper;
private final RoutePredicateMapper predicateMapper;
private final RoutePredicateArgsMapper routePredicateArgsMapper;
private final StringRedisTemplate stringRedisTemplate;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public int addRoute(Route route) {
//插入路由
int result = routeMapper.insert(route);
//插入过滤器
List<RouteFilter> filters = route.getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (RouteFilter routeFilter : filters) {
routeFilterMapper.insert(routeFilter);
List<RouteFilterArgs> filterArgs = routeFilter.getFilterArgs();
for (RouteFilterArgs routeFilterArgs : filterArgs) {
routeFilterArgsMapper.insert(routeFilterArgs);
}
}
}
//插入元数据
List<RouteMetadata> metadatas = route.getMetadatas();
if (!CollectionUtils.isEmpty(metadatas)) {
for (RouteMetadata routeMetadata : metadatas) {
routeMetadataMapper.insert(routeMetadata);
}
}
//插入断言
List<RoutePredicate> predicates = route.getPredicates();
if (!CollectionUtils.isEmpty(predicates)) {
for (RoutePredicate routePredicate : predicates) {
predicateMapper.insert(routePredicate);
List<RoutePredicateArgs> predicateArgs = routePredicate.getPredicateArgs();
for (RoutePredicateArgs routePredicateArgs : predicateArgs) {
routePredicateArgsMapper.insert(routePredicateArgs);
}
}
}
stringRedisTemplate.opsForValue().set(GATEWAY_ROUTES, new Gson().toJson(routeList()));
refresh();
return result;
}
@Override
public int deleteRoute(Route route) {
int result = routeMapper.deleteRouteById(route);
stringRedisTemplate.opsForValue().set(GATEWAY_ROUTES, new Gson().toJson(routeList()));
refresh();
return result;
}
@Override
public int deleteRoute() {
int result = routeMapper.remove();
stringRedisTemplate.delete(GATEWAY_ROUTES);
refresh();
return result;
}
@Override
public void refresh() {
stringRedisTemplate.delete(GATEWAY_ROUTES);
stringRedisTemplate.opsForValue().set(GATEWAY_ROUTES, new Gson().toJson(routeList()));
publisher.publishEvent(new RefreshRoutesEvent(this));
}
@Override
public List<Route> routeList() {
try {
String routeStr = stringRedisTemplate.opsForValue().get(GATEWAY_ROUTES);
if (!StringUtils.isEmpty(routeStr)) {
return new Gson().fromJson(routeStr, new TypeToken<List<Route>>() {
}.getType());
}
} catch (Exception e) {
log.error("[Gateway-1] 从 Redis 获取路由错误:", e);
}
List<Route> routes = routeMapper.findAll();
if (!CollectionUtils.isEmpty(routes)) {
for (Route route : routes) {
route.setMetadatas(routeMetadataMapper.selectByRouteId(route.getId()));
route.setFilters(getRouteFilters(route.getId()));
route.setPredicates(getRoutePredicates(route.getId()));
}
}
try {
stringRedisTemplate.opsForValue().set(GATEWAY_ROUTES, new Gson().toJson(routes));
} catch (Exception e) {
log.error("[Gateway-2] 从 Redis 获取路由错误:", e);
}
return routes;
}
/**
* 查询过滤器数据
*
* @param routeId
* @return
*/
private List<RouteFilter> getRouteFilters(String routeId) {
List<RouteFilter> fileters = routeFilterMapper.selectByRouteId(routeId);
if (!CollectionUtils.isEmpty(fileters)) {
for (RouteFilter routeFilter : fileters) {
// 查询过滤器参数集合
routeFilter.setFilterArgs(routeFilterArgsMapper.selectByFilterId(routeFilter.getId()));
}
}
return fileters;
}
/**
* 查询断言列表
*
* @param routeId
* @return
*/
private List<RoutePredicate> getRoutePredicates(String routeId) {
List<RoutePredicate> predicateList = predicateMapper.selectByRouteId(routeId);
for (RoutePredicate routePredicate : predicateList) {
// 查询断言参数列表
routePredicate.setPredicateArgs(routePredicateArgsMapper.selectByPredicateId(routePredicate.getId()));
}
return predicateList;
}
}
配置获取路由信息
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
private final RouteService routeService;
private static final String HTTP = "http";
private static final String HTTPS = "https";
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
log.info("[gateway] change gate start");
List<RouteDefinition> definitions = getDefinitions();
log.info("[gateway] change gate success:{}", new Gson().toJson(definitions));
return Flux.fromIterable(definitions);
}
/**
* 获取网关配置
*
* @return
*/
private List<RouteDefinition> getDefinitions() {
List<Route> routes = routeService.routeList();
List<RouteDefinition> routeDefinitions = new ArrayList<>(routes.size());
for (Route route : routes) {
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(route.getId());
routeDefinition.setOrder(route.getSeq());
routeDefinition.setMetadata(getMetadata(route.getMetadatas()));
routeDefinition.setFilters(getFilters(route.getFilters()));
routeDefinition.setPredicates(getPredicates(route.getPredicates()));
routeDefinition.setUri(getUri(route.getTargetUrl()));
routeDefinitions.add(routeDefinition);
}
return routeDefinitions;
}
/**
* 获取断言列表
*
* @param predicates
* @return
*/
private List<PredicateDefinition> getPredicates(List<RoutePredicate> predicates) {
List<PredicateDefinition> definitions = new ArrayList<>(predicates.size());
for (RoutePredicate routePredicate : predicates) {
PredicateDefinition definition = new PredicateDefinition();
definition.setArgs(getPredicateArg(routePredicate.getPredicateArgs()));
definition.setName(routePredicate.getPredicateName());
definitions.add(definition);
}
return definitions;
}
/**
* 获取断言参数
*
* @param predicateArgs
* @return
*/
private Map<String, String> getPredicateArg(List<RoutePredicateArgs> predicateArgs) {
Map<String, String> map = new HashMap<>();
for (RoutePredicateArgs routePredicateArg : predicateArgs) {
map.put(routePredicateArg.getKey(), routePredicateArg.getValue());
}
return map;
}
/**
* 获取过滤器
*
* @param filters
* @return
*/
private List<FilterDefinition> getFilters(List<RouteFilter> filters) {
List<FilterDefinition> definitions = new ArrayList<>(filters.size());
for (RouteFilter routeFilter : filters) {
FilterDefinition definition = new FilterDefinition();
definition.setName(routeFilter.getFilterName());
definition.setArgs(getFilterArgs(routeFilter.getFilterArgs()));
definitions.add(definition);
}
return definitions;
}
/**
* 获取过滤器参数
*
* @param filterArgs
* @return
*/
private Map<String, String> getFilterArgs(List<RouteFilterArgs> filterArgs) {
Map<String, String> map = new HashMap<>();
for (RouteFilterArgs routeFilterArg : filterArgs) {
map.put(routeFilterArg.getKey(), routeFilterArg.getValue());
}
return map;
}
/**
* 获取地址uri
*
* @param url
* @return
*/
private URI getUri(String url) {
if (url.startsWith(HTTP) || url.startsWith(HTTPS)) {
return UriComponentsBuilder.fromHttpUrl(url).build().toUri();
}
return UriComponentsBuilder.fromPath(url).build().toUri();
}
/**
* 获取元数据map
*
* @param metadatas
* @return
*/
private Map<String, Object> getMetadata(List<RouteMetadata> metadatas) {
Map<String, Object> map = new HashMap<>();
for (RouteMetadata routeMetadata : metadatas) {
map.put(routeMetadata.getKey(), routeMetadata.getValue());
}
return map;
}
}
暴露控制层接口
建议是在Nginx不屏蔽了,或者其他安全手段。
@RestController
@RequestMapping("/route")
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class RouteController {
private final RouteService routeService;
@RequestMapping("refresh")
public String refreshRoute() {
routeService.refresh();
return "succee";
}
@GetMapping("/list")
public List<Route> routeList() {
return routeService.routeList();
}
@PutMapping("/deleteOne")
public Integer deleteRoute(@RequestBody Route route) {
return routeService.deleteRoute(route);
}
@GetMapping("/delete")
public Integer delete() {
return routeService.deleteRoute();
}
@PostMapping("/add")
public Integer add(@RequestBody Route route) {
return routeService.addRoute(route);
}
}
其余都是按表生成就不黏,看图
- 参考网址