采用jwt的方式,前端携带token进行鉴权,后端采用微服务的方式提供服务,我们需要采用网关的方式对前端的访问进行限流、鉴权、负载均衡、监控、路由和灰度发布。 后端其他人员不再关注与权限与安全相关需求,只需要专注业务开发。
API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。
因为gateway的特殊性LoadBalancerClientFilter
过滤器主要解析lb:// 为前缀的路由规则,在通过LoadBalancerClient方法获取到需要的服务实例,从而实现负载均衡。在这里我们要写自己的负载均衡就需要重新需要重写LoadBalancerClientFilter 过滤器LoadBalancerClientFilter 介绍:次过滤器作用在url以lb开头的路由,然后利用loadBalancer来获取服务实例,构造目标requestUrl,设置到GATEWAYREQUESTURL_ATTR属性中,供NettyRoutingFilter使用。
GatewayLoadBalancerClientAutoConfiguration
在初始化会检测@ConditionalOnBean(LoadBalancerClient.class) 是否存在,如果存在就会加载LoadBalancerClientFilter
负载过滤器
代码实现github:
网关中常用的Filter实现有两种方式,一种是GatewayFilter
,实现该过滤器需要根据url来指定需要的过滤器,并且需要实现Ordered
(数字小的优先);第二种是GlobalFilter,实现该过滤器则所有的请求都会经过该过滤器。
常规的路由的实现方式主要是在yml里面进行配置,这样的话需要每次都重启服务来配置路由,非常麻烦。因此通过某种方式实现动态修改路由就很重要。直接上代码:
在springboot启动的时候就加载路由规则,同时在修改路由规则之后也会调用GatewayServiceHandler
刷新一遍路由规则到内存中。
@Service
public class GatewayServiceHandler implements ApplicationEventPublisherAware, CommandLineRunner{
private final static Logger log = LoggerFactory.getLogger(GatewayServiceHandler.class);
@Autowired
private RedisRouteDefinitionRepository routeDefinitionWriter;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
// 获取数据dao
@Autowired
private GatewayRouteDefinitionMapper gatewayRouteMapper;
// springboot启动后执行
@Override
public void run(String... args){
this.loadRouteConfig();
}
/**
* 更新路由
* @return
*/
public String loadRouteConfig() {
//从数据库拿到路由配置
List<GatewayRouteDefinition> gatewayRouteList = gatewayRouteMapper.queryAllRoutes();
log.info("网关配置信息:=====>"+ JSON.toJSONString(gatewayRouteList));
gatewayRouteList.forEach(gatewayRoute -> {
//todo 增加断言/路由规则校验
// 创建路由对象
RouteDefinition definition = new RouteDefinition();
definition.setId(gatewayRoute.getId());
// 设置路由执行顺序
definition.setOrder(gatewayRoute.getOrder());
// 设置路由规则转发的目标uri
URI uri = URI.create(gatewayRoute.getUri());
definition.setUri(uri);
// 设置路由断言
String predicatesJson = gatewayRoute.getPredicatesJson();
List<PredicateDefinition> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(predicatesJson)){
List<GatewayPredicateDefinition> predicateDefinitions = JSONArray.parseArray(predicatesJson, GatewayPredicateDefinition.class);
predicateDefinitions.stream().forEach(it->{
PredicateDefinition p = new PredicateDefinition();
p.setName(it.getName());
p.setArgs(it.getArgs());
predicates.add(p);
});
}
definition.setPredicates(predicates);
// 设置过滤器
String filtersJson = gatewayRoute.getFiltersJson();
List<FilterDefinition> filters = new ArrayList<>();
if (StringUtils.isNotBlank(filtersJson)){
List<GatewayFilterDefinition> filterDefinitions = JSONArray.parseArray(filtersJson, GatewayFilterDefinition.class);
filterDefinitions.stream().forEach(it->{
FilterDefinition f = new FilterDefinition();
f.setName(it.getName());
f.setArgs(it.getArgs());
filters.add(f);
});
}
definition.setFilters(filters);
// 保存路由
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
});
// 发送更新路由事件
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
/**
* 删除路由
* @param routeId
*/
public void deleteRoute(String routeId){
routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
// 发送更新路由事件
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
}
上面代码是,下面是路由缓存机制
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
//缓存的key
public static final String GATEWAY_ROUTES = "gateway";
@Autowired
private RedisTemplate redisTemplate;
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
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) {
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) {
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)));
});
}
}
@Component
public class LogFilter implements GatewayFilter, Ordered {
static final Logger logger = LogManager.getLogger("request");
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("=================进入自定义全局过滤器=============");
StringBuilder logBuilder = new StringBuilder();
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue().toUpperCase();
logBuilder.append(method).append(",").append(serverHttpRequest.getURI());
if("POST".equals(method)) {
String body = exchange.getAttributeOrDefault("cachedRequestBody", "");
if(StringUtils.isNotBlank(body)) {
logBuilder.append(",body=").append(body);
}
}
ServerHttpResponse serverHttpResponse = exchange.getResponse();
DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
String resp = new String(content, Charset.forName("UTF-8"));
logBuilder.append(",resp=").append(resp);
logger.info(logBuilder.toString());
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -20;
}
}
注意:
过滤器的核心是实现
AbstractGatewayFilterFactory
,例如下面的LogGatewayFilterFactory在加入到gateway的filterfactory(系统默认的会实现19种内置过滤方式)的代码中的时候会把“Log”截取下来,当做filter的名称,只有这样才能配置的时候获取到这个过滤器。
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public GatewayFilter apply(Object config) {
return new LogFilter();
}
}