文档:Spring Cloud Gateway
1.0:为什么使用网关小结:
nacos :注册中心,解决服务的注册与发现
Ribbon:客户端负载均衡器,解决微服务集群负载均衡的问题
Openfeign:声明式HTTP客户端,解决微服务之间远程调用问题
Sentinel:微服务流量防卫兵,以流量为入口,保护微服务,防止出现服务雪崩.
1.1:什么是Springcloud-gateway
1:springcloud-gateway是第二代网关,取代zuul
2:springcloud-gateway是基于 Spring Boot 2.x, Spring WebFlux和Project Reactor 构建的。netty服务器运行
3:是微服务的统一入口
1.2:Springcloud-gateway特点1:性能强劲(是zuul1.x的1.6倍)
2:功能强大(转发、监控、限流)
1.3:gateway核心概念
-
Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
-
Predicate(断言、谓词)
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
-
Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改1.4:gateway的工作流程
1:客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由 2:将其发送到 Gateway Web Handler。 3:Handler 再通过指 定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
1.5:搭建网关wfx-gateway 1.5.1:pom依赖
注意:不要依赖spring-boot-starter-web
org.springframework.boot spring-boot-starter-web
依赖如下:
1.5.2:基本配置org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-actuatorcom.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
配置文件采用yml
server: #gateway的端口 port: 8040 spring: application: name: wfx-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:88481.5.3:引导类
package com.wfx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class WfxGateway {
public static void main(String[] args) {
SpringApplication.run(WfxGateway.class,args);
}
}
二:路由配置姿势
2.1:路由到指定URL
spring: cloud: gateway: routes: - id: baidu uri: http://www.baidu.com predicates: - Path= @Component public class MyHeaderRoutePredicateFactory extends AbstractRoutePredicateFactory{ public MyHeaderRoutePredicateFactory() { super(MyConfig.class); } @Override public Predicate apply(MyConfig config) { return new Predicate () { @Override public boolean test(ServerWebExchange exchange) { //获取请求头 //需求:请求头中必须要有 指定的kv 键值对 // String value = exchange.getRequest().getHeaders().getFirst(config.getKey()); // if (value == null) { // // return false; // }else{ // if(value.equals(config.getValue())){ // return true; // }else{ // return false; // } // } if(StringUtils.isEmpty(config.getValue())){//只配置了key,但是没有配置value if(exchange.getRequest().getHeaders().containsKey(config.getKey())){ return true; }else{ return false; } }else{//同时配置了key和value //根据key获取value String value = exchange.getRequest().getHeaders().getFirst(config.getKey()); if(config.getValue().equals(value)){ return true; }else{ return false; } } } }; } //获取配置参数 @Override public List shortcutFieldOrder() { //- MyHeader=bbb,cccc //[bbb,cccc] //bbb赋值给MyConfig#key //cccc赋值给MyConfig#value return Arrays.asList("key","value"); } }
server:
#网关微服务的启动端口
port: 8040
spring:
application:
name: wfx-gateway #微服务的应用名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos-server的服务地址
gateway:
#配置路由规则
routes:
- id: wfx-goods
#请求转发到微服务集群
uri: lb://wfx-goods
predicates:
- Path=/goods
@Component
public class CalServiceTimeGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//前处理
long startTime = System.currentTimeMillis();
System.out.println("name:"+config.getName());
System.out.println("value:"+config.getValue());
// return chain.filter(exchange);//放行
return chain.filter(exchange).then(
//后置处理
Mono.fromRunnable(()->{
System.out.println("post come in");
//获取系统当前时间戳为endTime
long entTime = System.currentTimeMillis();
System.out.println("time="+(entTime-startTime));
}));
}
};
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.DEFAULT;
}
}
server:
#网关微服务的启动端口
port: 8040
spring:
application:
name: wfx-gateway #微服务的应用名
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #nacos-server的服务地址
gateway:
#配置路由规则
routes:
- id: wfx-goods
#请求转发到微服务集群
uri: lb://wfx-goods
predicates:
- Path=/goods
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//获取jwt令牌
String jwtToken = exchange.getRequest().getHeaders().getFirst("token");
if (jwtToken == null) {
//不能放行,直接返回,返回json信息
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Map res = new HashMap(){{
put("success",false);
put("msg","必须指定token");
}};
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = null;
try {
jsonStr = objectMapper.writeValueAsString(res);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer dataBuffer = response.bufferFactory().wrap(jsonStr.getBytes());
return response.writeWith(Flux.just(dataBuffer));//响应json数据
}else{
//校验令牌
if( jwtToken.equals("xx")){//校验成功
return chain.filter(exchange); //放行
}else{
//不能放行,直接返回,返回json信息
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Map res = new HashMap(){{
put("success",false);
put("msg","令牌不合法");
}};
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = null;
try {
jsonStr = objectMapper.writeValueAsString(res);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer dataBuffer = response.bufferFactory().wrap(jsonStr.getBytes());
return response.writeWith(Flux.just(dataBuffer));//响应json数据
}
}
}
//指定过滤器的执行顺序,值越小越先执行
@Override
public int getOrder() {
return 0;
}
}
package com.wfx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;
import java.time.ZonedDateTime;
@SpringBootApplication
@EnableDiscoveryClient
public class WfxGateway {
public static void main(String[] args) {
SpringApplication.run(WfxGateway.class,args);
}
@Bean
@Order(1)
public GlobalFilter aFilter(){
return new GlobalFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("A come in ");
return chain.filter(exchange).then( Mono.fromRunnable(()->{
System.out.println("A post come in");
}));
}
};
}
@Bean
@Order(2)
public GlobalFilter bFilter(){
return new GlobalFilter() {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("b come in ");
return chain.filter(exchange).then( Mono.fromRunnable(()->{
System.out.println("b post come in");
}));
}
};
}
@Bean
@Order(-1)
public GlobalFilter a() {
return (exchange, chain) -> {
System.out.println("first pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("third post filter");
}));
};
}
@Bean
@Order(0)
public GlobalFilter b() {
return (exchange, chain) -> {
System.out.println("second pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("second post filter");
}));
};
}
@Bean
@Order(1)
public GlobalFilter c() {
return (exchange, chain) -> {
System.out.println("third pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("first post filter");
}));
};
}
}
五:gateway整合sentinel
Sentinel从 1.6.0 版本开始提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
-
route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId自定义
-
API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
第一步:pom依赖
com.alibaba.cloud spring-cloud-starter-alibaba-sentinelcom.alibaba.csp sentinel-spring-cloud-gateway-adapter
第二步:配置
spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 sentinel: transport: dashboard: 127.0.0.1:8888 port: 87105.2:BlockException异常处理
默认BlockException异常全局处理器 SentinelGatewayBlockExceptionHandler
package com.wfx.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
//order不能为-1 必须优先级最高
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
public GlobalFilter sentinelGatewayFilter() {
// By default the order is HIGHEST_PRECEDENCE
return new SentinelGatewayFilter();
}
}
自定义BlockException异常处理器
package com.wfx.sentinel;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;
import java.util.Collections;
import java.util.List;
@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
// 必须优先级最高
@Order(Ordered.HIGHEST_PRECEDENCE)
public MySentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new MySentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
public GlobalFilter sentinelGatewayFilter() {
// By default the order is HIGHEST_PRECEDENCE
return new SentinelGatewayFilter();
}
}
package com.wfx.sentinel;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.function.Supplier;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.codec.HttpMessageWriter;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MySentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
private List viewResolvers;
private List> messageWriters;
private final Supplier contextSupplier = () -> {
return new ServerResponse.Context() {
public List> messageWriters() {
return MySentinelGatewayBlockExceptionHandler.this.messageWriters;
}
public List viewResolvers() {
return MySentinelGatewayBlockExceptionHandler.this.viewResolvers;
}
};
};
public MySentinelGatewayBlockExceptionHandler(List viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.messageWriters = serverCodecConfigurer.getWriters();
}
//自定义异常处理
private Mono writeResponse(ServerResponse response1, ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
//不能放行,直接返回,返回json信息
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
Map res = new HashMap(){{
put("success",false);
put("msg","系统繁忙,请稍后重试!!");
}};
ObjectMapper objectMapper = new ObjectMapper();
String jsonStr = null;
try {
jsonStr = objectMapper.writeValueAsString(res);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer dataBuffer = response.bufferFactory().wrap(jsonStr.getBytes());
return response.writeWith(Flux.just(dataBuffer));//响应json数据
}
public Mono handle(ServerWebExchange exchange, Throwable ex) {
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
} else {
return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
return this.writeResponse(response, exchange);
});
}
}
private Mono handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}
六:gateway跨域
由于gateway使用的是webflux,而不是springmvc,所以需要先关闭springmvc的cors,再从gateway的filter里边设置cors就行了。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}



