SpringCloudGateway:
底层使用的是webFlux技术(java),内部使用的服务器为Netty.
WebFlux是一个响应式的技术.
稍后我们在学习网关的过滤器时,使用的都是webFlux的过滤器,与我们之前学的稍有不同.
Netty服务器默认端口为8080
我们之前学过的所有组件,底层均依赖SpringMVC-Servlet,依赖Tomcat服务器.
Tomcat: 200
day04-cloud-demo
com.bw
1.0-SNAPSHOT
4.0.0
gateway-service
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.boot
spring-boot-maven-plugin
server:
port: 10010
spring:
application:
name: gateway-service # 服务名称
cloud:
gateway: # 网关配置
routes: # 路由(分发请求)
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
uri: http://127.0.0.1:8081 # 路由的目标微服务地址
predicates: # 断言,匹配规则
- Path=/user/** # 按照路径匹配的规则
使用Eureka做注册中心
day04-cloud-demo
com.bw
1.0-SNAPSHOT
4.0.0
gateway-service
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-maven-plugin
spring:
cloud:
gateway:
routes:
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
# uri: http://127.0.0.1:8081 # 路由的目标微服务地址
uri: lb://user-service # 路由的目标 微服务名称
predicates: # 断言,匹配规则
- Path=/user/** # 按照路径匹配的规则
application:
name: gateway-service
server:
port: 10010
# eureka服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
Spring Cloud Gateway
路由/局部过滤器: 只对某一个路由规则生效 默认过滤器: 对所有的路由规则都生效 全局/自定义过滤器: 作用与默认过滤器一致,对所有的路由规则都生效 需要手动写代码实现过滤器(webflux技术)
# 注意格式
spring:
cloud:
gateway:
routes:
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
# uri: http://127.0.0.1:8081 # 路由的目标微服务地址
uri: lb://user-service # 路由的目标 微服务名称
predicates: # 断言,匹配规则
- Path=/user-service/** # 按照路径匹配的规则
filters:
- AddRequestHeader=info, java is best!
application:
name: gateway-service
server:
port: 10010
# eureka服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
测试方式: 制造问题,将下游服务宕机(停止服务),然后通过网关访问下游服务,就会出现降级效果
org.springframework.cloud spring-cloud-starter-netflix-hystrix
spring:
cloud:
gateway:
default-filters: # 默认过滤项
- name: Hystrix # 指定过滤工厂名称
args: # 指定过滤的参数
name: fallbackcmd # hystrix的指令名
fallbackUri: forward:/fallbackTest # 失败后的跳转路径
routes:
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
# uri: http://127.0.0.1:8081 # 路由的目标微服务地址
uri: lb://user-service # 路由的目标 微服务名称
predicates: # 断言,匹配规则
- Path=/user-service/** # 按照路径匹配的规则
filters:
- AddRequestHeader=info, java is best!
application:
name: gateway-service
server:
port: 10010
# eureka服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FallbackController {
@RequestMapping("/fallbackTest")
public String fallbackTest(){
return "网关服务降级: 服务器繁忙,稍后重试...";
}
}
server:
port: 10010
spring:
application:
name: gateway-service # 服务名称
cloud:
gateway: # 网关配置
default-filters: # 默认过滤 全局过滤,对所有的路由规则都生效
- StripPrefix=1 # 截去路由的前缀(1个前缀)
- name: Hystrix # 指定过滤工厂名称(不能乱写)
args: # 指定过滤的参数
name: fallbackcmd # hystrix的指令名(固定值)
fallbackUri: forward:/fallbackTest # 失败后的跳转路径
routes: # 路由(分发请求)
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
# uri: http://127.0.0.1:8081 # 路由的目标微服务地址
uri: lb://user-service # 路由的目标微服务名称
predicates: # 断言,匹配规则
- Path=/user-service/** # 按照路径匹配的规则
filters: # 添加局部过滤器
- AddRequestHeader=info,java is best
# eureka服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Order(0) // 通过注解声明过滤器顺序
@Component // 创建类对象存放到IOC容器中
public class LoginFilter implements GlobalFilter {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求携带的username值
String username = exchange.getRequest().getQueryParams().toSingleValueMap().get("username");
// 判断请求参数是否正确
if(StringUtils.equals(username, "admin")){
// 正确,放行
return chain.filter(exchange);
}
// 错误,需要拦截,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 结束任务
return exchange.getResponse().setComplete();
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;
@Slf4j
@Configuration
public class FilterConfiguration {
@Bean
@Order(-2)
public GlobalFilter globalFilter1(){
return ((exchange, chain) -> {
log.info("过滤器1的pre阶段!");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("过滤器1的post阶段!");
}));
});
}
@Bean
@Order(-1)
public GlobalFilter globalFilter2(){
return ((exchange, chain) -> {
log.info("过滤器2的pre阶段!");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("过滤器2的post阶段!");
}));
});
}
@Bean
@Order(0)
public GlobalFilter globalFilter3(){
return ((exchange, chain) -> {
log.info("过滤器3的pre阶段!");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("过滤器3的post阶段!");
}));
});
}
}
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
排序的规则是什么呢?
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
限流: 限制单位时间内请求的数量
org.springframework.boot
spring-boot-starter-data-redis-reactive
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.InetSocketAddress;
/**
* 限流的原理:
* 当并发请求来时,解析当前请求中携带的用户信息(ip,token),将用户的唯一标记作为redis的key
* 并给key设置超时时间和有效次数,每访问一次次数减1
*
* 每个ip5秒内访问5次
* 第一次过来: 127.0.0.1 4
* 第二次过来: 127.0.0.1 3
* 第三次过来: 127.0.0.1 2
* 第四次过来: 127.0.0.1 1
* 第五次过来: 127.0.0.1 0
* 第六次过来: 127.0.0.1 0 忽略当前请求
*/
@Component
public class IpKeyResolver implements KeyResolver {
@Override
public Mono resolve(ServerWebExchange exchange) {
// 获取请求信息对象
ServerHttpRequest request = exchange.getRequest();
// 获取请求者的地址信息
InetSocketAddress remoteAddress = request.getRemoteAddress();
// 获取请求者的主机名称 ip
String hostName = remoteAddress.getHostName();
System.out.println("请求者的地址信息: "+remoteAddress);
System.out.println("请求者的主机名称: "+hostName);
return Mono.just(hostName);
}
}
server:
port: 10010
spring:
application:
name: gateway-service # 服务名称
cloud:
gateway: # 网关配置
default-filters: # 默认过滤 全局过滤,对所有的路由规则都生效
- name: RequestRateLimiter #请求数限流 名字不能随便写
args:
key-resolver: "#{@ipKeyResolver}" # 指定一个key生成器
redis-rate-limiter.replenishRate: 2 # 生成令牌的速率 每秒生成2个令牌
redis-rate-limiter.burstCapacity: 2 # 桶的容量
- StripPrefix=1 # 截去路由的前缀(1个前缀)
- name: Hystrix # 指定过滤工厂名称(不能乱写)
args: # 指定过滤的参数
name: fallbackcmd # hystrix的指令名(固定值)
fallbackUri: forward:/fallbackTest # 失败后的跳转路径
routes: # 路由(分发请求)
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
# uri: http://127.0.0.1:8081 # 路由的目标微服务地址
order: 0 # 设置过滤器的执行顺序 值越小优先级越高
uri: lb://user-service # 路由的目标微服务名称
predicates: # 断言,匹配规则
- Path=/user-service/** # 按照路径匹配的规则
filters: # 添加局部过滤器
- AddRequestHeader=info,java is best
# eureka服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
同源策略: 浏览器默认遵循同源策略
一个网页上展示的所有数据都应当来自同一台服务器跨域: 协议、ip、端口三者有任何一者不同就是跨域
违背同源策略就是跨域
server:
# 配置netty服务器的端口号,netty服务器默认端口与tomcat端口一致 8080
port: 10010
spring:
application:
name: gateway-service # 服务名称
cloud:
gateway: # 网关配置
default-filters: # 默认过滤器: 对所有的路由规则都生效
- StripPrefix=1 # 截去路由的前缀(1个前缀) localhost:10010/userservice/user/1 ---> 微服务ip地址/user/1
routes: # 路由(分发请求) 配置分发的规则(路由断言)
- id: devic-service-name # 当前路由的唯一标识(自定义,唯一即可)
uri: lb://devic-service # 路由的目标微服务地址
predicates: # 断言,匹配规则
- Path=/deviceservice/** # 按照路径匹配的规则
- id: type-service-name # 当前路由的唯一标识(自定义,唯一即可)
uri: lb://type-service # 路由的目标微服务地址
predicates: # 断言,匹配规则
- Path=/typeservice/** # 按照路径匹配的规则
- id: user-service-name # 当前路由的唯一标识(自定义,唯一即可)
uri: lb://user-service # 路由的目标微服务地址
predicates: # 断言,匹配规则
- Path=/userservice/** # 按照路径匹配的规则
# 配置网关允许跨域
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
allowCredentials: true
maxAge: 360000