gataway动态路基于数据库读取路由信息进行路由网关拦截等功能
1:需要使用注册中心 nacos eureka 等都可以
2:需要重写gateway的方法
package com.lary.gatewaydemo.ceshi.service;
import com.alibaba.fastjson.JSON;
import com.lary.gatewaydemo.ceshi.entity.GatewayRoute;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.lary.gatewaydemo.ceshi.service.RedisRouteDefinitionRepository.GATEWAY_ROUTES;
/**
* @program: gateway
* @description: sss
* @author: lary
* @create: 2021-08-05 15:28
*/
@Slf4j
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner {
@Autowired
private RedisRouteDefinitionRepository routeDefinitionWriter;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
@Autowired
private RedisTemplate redisTemplate;
@Resource
private GatewayRouteService gatewayRouteService;
@Override
public void run(String... args){
this.loadRouteConfig();
}
public String loadRouteConfig() {
log.info("====开始加载=====网关配置信息=========");
//删除redis里面的路由配置信息
redisTemplate.delete(GATEWAY_ROUTES);
//从数据库拿到基本路由配置
List<GatewayRoute> gatewayRouteList = gatewayRouteService.gatewayRoutes();
gatewayRouteList.forEach(gatewayRoute -> {
RouteDefinition definition=handleData(gatewayRoute);
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
});
this.publisher.publishEvent(new RefreshRoutesEvent(this));
log.info("=======网关配置信息===加载完成======");
return "success";
}
public void saveRoute(GatewayRoute gatewayRoute){
RouteDefinition definition=handleData(gatewayRoute);
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
public void update(GatewayRoute gatewayRoute) {
RouteDefinition definition=handleData(gatewayRoute);
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteRoute(String routeId){
routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* 路由数据转换公共方法
* @param gatewayRoute
* @return
*/
private RouteDefinition handleData(GatewayRoute gatewayRoute){
RouteDefinition definition = new RouteDefinition();
Map<String, String> predicateParams = new HashMap<>(8);
PredicateDefinition predicate = new PredicateDefinition();
FilterDefinition filterDefinition = new FilterDefinition();
Map<String, String> filterParams = new HashMap<>(8);
URI uri = null;
if(gatewayRoute.getUri().startsWith("http")){
//http地址
uri = UriComponentsBuilder.fromHttpUrl(gatewayRoute.getUri()).build().toUri();
}else{
//注册中心
uri = UriComponentsBuilder.fromUriString("lb://"+gatewayRoute.getUri()).build().toUri();
}
definition.setId(gatewayRoute.getServiceId());
// 名称是固定的,spring gateway会根据名称找对应的PredicateFactory
predicate.setName("Path");
String predicate1 = gatewayRoute.getPredicates();
predicateParams.put("pattern",gatewayRoute.getPredicates());
predicate.setArgs(predicateParams);
// 名称是固定的, 路径去前缀
filterDefinition.setName("StripPrefix");// 作用为去前缀 意思就是列入我的实际接口为localhost:8080/ceshi/ceshio2
// 那么他就会去掉ceshi 接口变为localhost:8080/ceshio2接口肯定不通,所以
// 例如我的地址为/ga/demo/**那么请求的地址也为这个。然后接口会去掉ga,ga即可调通
filterParams.put("_genkey_0", gatewayRoute.getFilters().toString());
filterDefinition.setArgs(filterParams);
definition.setPredicates(Arrays.asList(predicate));
definition.setFilters(Arrays.asList(filterDefinition));
definition.setUri(uri);
definition.setOrder(Integer.parseInt(gatewayRoute.getSort()));
System.out.println("definition:" + JSON.toJSONString(definition));
return definition;
}
package com.lary.gatewaydemo.ceshi.service;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
/**
* @program: gateway
* @description: peizhi
* @author: lary
* @create: 2021-08-05 15:25
*/
/**
*将定义好的路由表信息通过此类读写到redis中
*/
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
public static final String GATEWAY_ROUTES = "gateway_routes";
@Autowired
private RedisTemplate redisTemplate;
//请注意,此方法很重要,从redis取路由信息的方法,官方核心包要用,核心路由功能都是从redis取的
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
System.out.println("===============RedisRouteDefinitionRepository==============");
List<RouteDefinition> routeDefinitions = new ArrayList<>();
redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream().forEach(routeDefinition -> {
routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class));
});
return Flux.fromIterable(routeDefinitions);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
System.out.println("===============RedisRouteDefinitionRepository---save ==============");
return route
.flatMap(routeDefinition -> {
redisTemplate.opsForHash().put(GATEWAY_ROUTES, routeDefinition.getId(),
JSON.toJSONString(routeDefinition));
return Mono.empty();
});
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
System.out.println("===============RedisRouteDefinitionRepository---delete ==============");
return routeId.flatMap(id -> {
if (redisTemplate.opsForHash().hasKey(GATEWAY_ROUTES, id)) {
redisTemplate.opsForHash().delete(GATEWAY_ROUTES, id);
return Mono.empty();
}
return Mono.defer(() -> Mono.error(new NotFoundException("路由文件没有找到: " + routeId)));
});
}
}
配置文件:
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8040/eureka
server:
port: 8082
spring:
application:
name: service-gateway
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://*****:3306/mx_institute?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: meix
password: *****
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 1
数据库数据:
CREATE TABLE `gateway_route` (
`id` bigint(20) NOT NULL,
`route_id` varchar(255) DEFAULT NULL,
`uri` varchar(255) NOT NULL,
`predicates` varchar(255) NOT NULL,
`filters` varchar(255) DEFAULT NULL,
`sort` int(11) DEFAULT '0',
`remarks` varchar(255) DEFAULT NULL,
`create_time` date DEFAULT NULL,
`update_time` date DEFAULT NULL,
`is_deleted` int(11) DEFAULT '0',
`version` int(11) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
总结:如果把服务都写在数据库中,则无法做负载均衡,需要处理,所以目前引用注册中心进行,两个子服务的application-name都是一样即可做到负载均衡
如需源码可以留言