gateway集成ribbon的原理是在全局LoadBalancerClientFilter中进行拦截,然后该过滤器当中依赖LoadBalancerClient loadBalancer,然而负载均衡接口的具体实现是RibbonLoadBalancerClient implements LoadBalancerClient,所以gateway已经整合了ribbon,实现了负载均衡,不需要任何处理网关对微服务的请求转发已经具有负载均衡。
网关集成Sentinel是为了流控熔断降级
1、添加依赖
<!-- sentinel-spring-cloud-gateway-adapter -->
com.alibaba.csp</groupId>
sentinel-spring-cloud-gateway-adapter</artifactId>
1.7.2</version>
</dependency>
<!--sentinel-datasource-extension数据源扩展-->
com.alibaba.csp</groupId>
sentinel-datasource-extension</artifactId>
</dependency>
<!--sentinel数据持久化-->
com.alibaba.csp</groupId>
sentinel-datasource-nacos</artifactId>
</dependency>
2、添加Sentinel控制台配置
#sentinel dashboard管理后台
sentinel:
eager: true
transport:
dashboard: 192.168.25.1:8080
#配置sentinel规则持久化到nacos
datasource:
flow: #流控
nacos:
server-addr: 47.110.237.194:80
data-id: ${spring.application.name}-flow.json
group-id: DEFAULT_GROUP
rule-type: flow
data-type: json
degrade:
nacos: #降级
server-addr: 47.110.237.194:80
data-id: ${spring.application.name}-degrade.json
group-id: DEFAULT_GROUP
rule-type: degrade
data-type: json
3、在Spring容器当中配置一个Sentinel的全局过滤器
/**
* 配置一个Sentinel的全局过滤器;
* @return
*/
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter(){
return new SentinelGatewayFilter();
}
第一种:持久化到本地文件
第二种:在application.yaml配置持久化规则
#配置sentinel规则持久化到nacos
datasource:
flow: #流控
nacos:
server-addr: 47.110.237.194:80
data-id: ${spring.application.name}-flow.json
group-id: DEFAULT_GROUP
rule-type: flow
data-type: json
degrade:
nacos: #降级
server-addr: 47.110.237.194:80
data-id: ${spring.application.name}-degrade.json
group-id: DEFAULT_GROUP
rule-type: degrade
data-type: json
在nacos配置中心配置流控规则:
[
{
"resource": "route1",
"controlBehavior": 0,
"count": 1.0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
,
{
"resource": "route2",
"controlBehavior": 0,
"count": 2.0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
,
{
"resource": "route3",
"controlBehavior": 0,
"count": 3.0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
(1)根据自动装配spring-cloud-gateway-core.jar的spring.factories;
(2)GatewayClassPathWarningAutoConfiguration检查前端控制器;
(3)网关自动配置GatewayAutoConfiguration;
(4)RoutePredicateHandlerMapping.getHandlerInternal(…)获取Route;
(5)执行FilteringWebHandler
Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,可以自定义路由谓词工厂来实现特定的需求;
举例如下:
1、要求请求必须携带一个token,并且token值等于指定的值,才能访问;
(1)首先定义一个配置类,用于承载配置参数;
cloud:
#配置网关
gateway:
discovery:
locator:
enabled: true #启用DiscoveryClient网关集成,可以实现服务的发现
#配置网关路由转发规则
routes:
- id: route1
uri: lb://29-nacos-discovery-customer
predicates: #谓词:判断,是不是?对不对?是否匹配?
- Path=/test2, /notFound-feign, /index, /echo
- Token=123456
(2)定义一个路由谓词工厂;
注:TokenRoutePredicateFactory类,前面的Token与.yml配置文件里面配置的名字对应,后面的RoutePredicateFactory名字是固定的,不能随便写,这是Spring Cloud Gateway的约定,类名须为“谓词工厂名(比如:Token)” + RoutePredicateFactory
@Slf4j
@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenConfig> {
public TokenRoutePredicateFactory() {
super(TokenConfig.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("token");
}
@Override
public Predicate<ServerWebExchange> apply(TokenConfig tokenConfig) {
// (T t) -> true
return exchange -> {
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
boolean flag = false;
List<String> list = new ArrayList<>();
valueMap.forEach((k, v) -> {
list.addAll(v);
});
for (String s : list) {
log.info("Token -> {} ", s);
if (StringUtils.equalsIgnoreCase(s, tokenConfig.getToken())) {
flag = true;
break;
}
}
return flag;
};
}
}
结果如下所示
处理的顶层接口是WebExceptionHandler
默认实现是DefaultErrorWebExceptionHandler
/**
* @author
* @Description: 自定义错误页面
* @date 2021/1/12 23:44
*/
public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
/**
* Create a new {@code DefaultErrorWebExceptionHandler} instance.
*
* @param errorAttributes the error attributes
* @param resourceProperties the resources configuration properties
* @param errorProperties the error configuration properties
* @param applicationContext the current application context
*/
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, errorProperties, applicationContext);
}
/**
* 指定响应处理方法为JSON处理的方法
*
* @param errorAttributes
*/
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
/**
* 根据code获取对应的HttpStatus
* @param errorAttributes
* @return
*/
@Override
protected int getHttpStatus(Map<String, Object> errorAttributes){
int status = (int)errorAttributes.get("status");
return status;
}
/**
* 构建异常信息
* @param request
* @param ex
* @return
*/
private String buildMessage(ServerRequest request, Throwable ex) {
StringBuilder message = new StringBuilder("Failed to handle request [");
message.append(request.methodName());
message.append(" ");
message.append(request.uri());
message.append("]");
if (ex != null) {
message.append(": ");
message.append(ex.getMessage());
}
return message.toString();
}
/**
* 构建返回的JSON数据格式
*
* @param status 状态码
* @param errorMessage 异常信息
* @return
*/
public static Map<String, Object> response(int status, String errorMessage) {
Map<String, Object> map = new HashMap<>();
map.put("status",status);
map.put("errorMessage",errorMessage);
map.put("data",null);
return map;
}
}
过滤器工厂是执行在指定路由之上,可以称为路由过滤器(或者局部过滤器),而全局过滤器是作用于所有的路由上,对所有的路由进行过滤;
全局过滤器的顶层接口是GlobalFilter ,和GatewayFilter 有一样的接口定义,只不过GlobalFilter 会作用于所有路由;
全局过滤器有执行顺序问题,通过getOrder()方法的返回值决定执行顺序,数值越小越靠前执行;
/**
* @author
* @Description: 自定义全局Filter
* @date 2021/1/13 0:16
*/
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("自定义全局Filter......");
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
valueMap.forEach((k,v) -> {
log.info("全局filter参数: ,{}",k);
v.forEach( s -> {
log.info("全局filter参数值:,{}",s);
});
});
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
网关过滤器的顶层接口GatewayFilterFactory,通常继承AbstractGatewayFilterFactory实现自定义网关过滤器;或者继承AbstractNameValueGatewayFilterFactory,该方式配置方式更简单,然后覆盖里面的一个方法
/**
* @author
* @Description: 自定义过滤器
* @date 2021/1/12 23:56
*/
@Slf4j
@Component
public class RequestLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (ServerWebExchange exchange, GatewayFilterChain chain) -> {
log.info("请求网关:,{},{}",config.getName(),config.getValue());
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
valueMap.forEach((k,v) -> {
log.info("自定义过滤器请求参数:{}",k);
v.forEach(s -> {
log.info("自定义过滤器请求参数值 {}",s);
});
});
return chain.filter(exchange);
};
}
}
cloud:
#配置网关
gateway:
discovery:
locator:
enabled: true #启用DiscoveryClient网关集成,可以实现服务的发现
#配置网关路由转发规则
routes:
- id: route1
uri: lb://29-nacos-discovery-customer
predicates: #谓词:判断,是不是?对不对?是否匹配?
- Path=/test2, /notFound-feign, /index
- Token=123456
filters:
- AddRequestParameter=color, black
- RequestLog=prefix, gatewaytest #自定义的filter
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),它允许浏览器向跨域的另一台服务器发出XMLHttpRequest请求,从而克服了AJAX只能访问同域名下的资源的限制。
如果 访问协议、端口(如果指定了端口的话)、host都相同,则称之为同源(不跨域),否则为非同源(跨域);
源链接:http://www.mydown.com/soft/453/473302453.shtml
URL | 是否同源 | 原因 |
---|---|---|
http://www.mydown.com/soft/227/1506342727.shtml | 是 | |
https://www.mydown.com/soft/453/473302453.shtml | 否 | 协议不同 |
http://www.mydown.com:81/soft/227/1506342727.shtml | 否 | 端口不同 |
http://www.bear20.com/window/4531/473302453.html | 否 | host不同 |
Spring Cloud Gateway解决跨域问题,只需要配置如下代码即可:
/**
* @author
* @Description: 解决跨域问题
* @date 2021/1/13 23:46
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedMethod("*"); //是什么请求方法,比如 GET POST PUT DELETE .....
corsConfiguration.addAllowedOrigin("*"); //来自哪个域名的请求,*号表示所有
corsConfiguration.addAllowedHeader("*"); //是什么请求头
org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}