编写:HorinJsor
文章目录
前言
后端的服务调用通过Eurka或者Feigen实现,那么前端调用服务该如何实现呢?这就涉及到了网关。
一、网关的主要功能
1、身份认证和权限校验
2、服务路由、负载均衡
3、请求限流
在SpringCloud中网关的实现包括两种:Gateway、Zuul。
本文只介绍Gateway。
二、Gateway搭建步骤
原理
1.引入依赖:创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
注意:nacos的作用相当于将Gateway注册成一个服务,目的是注册到注册中心,使用路由负载均衡。不要引入Web依赖,因为他不是tomcat服务器,是net。
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.配置动态路由 方式一:在配置文件yaml中编写路由配置及nacos地址
- routes :路由规则配置,可以是列表。
- predicates :断言判断,写接口路径。
server:
port: 80 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址,目的是注册到注册中心,使用路由负载均衡。
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址,静态,固定地址不建议写定。
uri: lb://userservice # 路由的目标地址(动态):lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条(前端访问路径)
# - Path=/user/login 静态,不建议写定
- Path=/user/** #动态
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
3.配置动态路由 方式二:基于代码的方式路由
创建配置类
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes
//path_rote_guonei为路由id,usri为路由目标地址,path为断言,符合规则的条。
.route("path_rote_guonei", r -> r.path("/guonei").uri("http://news.baidu.com/guonei"))
.route("path_rote_guoji", r -> r.path("/guoji").uri("http://news.baidu.com/guoji"))
.route("path_rote_tech", r -> r.path("/tech").uri("http://news.baidu.com/tech"))
.route("path_rote_lady", r -> r.path("/lady").uri("http://news.baidu.com/lady"))
.build();
return routes.build();
}
4.动态路由测试:访问
在地址栏输入:
http://localhost:80/user-service/user/login
IP:端口/服务名称/接口路径
如果使用的是静态路由:
http://localhost:80/user/login
IP:端口/接口路径
如果没有引入注册中心,不用写服务名称。
三、Gateway网关:路由断言(判断)工厂
- 根据规则断言
1.工厂产品示例
1.断言用于判断请求是否符合路由规则的条。
2.是给每一个路由配置的。
3.在动态路由种不会生效。
具体例子可查看官方文档: SpringCloud网关路由工厂
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates: #断言写在此处
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
四、Gateway网关:路由过滤器
检验前端带来的东西:
- Token校验
- 参数校验
- 黑名单校验
1.工厂过滤器
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
filters: # 这里写局部过滤器
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
default-filters: # 默认过滤器,会对所有的路由请求都生效
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
2.全局过滤器GlobalFilter
自定义类,实现GlobalFilter接口,添加@Order注解:
- @Order注解:过滤器优先级
@Order(-1) //过滤器可以不止一个,进行排序。
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2.获取authorization参数
String auth = params.getFirst("authorization");
// 3.校验
if ("admin".equals(auth)) {
// 放行
return chain.filter(exchange);
}
// 4.拦截
// 4.1 拦截方式一
//说明是黑名单里面的 ip
//ServerHttpResponse response = exchange.getResponse();
//response.setStatusCode(HttpStatus.UNAUTHORIZED);
//Map<String, Object> map = new HashMap<>();
//map.put("code", HttpStatus.UNAUTHORIZED);
//map.put("msg", "非法访问");
//response.getHeaders().add("content-Type","application/json;charset=UTF-8");
//ObjectMapper objectMapper = new ObjectMapper();
//byte[] bytes = objectMapper.writeValueAsBytes(map);
//DataBuffer buffer = response.bufferFactory().wrap(bytes);
//return response.writeWith(Mono.just(buffer));
// 4.2 拦截方式二
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.3 结束处理
return exchange.getResponse().setComplete();
}
}
3.过滤器执行顺序
- 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
- order值越小,优先级越高。
五、Gateway 结合 redis 实现请求量限流
原理:发限量令牌,有的可以通过访问。
- 解决IP频繁访问
- 解决接口请求量大
1.添加依赖
<!--限流要引入 Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2.创建配置类 RequestRateLimiterConfig
@Configuration
public class RequestRateLimiterConfig {
/**
* IP 限流
* 把用户的 IP 作为限流的 Key
*
* @return
*/
@Bean
@Primary //主候选Bean
public KeyResolver hostAddrKeyResolver() {
return (exchange) -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
/**
* 用户 id 限流
* 把用户 ID 作为限流的 key
*
* @return
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/**
* 请求接口限流
* 把请求的路径作为限流 key
*
* @return
*/
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
3.修改配置文件
server:
port: 80
spring:
application:
name: gateway-80
cloud:
gateway:
enabled: true
routes:
- id: user-service
uri: lb://consumer-user-service
predicates:
- Path=/info/**
filters:
- name: RequestRateLimiter
args: #主要是配置这里
key-resolver: '#{@hostAddrKeyResolver}' #配置类种的方法名称
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 3
redis: #redis 的配置
host: 192.168.226.128
port: 6379
database: 0
eureka:
instance:
instance-id: ${spring.application.name}:${server.port}
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8761/eureka/
六、跨域问题
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现:
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
总结
冲